[前端]仿Chrome实现标签栏(react)-2

上个版本的标签栏实现了标签切换以及右键菜单功能。这几天利用闲余时间加上了拖动功能:

GitHub

主要参考了React 实现简易的图片拖动排序

实现思路

原本想使用onDragStart/onDragOver/onDragEnd事件来实现,但由于标签的样式实现方式影响,拖动出来的样式乱掉了,因此pass掉,还是使用onMouseDown/onMouseMove/onMouseEnd实现:

  • 每一个标签监听onMouseDown事件

    1. 记录当前点击的标签(movingTagPath)
    2. 一些位置(originMousePos/originTagPos)
    3. 设置当前标签为选中状态
  • document监听mousemovemouseup事件

    1. mousemove处理函数中计算移动的距离,(处理边界值后)设置movingTagPos
    2. mouseup处理函数中重置movingTagPos/movingTagPath并移除document监听的事件
  • 标签父元素监听onMouseMove事件

    1. 处理拖动过程中标签位置更新逻辑
//onMouseDown handler
const onTagMouseDown = useCallback((e, path) => {
    e.preventDefault();
    movingTagPath.current = path;
    const x = (parseInt(e.currentTarget.style.left, 10) || 0);
    originMousePos = { x: e.pageX, y: 0 };
    originTagPos = ({ x, y: 0 });
    const activeTag = contentDivRef.current?.querySelector(`.${style.active}`);
    activeTag?.setAttribute('class', style.tagC);
    e.currentTarget?.classList.add(style.active);
    e.currentTarget?.classList.add(style.moving);
    setMovingTagPos({ x: 0, y: 0 });
    document.addEventListener('mousemove', onTagMouseMove);
    document.addEventListener('mouseup', onTagMouseUp);
}, [onTagMouseMove, onTagMouseUp]);

// mousemove及mouseup
const onTagMouseMove = useCallback((e) => {
    e.preventDefault();
    const contentWidth = (contentDivRef.current?.getBoundingClientRect().width || 0);
    const contentLeft = (contentDivRef.current?.getBoundingClientRect().left || 0);
    // 边界值处理
    const threshold = (contentLeft + (TAG_WIDTH / 2));
    if (e.pageX < threshold) {
      setMovingTagPos({ x: 0, y: 0 });
      return;
    }
    let x = e.pageX - originMousePos.x + originTagPos.x;
    x = x > contentWidth - threshold ? contentWidth - threshold : x;
    setMovingTagPos({ x, y: 0 });
}, []);
const onTagMouseUp = useCallback(() => {
    document.removeEventListener('mousemove', onTagMouseMove);
    document.removeEventListener('mouseup', onTagMouseUp);
    setMovingTagPos({ x: 0, y: 0 });
    movingTagPath.current = '';
}, [onTagMouseMove]);

// onMouseMove handler
const onTagOver = useCallback((e) => {
    e.preventDefault();
    updateTags(e.clientX);
}, [updateTags]);
const updateTags = useCallback((clientX: number) => {
    const dropRect = contentDivRef.current?.getBoundingClientRect();
    if (dropRect && movingTagPath.current) {
      const offsetX = clientX - dropRect.left;
      const dragItem = tags.find((item) => item.path === movingTagPath.current) || { path: '', title: '' };
      const col = Math.floor(offsetX / TAG_WIDTH);
      let currentIndex = col;
      const fromIndex = tags.indexOf(dragItem);
      if (fromIndex < currentIndex) {
        currentIndex += 1;
      }
      const currentItem = tags[currentIndex];
      const ordered = insertBefore(tags, dragItem, currentItem);
      if (isEqualBy(ordered, tags, 'path')) return;
      // 矫正更换顺序后标签位置偏移
      if (fromIndex < currentIndex) {
        originMousePos.x += TAG_WIDTH;
      } else {
        originMousePos.x -= TAG_WIDTH;
      }
      setTags(ordered);
    }
}, [tags]);

你可能感兴趣的:(react.js标签栏)