八点超丝滑拖拽组件,高速拖拽【无卡顿抖动】带边界,

4,398 阅读2分钟

先看效果

  • 左上的点是禁止元素出界的功能
  • 在有父容器的情况下依旧能丝滑拖拽
  • 在元素的宽高缩小到64PX以下,会隐藏部分手柄
  • 任何情况下,都能一拉到底
  • 实现整个逻辑的代码仅有300+行,一个组件而已,体积轻,已修改
  • 后面有技术分析

拖拽.gif

竞品对比

visual-drag-demo;

  1. visual-drag-demo 是一个开源项目,目前5.3kStar
  2. 它在高速拉伸的时候,明显有大的空间
  3. 当然,它有个旋转,复杂度高了不少
  4. 但它的旋转算法不太对,left,top并不是真实的位置,引入一个坐标系就能发现问题了 vd.gif

创客贴

  1. 这是一个商业级的项目,同样以div为载体的画板
  2. 也能看出,在高速拖拽的时候照样无法一拉到底

ck.gif

分析原因

  • 分析两种拖拽的实现思路,当前拖拽只适合没有碰撞检测类型的
  • 这种方式是不断减去上一个鼠标的位置
  • 缺点是,没有根据鼠标按下时的坐标信息去计算,而是一直 += 更新的偏移值
  • 一旦使用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
  })
})