先看效果
- 左上的点是禁止元素出界的功能
- 在有父容器的情况下依旧能丝滑拖拽
- 在元素的宽高缩小到64PX以下,会隐藏部分手柄
- 任何情况下,都能一拉到底
- 实现整个逻辑的代码仅有300+行,一个组件而已,体积轻,已修改
- 后面有技术分析
竞品对比
visual-drag-demo;
- visual-drag-demo 是一个开源项目,目前5.3kStar
- 它在高速拉伸的时候,明显有大的空间
- 当然,它有个旋转,复杂度高了不少
- 但它的旋转算法不太对,left,top并不是真实的位置,引入一个坐标系就能发现问题了
创客贴
- 这是一个商业级的项目,同样以div为载体的画板
- 也能看出,在高速拖拽的时候照样无法一拉到底
分析原因
- 分析两种拖拽的实现思路,当前拖拽只适合没有碰撞检测类型的
- 这种方式是不断减去上一个鼠标的位置
- 缺点是,没有根据鼠标按下时的坐标信息去计算,而是一直 += 更新的偏移值
- 一旦使用clamp截取了部分信息,div就回不到原来的点了,很多拖拽都有这样的问题
const position = {
x: 0,
y: 0,
}
const targetDiv = {
x: 0,
y: 0,
}
document.addEventListener('mousedown', (me) => {
position.x = me.x
position.y = me.y
document.addEventListener('mousemove', (de) => {
const offsetX = de.x - position.x
const offsetY = de.y - position.y
targetDiv.x += offsetX
targetDiv.y += offsetY
position.x = de.x
position.y = de.x
})
//释放 mousemove
})
- 适合有边界的拖拽算法
- 这种方式代码上略麻烦些,但更好懂
- div的坐标=初始坐标 + 鼠标偏移量
//临时变量,用于记录[最后一次|首次拖拽]的点
const position = {
x: 0,
y: 0,
}
const targetDiv = {
x: 0,
y: 0,
}
document.addEventListener('mousedown', (me) => {
targetDiv.x = position.x
targetDiv.y = position.y
document.addEventListener('mousemove', (de) => {
const offsetX = de.x - me.x
const offsetY = de.y - me.y
targetDiv.x = position.x + offsetX
targetDiv.y = position.y + offsetY
})
document.addEventListener('mouseup', () => {
position.x = targetDiv.x
position.y = targetDiv.y
//释放 mousemove
})
})