feat(ebiz): 优化FAB组件拖拽功能

- 修复当移动浮窗之后,点击其他地方浮窗会出现点击位置的问题
- 完善拖拽相关数据字段注释
- 新增窗口大小更新方法
- 重构拖拽开始、进行和结束逻辑
- 添加触摸事件支持和边界限制
- 实现点击与拖拽事件的正确分离
This commit is contained in:
hz
2025-12-01 13:43:46 +08:00
parent 3bb1d67290
commit 7fa320601c

View File

@@ -30,18 +30,20 @@ export default {
data() {
return {
isDragging: false,
hasDragged: false, // 新增:标记是否发生了拖拽
startX: 0,
startY: 0,
posX: 0,
posY: 0,
windowWidth: 0,
windowHeight: 0,
fabWidth: 0,
fabHeight: 0,
initialX: 0,
initialY: 0
isDragging: false, // 是否正在拖动
hasDragged: false, // 是否发生了拖拽
startX: 0, // 触摸点相对于元素左上角的X偏移
startY: 0, // 触摸点相对于元素左上角的Y偏移
posX: 0, // 元素当前位置X
posY: 0, // 元素当前位置Y
initialTouchX: 0, // 初始触摸点X
initialTouchY: 0, // 初始触摸点Y
windowWidth: 0, // 视口宽度
windowHeight: 0, // 视口高度
fabWidth: 0, // FAB宽度
fabHeight: 0, // FAB高度
initialX: 0, // 初始位置X
initialY: 0 // 初始位置Y
}
},
@@ -79,6 +81,9 @@ export default {
},
methods: {
/**
* 更新窗口大小
*/
updateWindowSize() {
this.windowWidth = window.innerWidth
this.windowHeight = window.innerHeight
@@ -90,89 +95,128 @@ export default {
}
},
/**
* 开始拖动
* @param event {TouchEvent}
*/
startDrag(event) {
if (!this.draggable) return
// 阻止默认行为(如页面滚动)和事件冒泡
event.preventDefault()
event.stopPropagation()
const touch = event.touches[0]
// 记录初始触摸位置
this.initialTouchX = touch.clientX
this.initialTouchY = touch.clientY
// 记录元素当前位置和触摸点的偏移
this.startX = touch.clientX - this.posX
this.startY = touch.clientY - this.posY
// 设置拖动状态
this.isDragging = true
this.hasDragged = false
const clientX = event.touches ? event.touches[0].clientX : event.clientX
const clientY = event.touches ? event.touches[0].clientY : event.clientY
this.startX = clientX - this.posX
this.startY = clientY - this.posY
// 禁用文本选择
document.body.style.userSelect = 'none'
const options = { passive: false }
document.addEventListener('mousemove', this.onDrag, options)
document.addEventListener('touchmove', this.onDrag, options)
document.addEventListener('mouseup', this.stopDrag, options)
document.addEventListener('touchend', this.stopDrag, options)
document.body.style.webkitUserSelect = 'none'
},
/**
* 拖动中
* @param event { TouchEvent }
*/
onDrag(event) {
if (!this.isDragging) return
this.hasDragged = true
// 阻止默认行为(如页面滚动)
event.preventDefault()
event.stopPropagation()
const clientX = event.touches ? event.touches[0].clientX : event.clientX
const clientY = event.touches ? event.touches[0].clientY : event.clientY
// 获取当前触摸点
const touch = event.touches[0]
let newX = clientX - this.startX
let newY = clientY - this.startY
// 计算新位置(相对于初始触摸点的偏移)
let newX = touch.clientX - this.startX
let newY = touch.clientY - this.startY
// 应用边界限制
if (this.boundary) {
newX = Math.max(0, Math.min(newX, this.windowWidth - this.fabWidth))
newY = Math.max(0, Math.min(newY, this.windowHeight - this.fabHeight))
const maxX = this.windowWidth - this.fabWidth
const maxY = this.windowHeight - this.fabHeight
// 确保不会移出视口
newX = Math.max(0, Math.min(newX, maxX))
newY = Math.max(0, Math.min(newY, maxY))
}
// 使用transform实现平滑移动
this.posX = newX
this.posY = newY
// 标记为已拖动,防止点击事件触发
if (Math.abs(touch.clientX - this.initialTouchX) > 5 || Math.abs(touch.clientY - this.initialTouchY) > 5) {
this.hasDragged = true
}
},
/**
* 拖动结束
* @param event { TouchEvent }
*/
stopDrag(event) {
if (!this.isDragging) return
this.isDragging = false
document.body.style.userSelect = ''
this.removeEventListeners()
// 如果是拖拽结束,阻止后续的点击事件
if (this.hasDragged) {
// 延迟重置标志位,确保不会触发点击事件
setTimeout(() => {
this.hasDragged = false
}, 100)
// 阻止事件冒泡,避免触发父元素的点击事件
if (event) {
event.stopPropagation()
event.preventDefault()
}
this.removeEventListeners()
this.$emit('drag-end', { x: this.posX, y: this.posY })
},
/**
* 点击事件
* @param event { TouchEvent }
*/
handleClick(event) {
// 如果是拖拽操作,不触发点击事件
if (this.isDragging || this.hasDragged) {
if (this.hasDragged) {
event.stopPropagation()
event.preventDefault()
this.hasDragged = false
return
return false
}
this.$emit('click', event)
return true
},
/**
* 移除事件监听器
*/
removeEventListeners() {
const options = { passive: false }
document.removeEventListener('mousemove', this.onDrag, options)
document.removeEventListener('touchmove', this.onDrag, options)
document.removeEventListener('mouseup', this.stopDrag, options)
document.removeEventListener('touchend', this.stopDrag, options)
// 恢复用户选择
document.body.style.userSelect = ''
document.body.style.webkitUserSelect = ''
// 重置拖动状态
this.isDragging = false
}
}
}
</script>
<template>
<div ref="fab" :style="fabStyle" class="fab-container" @mousedown="startDrag" @touchstart="startDrag">
<div class="fab-content" @click="handleClick" @touchend="handleClick">
<div ref="fab" :style="fabStyle" class="fab-container" @touchend="handleClick" @touchmove="onDrag" @touchstart="startDrag">
<div class="fab-content">
<slot>
<div class="fab-default">
<i v-if="icon" :class="icon"></i>
@@ -188,9 +232,10 @@ $base: #e9332e;
.fab-container {
position: fixed;
cursor: move;
user-select: none;
-webkit-user-select: none;
touch-action: none;
-webkit-tap-highlight-color: transparent;
}
.fab-content {