乐趣区

关于react.js:前端仿Chrome实现标签栏react2

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

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]);
退出移动版