使用Vue3实现可拖拽的九点导航面板
开篇
本文使用Vue3实现了一个可拖拽的九宫导航面板。这个面板在我这里的应用场景是我个人网站的首页的位置,九宫导航对应的是用户最后使用或者最多使用的九个功能,正常应该是由后端接口返回的,不过这里为了简化,写的是固定的数组数据。
效果展示
截图


视频
九点导航面板
功能概述
该导航面板初始状态为三行三列排布的九个圆点的集合,当鼠标放上去之后,九个圆点就会变成九个功能的图标。同时,该面板具有可拖拽功能,可以拖到浏览器上任何一个位置。
代码实现
<template><div class="nine-point-container"v-draggable:style="{ left: position.x + 'px', top: position.y + 'px' }":class="{ 'expanded': isPanelHovered && !isDragging }"><!-- 九点导航面板 --><div class="nine-point-panel"@mouseenter="handlePanelHover(true)"@mouseleave="handlePanelHover(false)":class="{ 'panel-expanded': isPanelHovered && !isDragging }"><!-- 背景遮罩,只在未展开状态显示 --><div class="background-mask" :class="{ 'mask-hidden': isPanelHovered && !isDragging }"></div><div v-for="(item, index) in navigationItems" :key="index"class="nav-item":style="getItemStyle(index)"><!-- 默认状态显示圆点 --><div class="dot" :class="{ 'dot-hidden': isPanelHovered && !isDragging }":style="getDotStyle(index)"></div><!-- 图标和文字 --><div class="hover-content" :class="{ 'content-visible': isPanelHovered && !isDragging }":style="getContentStyle(index)"><el-icon><component :is="item.icon" /></el-icon><span class="item-text">{{ item.text }}</span></div></div></div></div>
</template><script setup>
import { ref, computed } from 'vue'
import { Clock,FullScreen,CirclePlus,Search,Message,Crop,Delete,Tools,PieChart
} from '@element-plus/icons-vue'// 添加拖动状态
const isDragging = ref(false)// 修改拖拽指令
const vDraggable = {mounted(el) {el.style.position = 'fixed'el.style.cursor = 'move'el.onmousedown = (e) => {isDragging.value = trueconst disX = e.clientX - el.offsetLeftconst disY = e.clientY - el.offsetTopdocument.onmousemove = (e) => {let left = e.clientX - disXlet top = e.clientY - disY// 防止拖出视口const maxX = window.innerWidth - el.offsetWidthconst maxY = window.innerHeight - el.offsetHeightleft = Math.min(maxX, Math.max(0, left))top = Math.min(maxY, Math.max(0, top))position.value.x = leftposition.value.y = top}document.onmouseup = () => {document.onmousemove = nulldocument.onmouseup = null// 添加一个小延时,防止拖动结束后立即触发hover效果setTimeout(() => {isDragging.value = false}, 100)}}}
}// 组件位置状态
const position = ref({x: 20,y: 20
})// 面板悬停状态
const isPanelHovered = ref(false)// 处理面板悬停
const handlePanelHover = (isHovered) => {isPanelHovered.value = isHovered
}// 导航项配置 - 移除 isHovered 属性,因为现在统一控制
const navigationItems = ref([{ icon: FullScreen, text: '全屏' },{ icon: CirclePlus, text: '新建' },{ icon: Clock, text: '时钟' },{ icon: Search, text: '搜索' },{ icon: Message, text: '消息' },{ icon: Crop, text: '裁剪' },{ icon: Delete, text: '删除' },{ icon: Tools, text: '工具' },{ icon: PieChart, text: '图表' }
])// 计算每个项目的动画延迟
const getItemStyle = (index) => {// 计算项目在网格中的位置const row = Math.floor(index / 3)const col = index % 3// 计算到中心点的距离(用于径向动画)const centerRow = 1const centerCol = 1const distance = Math.sqrt(Math.pow(row - centerRow, 2) + Math.pow(col - centerCol, 2))// 基础延迟时间(ms)const baseDelay = distance * 50return {'--item-delay': `${baseDelay}ms`,'--item-row': row,'--item-col': col,'--item-distance': distance}
}// 圆点的特定样式
const getDotStyle = (index) => {return {'--dot-delay': `${index * 30}ms`}
}// 内容的特定样式
const getContentStyle = (index) => {return {'--content-delay': `${index * 30}ms`}
}
</script><style lang="scss" scoped>
.nine-point-container {z-index: 1000;user-select: none;transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);transform-origin: center;width: 120px; // 初始较小的尺寸height: 120px;&.expanded {width: 300px; // 展开后的较大尺寸height: 300px;transform: scale(1.1);}
}.nine-point-panel {position: relative;display: grid;grid-template-columns: repeat(3, 1fr);gap: 8px; // 初始较小的间距padding: 12px; // 初始较小的内边距border-radius: 16px;transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);transform-origin: center;// 背景遮罩.background-mask {position: absolute;top: 0;left: 0;right: 0;bottom: 0;background: rgba(30, 30, 30, 0.9);backdrop-filter: blur(10px);border-radius: 16px;transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);z-index: -1;&.mask-hidden {opacity: 0;transform: scale(1.2);}}&.panel-expanded {gap: 24px;padding: 24px;}
}.nav-item {position: relative;width: 24px; // 初始较小的尺寸height: 24px;display: flex;align-items: center;justify-content: center;transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);transform-origin: center;.dot {width: 6px; // 初始较小的圆点height: 6px;background: rgba(255, 255, 255, 0.5);border-radius: 50%;position: absolute;transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);transition-delay: var(--dot-delay);transform-origin: center;box-shadow: 0 0 10px rgba(255, 255, 255, 0.3);&.dot-hidden {transform: scale(0) rotate(180deg);opacity: 0;}}.hover-content {position: absolute;width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: center;color: #fff;opacity: 0;transform: scale(0.6) rotate(-45deg);transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);transition-delay: var(--content-delay);filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.2));&.content-visible {opacity: 1;transform: scale(1) rotate(0deg);width: 60px;height: 60px;.el-icon {color: #409EFF; // 默认使用Element Plus的主题蓝色}.item-text {color: #E6E8EB; // 浅灰色文字}}.el-icon {font-size: 24px;margin-bottom: 8px;transition: all 0.3s ease;// 添加渐变色图标效果background: linear-gradient(120deg, #409EFF, #53C1FF);-webkit-background-clip: text;background-clip: text;-webkit-text-fill-color: transparent;filter: drop-shadow(0 0 8px rgba(64, 158, 255, 0.3));}.item-text {font-size: 12px;opacity: 0;transform: translateY(10px);transition: all 0.3s ease;transition-delay: calc(var(--content-delay) + 100ms);white-space: nowrap;text-shadow: 0 0 10px rgba(230, 232, 235, 0.3);}}&:hover {.hover-content {.el-icon {transform: scale(1.2);filter: drop-shadow(0 0 12px rgba(64, 158, 255, 0.5));}.item-text {opacity: 1;transform: translateY(0);color: #FFFFFF;}}background: transparent; // 移除悬停背景色border-radius: 12px;transform: translateZ(20px);}
}// 修改图标颜色样式
.nav-item {&:nth-child(1) .hover-content .el-icon { color: #FF6B6B; }&:nth-child(2) .hover-content .el-icon { color: #4ECDC4; }&:nth-child(3) .hover-content .el-icon { color: #96E6A1; }&:nth-child(4) .hover-content .el-icon { color: #A18CD1; }&:nth-child(5) .hover-content .el-icon { color: #FF9A9E; }&:nth-child(6) .hover-content .el-icon { color: #84FAB0; }&:nth-child(7) .hover-content .el-icon { color: #FF9A9E; }&:nth-child(8) .hover-content .el-icon { color: #43E97B; }&:nth-child(9) .hover-content .el-icon { color: #FA709A; }.hover-content {.el-icon {font-size: 24px;margin-bottom: 8px;transition: all 0.3s ease;// 移除渐变背景filter: drop-shadow(0 0 8px rgba(255, 255, 255, 0.3));}&.content-visible .el-icon {filter: drop-shadow(0 0 12px rgba(255, 255, 255, 0.5));}}&:hover .hover-content .el-icon {filter: drop-shadow(0 0 15px currentColor);}
}// 优化展开动画
.panel-expanded .nav-item .hover-content {transition: all 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);.el-icon {transition: all 0.3s ease, filter 0.5s ease;}
}// 添加拖动时的样式
.nine-point-container.dragging {cursor: grabbing;.nav-item {pointer-events: none;}
}
</style>
后续可优化点
- 九个功能图标由后端动态返回,可动态展示用户最常用的九个功能或者最近使用的九个功能的快捷入口;
- 九个功能图标的样式和颜色,也可由后端返回,并增加对应的功能图标的样式配置页面;
- 可进一步优化由九点到图标的动画效果;
注
以上便是九点导航面板的全部实现代码,希望能对您有所抛砖引玉的作用~
感谢阅读!
相关文章:
使用Vue3实现可拖拽的九点导航面板
开篇 本文使用Vue3实现了一个可拖拽的九宫导航面板。这个面板在我这里的应用场景是我个人网站的首页的位置,九宫导航对应的是用户最后使用或者最多使用的九个功能,正常应该是由后端接口返回的,不过这里为了简化,写的是固定的数组数…...
68-《贝壳花》
贝壳花 贝壳花(学名:Moluccella laevis Linn.)是属于唇形科,贝壳花是一、二年的草本。植株高5至60cm,茎四棱,不分枝。叶对生,心脏状圆形,边缘疏生齿牙;叶柄和叶近等长。花…...
C++ Lambda 表达式的本质及原理分析
目录 1.引言 2.Lambda 的本质 3.Lambda 的捕获机制的本质 4.捕获方式的实现与底层原理 5.默认捕获的实现原理 6.捕获 this 的机制 7.捕获的限制与注意事项 8.总结 1.引言 C 中的 Lambda 表达式是一种匿名函数,最早在 C11 引入,用于简化函数对象的…...
深入理解三高架构:高可用性、高性能、高扩展性的最佳实践
引言 在现代互联网环境下,随着用户规模和业务需求的快速增长,系统架构的设计变得尤为重要。为了确保系统能够在高负载和复杂场景下稳定运行,"三高架构"(高可用性、高性能、高扩展性)成为技术架构设计中的核…...
【自然语言处理(NLP)】深度循环神经网络(Deep Recurrent Neural Network,DRNN)原理和实现
文章目录 介绍深度循环神经网络(DRNN)原理和实现结构特点工作原理符号含义公式含义 应用领域优势与挑战DRNN 代码实现 个人主页:道友老李 欢迎加入社区:道友老李的学习社区 介绍 **自然语言处理(Natural Language Pr…...
2025数学建模美赛|F题成品论文
国家安全政策与网络安全 摘要 随着互联网技术的迅猛发展,网络犯罪问题已成为全球网络安全中的重要研究课题,且网络犯罪的形式和影响日益复杂和严重。本文针对网络犯罪中的问题,基于多元回归分析和差异中的差异(DiD)思…...
自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测
代码: import torch import numpy as np import torch.nn as nn# 定义数据:x_data 是特征,y_data 是标签(目标值) data [[-0.5, 7.7],[1.8, 98.5],[0.9, 57.8],[0.4, 39.2],[-1.4, -15.7],[-1.4, -37.3],[-1.8, -49.…...
【MQ】如何保证消息队列的高可用?
RocketMQ NameServer集群部署 Broker做了集群部署 主从模式 类型:同步复制、异步复制 主节点返回消息给客户端的时候是否需要同步从节点 Dledger:要求至少消息复制到半数以上的节点之后,才给客户端返回写入成功 slave定时从master同步数据…...
本地大模型编程实战(04)给文本自动打标签
文章目录 准备实例化本地大模型情感分析更精细的控制总结代码 使用本地大模型可以根据需要给文本打标签,本文介绍了如何基于 langchain 和本地部署的大模型给文本打标签。 本文使用 llama3.1 作为本地大模型,它的性能比非开源大模型要查一下,…...
关于使用PHP时WordPress排错——“这意味着您在wp-config.php文件中指定的用户名和密码信息不正确”的解决办法
本来是看到一位好友的自己建站,所以突发奇想,在本地装个WordPress玩玩吧,就尝试着装了一下,因为之前电脑上就有MySQL,所以在自己使用PHP建立MySQL时报错了。 最开始是我的php启动mysql时有问题,也就是启动过…...
【蓝桥杯】43694.正则问题
题目描述 考虑一种简单的正则表达式: 只由 x ( ) | 组成的正则表达式。 小明想求出这个正则表达式能接受的最长字符串的长度。 例如 ((xx|xxx)x|(x|xx))xx 能接受的最长字符串是: xxxxxx,长度是 6。 输入描述 一个由 x()| 组成的正则表达式。…...
服务器虚拟化技术详解与实战:架构、部署与优化
📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 引言 在现代 IT 基础架构中,服务器虚拟化已成为提高资源利用率、降低运维成本、提升系统灵活性的重要手段。通过服务…...
git困扰的问题
.gitignore中添加的某个忽略文件并不生效 把某些目录或文件加入忽略规则,按照上述方法定义后发现并未生效, gitignore只能忽略那些原来没有被追踪的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。 解决方…...
jvm--类的生命周期
学习类的生命周期之前,需要了解一下jvm的几个重要的内存区域: (1)方法区:存放已经加载的类信息、常量、静态变量以及方法代码的内存区域 (2)常量池:常量池是方法区的一部分&#x…...
定制Centos镜像(一)
环境准备: 一台最小化安装的干净的系统,这里使用Centos7.9,一个Centos镜像,镜像也使用Centos7.9的。 [rootlocalhost ~]# cat /etc/system-release CentOS Linux release 7.9.2009 (Core) [rootlocalhost ~]# rpm -qa | wc -l 306 [rootloca…...
C语言------数组思维导图
...
TensorFlow实现逻辑回归模型
逻辑回归是一种经典的分类算法,广泛应用于二分类问题。本文将介绍如何使用TensorFlow框架实现逻辑回归模型,并通过动态绘制决策边界和损失曲线来直观地观察模型的训练过程。 数据准备 首先,我们准备两类数据点,分别表示两个不同…...
《十七》浏览器基础
浏览器:是安装在电脑里面的一个软件,能够将页面内容渲染出来呈现给用户查看,并让用户与网页进行交互。 常见的主流浏览器: 常见的主流浏览器有:Chrome、Safari、Firefox、Opera、Edge 等。 输入 URL,浏览…...
Windows 靶机常见服务、端口及枚举工具与方法全解析:SMB、LDAP、NFS、RDP、WinRM、DNS
在渗透测试中,Windows 靶机通常会运行多种服务,每种服务都有其默认端口和常见的枚举工具及方法。以下是 Windows 靶机常见的服务、端口、枚举工具和方法的详细说明: 1. SMB(Server Message Block) 端口 445/TCP&…...
IME关于输入法横屏全屏显示问题-Android14
IME关于输入法横屏全屏显示问题-Android14 1、输入法全屏模式updateFullscreenMode1.1 全屏模式判断1.2 全屏模式布局设置 2、应用侧关闭输入法全屏模式2.1 调用输入法的应用设置flag2.2 继承InputMethodService.java的输入法应用覆盖onEvaluateFullscreenMode方法 InputMethod…...
网络安全 | F5-Attack Signatures-Set详解
关注:CodingTechWork 创建和分配攻击签名集 可以通过两种方式创建攻击签名集:使用过滤器或手动选择要包含的签名。 基于过滤器的签名集仅基于在签名过滤器中定义的标准。基于过滤器的签名集的优点在于,可以专注于定义用户感兴趣的攻击签名…...
STranslate 中文绿色版即时翻译/ OCR 工具 v1.3.1.120
STranslate 是一款功能强大且用户友好的翻译工具,它支持多种语言的即时翻译,提供丰富的翻译功能和便捷的使用体验。STranslate 特别适合需要频繁进行多语言交流的个人用户、商务人士和翻译工作者。 软件功能 1. 即时翻译: 文本翻译ÿ…...
基于微信小程序的助农扶贫系统设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
我谈区域偏心率
偏心率的数学定义 禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》P312 区域的拟合椭圆看这里。 Rafael Gonzalez的二阶中心矩的表达不说人话。 我认为半长轴和半短轴不等于特征值,而是特征值的根号。…...
关于低代码技术架构的思考
我们经常会看到很多低代码系统的技术架构图,而且经常看不懂。是因为技术架构图没有画好,还是因为技术不够先进,有时候往往都不是。 比如下图: 一个开发者,看到的视角往往都是技术层面,你给用户讲React18、M…...
若依路由配置教程
1. 路由配置文件 2. 配置内容介绍 { path: "/tool/gen-edit", component: Layout, //在路由下,引用组件的名称,在页面中包括这个组件的内容(页面框架内容) hidden: true, //此页面的内容,在左边的菜单中不用显示。 …...
基于ESP8266的多功能环境监测与反馈系统开发指南
项目概述 本系统集成了物联网开发板、高精度时钟模块、环境传感器和可视化显示模块,构建了一个智能环境监测与反馈装置。通过ESP8266 NodeMCU作为核心控制器,结合DS3231实时时钟、DHT11温湿度传感器、光敏电阻和OLED显示屏,实现了环境参数的…...
【Elasticsearch】doc_values 可以用于查询操作
确实,doc values 可以用于查询操作,尽管它们的主要用途是支持排序、聚合和脚本中的字段访问。在某些情况下,Elasticsearch 也会利用 doc values 来执行特定类型的查询。以下是关于 doc values 在查询操作中的使用及其影响的详细解释ÿ…...
HTML5 Web Worker 的使用与实践
引言 在现代 Web 开发中,用户体验是至关重要的。如果页面在执行复杂计算或处理大量数据时变得卡顿或无响应,用户很可能会流失。HTML5 引入了 Web Worker,它允许我们在后台运行 JavaScript 代码,从而避免阻塞主线程,保…...
flutter_学习记录_00_环境搭建
1.参考文档 Mac端Flutter的环境配置看这一篇就够了 flutter的中文官方文档 2. 本人环境搭建的背景 本人的电脑的是Mac的,iOS开发,所以iOS开发环境本身是可用的;外加Mac电脑本身就会配置Java的环境。所以,后面剩下的就是&#x…...
