一、组件核心功能
本组件实现移动端悬浮按钮的完整交互方案,包含以下特性:
- 自由拖拽:支持全屏范围内自由拖动
- 边界约束:自动限制在可视区域内
- 吸边效果:释放时自动吸附到屏幕边缘[
- 事件分离:点击事件与拖拽事件互不干扰
- 性能优化:采用transform实现流畅动画
二、完整组件代码
<template>
<div class="dock-btn"
ref="dockBtn"
@click="handleClick"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd">
<slot name="icon">
<!-- 默认图标 -->
<svg ...></svg>
</slot>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
const emits = defineEmits(['click'])
const dockBtn = ref(null)
let isDragging = false
let startX = 0, startY = 0
let initialX = 0, initialY = 0
// 初始化定位
const initPosition = () => {
const btn = dockBtn.value
btn.style.transform = `translate(${window.innerWidth - 80}px, ${window.innerHeight - 200}px)`
}
// 触摸事件处理
const handleTouchStart = (e) => {
isDragging = true
const touch = e.touches[0]
startX = touch.clientX
startY = touch.clientY
const rect = dockBtn.value.getBoundingClientRect()
initialX = rect.left
initialY = rect.top
}
const handleTouchMove = (e) => {
if (!isDragging) return
e.preventDefault()
const touch = e.touches[0]
const deltaX = touch.clientX - startX
const deltaY = touch.clientY - startY
// 计算新位置
let newX = initialX + deltaX
let newY = initialY + deltaY
// 边界约束
const maxX = window.innerWidth - dockBtn.value.offsetWidth
const maxY = window.innerHeight - dockBtn.value.offsetHeight
newX = Math.max(0, Math.min(maxX, newX))
newY = Math.max(0, Math.min(maxY, newY))
// 使用transform优化性能[7](@ref)
dockBtn.value.style.transform = `translate(${newX}px, ${newY}px)`
}
const handleTouchEnd = () => {
isDragging = false
// 吸边处理[5](@ref)
const rect = dockBtn.value.getBoundingClientRect()
const midPoint = window.innerWidth / 2
const newX = rect.left > midPoint
? window.innerWidth - dockBtn.value.offsetWidth - 20
: 20
dockBtn.value.style.transition = 'transform 0.3s ease'
dockBtn.value.style.transform = `translate(${newX}px, ${rect.top}px)`
setTimeout(() => {
dockBtn.value.style.transition = ''
}, 300)
}
const handleClick = () => {
if (!isDragging) {
emits('click')
}
}
onMounted(initPosition)
onBeforeUnmount(() => {
dockBtn.value = null
})
</script>
<style lang="scss" scoped>
.dock-btn {
position: fixed;
width: 60px;
height: 60px;
border-radius: 30px;
background: linear-gradient(145deg, #ffffff, #e6ebee);
box-shadow: 0 4px 12px rgba(#1678fc, 0.2);
display: flex;
align-items: center;
justify-content: center;
touch-action: none;
transition: transform 0.2s ease;
z-index: 9999;
&.dragging {
opacity: 0.9;
transform: scale(1.1);
box-shadow: 0 6px 16px rgba(#1678fc, 0.3);
}
}
</style>
## 三、进阶功能扩展
### 1. 自动吸边优化
```javascript
const autoDock = (currentX) => {
const threshold = 20
const midPoint = window.innerWidth / 2
return currentX > midPoint
? window.innerWidth - dockBtn.value.offsetWidth - threshold
: threshold
}
2. 拖拽方向限制
// 垂直拖拽模式
if (props.direction === 'vertical') {
newX = initialX // 锁定X轴位置
}
3. 多指触控处理
const handleTouchStart = (e) => {
if (e.touches.length > 1) return // 屏蔽多指操作
// ...原有逻辑
}
四、最佳实践建议
-
性能优化:
- 使用
will-change: transform
启用GPU加速 - 对高频事件使用
requestAnimationFrame
节流
- 使用
-
兼容性处理:
// 桌面端兼容 const handleMouseDown = (e) => { startX = e.clientX startY = e.clientY // ...后续逻辑与touch事件相同 }
-
状态管理:
// 持久化位置数据 const savePosition = () => { localStorage.setItem('btnPosition', JSON.stringify({ x: dockBtn.value.offsetLeft, y: dockBtn.value.offsetTop })) }
五、应用场景示例
场景 | 技术方案 | 实现要点 |
---|---|---|
客服悬浮按钮 | 基础拖拽+点击事件 | 限制右侧吸附 |
游戏虚拟摇杆 | 圆形区域限制+方向计算 | 极坐标转换 |
图片裁切控件 | 拖拽+缩放组合 | 矩阵变换计算 |
看板组件布局 | 多实例拖拽+碰撞检测 | 使用ResizeObserver |
: 实现拖拽的基础事件绑定与DOM操作 : 边界约束计算与可视区域限制 : 移动端触摸事件处理方案 : 吸边效果实现原理 : 性能优化与transform应用 : 触摸事件细节处理