探索基于 Vue.js 的悬浮球组件
在当今的现代网页应用世界里,悬浮球(Floating Ball)已然成为一种备受青睐的用户界面元素。它宛如一位贴心小助手,随时静候在旁,能够为用户提供快速访问各类功能或信息的便捷通道。今天,就带大家深入了解一个基于 Vue.js 精心打造的悬浮球组件,一同探究它的结构奥秘、强大功能以及那灵动的可拖动特性。
一、组件结构剖析
我们所打造的这个悬浮球组件,就像是一座精密搭建的小型建筑,由三个关键支柱稳稳支撑:模板(template)、脚本(script)和样式(style)。
模板部分:这是悬浮球面向用户的 “颜面”,它精心勾勒出了悬浮球的外观轮廓,每一处弧度、每一种色彩搭配,都经过了反复斟酌。不仅如此,它还赋予了悬浮球灵动的交互能力,让用户的每一次点击、每一次触碰都能得到及时且恰当的回应。
脚本部分:作为组件的 “智慧大脑”,运用 Vue 3 先进的组合式 API,有条不紊地处理着组件内部复杂的逻辑关系。它就像是一位幕后指挥官,精准调度着各种指令,确保组件的运行顺畅无阻。
样式部分:若把悬浮球比作一位时尚达人,那样式部分无疑就是它的专属造型师。借助 SCSS 这把神奇的 “剪刀” 与 “画笔”,为悬浮球裁剪出合身且时尚的 “外衣”,定义出独特的风格样式,使其在众多元素中脱颖而出。
二、代码实现解密
接下来,让我们揭开 FloatingBall 组件核心代码的神秘面纱。
<template>
<div :style="{ right: `${right}px`, bottom: `${bottom}px` }" class="floating-ball" @click="handleClick" @mousedown="startDrag">
助手
</div>
<div v-if="showContentInfo" class="content-info" :style="{ right: `${right}px`, bottom: `${bottom + 80}px` }">
<el-tabs class="custom-tabs" type="border-card">
<el-tab-pane label="智能提醒">智能提醒</el-tab-pane>
<el-tab-pane label="文件记录查询">文件记录查询</el-tab-pane>
<el-tab-pane label="事件发起">事件发起</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import { ref, defineProps } from "vue";
import { useDraggable } from "./hooks/useDraggable";
const props = defineProps({
onClick: Function
});
const { right, bottom, startDrag } = useDraggable();
const showContentInfo = ref(false);
const handleClick = () => {
// 处理点击逻辑
showContentInfo.value =!showContentInfo.value;
};
</script>
<style scoped lang="scss">
.floating-ball {
display: flex;
justify-content: center;
align-items: center;
width: 58px;
height: 58px;
color: #ffffff; /* 文字颜色 */
background: linear-gradient(1deg, #809cf5 1%, #809cf5 1%, #3f63d8 96%);
border-radius: 50%;
border: 2px solid #3f63d9;
position: absolute;
cursor: grab;
transition: background-color 0.3s ease, box-shadow 0.3s ease;
z-index: 100;
user-select: none;
font-size: 16px;
font-weight: 500;
}
.floating-ball:active {
cursor: grabbing;
}
.floating-ball:hover {
background-color: #4c6fde; /* 背景 */
box-shadow: 6px 6px 14px 0px rgba(68, 119, 179, 0.48); /* 悬停时的阴影 */
}
.content-info {
position: absolute;
background-color: #ffffff;
padding: 10px;
z-index: 99;
}
.content-info::before {
content: "";
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
border-radius: 8px;
border: 1px solid rgba(40, 140, 115, 0.3);
}
.content-info::after {
content: "";
position: absolute;
bottom: -8px;
right: 20px;
width: 18px;
height: 18px;
transform: rotate(-45deg);
border: 1px solid rgba(40, 140, 115, 0.3);
background-color: #ffffff;
clip-path: polygon(0 0, 0 100%, 100% 100%);
}
.custom-tabs {
:deep(.el-tabs__nav) {
background-color: #ecf5ff;
overflow: hidden;
.el-tabs__item {
height: 30px;
color: #597494;
padding: 0 20px;
}
.el-tabs__item.is-active:hover {
color: #ffffff;
}
.is-active {
border-bottom-color: none;
background-color: #4477b3;
color: #ffffff;
}
}
}
</style>
从这段代码中,我们能清晰看到模板里悬浮球主体以及点击展开后的信息模块布局,脚本里对组件交互逻辑的严谨设定,还有样式中对悬浮球及其附属信息展示区域细致入微的美化雕琢。
三、神奇的拖动功能
为了赋予悬浮球那灵动的可拖动 “魔法”,我们匠心独运地创建了一个 useDraggable 的组合式函数。这个函数宛如一位隐形的 “操控大师”,默默管理着悬浮球的位置坐标以及拖动过程中的各种状态变化。
import { ref } from "vue";
export function useDraggable() {
const ballSize = 50;
const right = ref(20); // 初始距右边距离
const bottom = ref(20); // 初始距底部距离
const isDragging = ref(false);
let startX, startY, offsetX: number, offsetY: number, moved;
let animationFrameId: number | null = null;
const startDrag = (event: { clientX: any; clientY: any }) => {
isDragging.value = true;
startX = event.clientX;
startY = event.clientY;
offsetX = window.innerWidth - right.value - 25 - startX;
offsetY = window.innerHeight - bottom.value - 30 - startY;
moved = false;
window.addEventListener("mousemove", onMouseMove);
window.addEventListener("mouseup", stopDrag);
};
const onMouseMove = (event: { clientX: any; clientY: any }) => {
if (!isDragging.value) return;
if (animationFrameId) cancelAnimationFrame(animationFrameId);
animationFrameId = requestAnimationFrame(() => {
updatePosition(event.clientX, event.clientY);
});
};
const updatePosition = (x: number, y: number) => {
moved = true;
const halfBallSize = ballSize / 2;
right.value = Math.max(
-halfBallSize,
Math.min(window.innerWidth - x - offsetX - halfBallSize, window.innerWidth - halfBallSize)
);
bottom.value = Math.max(
-halfBallSize,
Math.min(window.innerHeight - y - offsetY - halfBallSize, window.innerHeight - halfBallSize)
);
};
const stopDrag = () => {
isDragging.value = false;
window.removeEventListener("mousemove", onMouseMove);
window.removeEventListener("mouseup", stopDrag);
};
return { right, bottom, isDragging, startDrag };
}
通过巧妙地捕捉鼠标的移动、按下与松开事件,精准计算坐标差值,实时更新悬浮球的位置,让它能顺滑地跟随用户的鼠标轨迹移动,仿佛与用户的手指心有灵犀。
四、总结展望
至此,我们通过一步步的精雕细琢,成功创建出了一个功能完备、可拖动的悬浮球组件。用户得以凭借简单的点击和拖动操作,轻松与它展开交互,获取所需信息或触发特定功能。这一小小的组件,不仅极大地提升了用户体验,让网页操作更加便捷高效,而且还具备广阔的拓展潜力。未来,我们能够依据不同项目的独特需求,为其添加更多新奇有趣的交互选项,或是进一步丰富信息展示的形式与内容。相信通过这篇博客的分享,大家已经对如何在 Vue.js 中实现悬浮球组件有了较为深入的理解,期待大家能将其运用到自己的精彩项目中,创造出更多令人惊艳的交互体验!