拖拽

220 阅读1分钟

封装

思路:

  • 如果拖拽时box相对于自己的位移为 0, 那么鼠标移动多少box就移动多少
  • 如果拖拽时box相对于自己的位移为 3, 那么translateX = 3 + 鼠标位移
const useDrager = () => () => useDrag()

export function useDrag() {
  const target = useRef<HTMLDivElement>(null);
  const [style, setStyle] = useState<CSSProperties>({})

  useEffect(() => {
    let dragging = false;
    // 鼠标相对于视口的初始位置
    let start = { x:0, y: 0}; 
    // transform: 
    // box相对于自己的位置
    // 如果拖拽时box相对于自己的位移为 0, 那么鼠标移动多少box就移动多少
    // 如果拖拽时box相对于自己的位移为 3, translateX = 3 + 鼠标位移
    let transform = { x:0, y: 0}

    target.current?.addEventListener("mousedown", function (e: MouseEvent) {
      dragging = true;
      start = getMouse(e) // 记录鼠标初始位置
      transform = getTransform(target.current) // 获取dom相对于自己的位置
    });

    document.addEventListener("mousemove", function (e) {
      if (dragging===false) return;
      const sx = e.clientX - start.x // 鼠标位移x
      const sy = e.clientY - start.y // 鼠标位移y
      const x = transform.x + sx
      const y = transform.y + sy
      setStyle({ transform: `translate(${x}px, ${y}px)`});
    });

    document.addEventListener("mouseup", function (e) {
      dragging = false;
    });

  }, [target]);

  return { 
    ref: target,
    style
   }
}

// 获取鼠标在视口中的位置
function getMouse(e: MouseEvent) {
  return {
    x: e.clientX,
    y: e.clientY
  }
}

// 获取dom元素的translateX, translateY, 文心一言搜的方法
function getTransform(dom) {
  const style = window.getComputedStyle(dom);
  const transformMatrix = style.transform
  const matrix = transformMatrix.match(/^matrix\((.+)\)$/);

  if (matrix) {
    const values = matrix[1].split(',').map(parseFloat);
    // const translateX = values[12] || 0;
    return {
      y: values.pop(),
      x: values.pop(),
    }
  }
  return {x: 0, y: 0}
}

使用

const Test = function () {
  const props = useDrag()
  const bind = useDrager()

  return <>
    <div {...props} className='box' />

    <div {...bind()} className='box' />
    <div {...bind()} className='box' />
    <div {...bind()} className='box' />
    <div {...bind()} className='box' />
    <div {...bind()} className='box' />
  </>

};