背景

最近接到一个新需求,就是有一个流程的东西,而这个流程可能会比较多,那么在水平就需要 scroll-x,表面上使用触摸板双指即可拖动,但是产品和测试讨论之后觉得鼠标也需要拖拽滑动。

但鼠标可以通过点击滚动条即可滑动,那么,既然需求砍不掉,那就试着做一做。

具体任务也很明确了,就是希望鼠标在流程中可支持拖动。

寻找答案

在网上找了许多的资料,实在是太多重复而无用的内容了,不过功夫不负有心人,找到了几篇有用的资料,如下:

首先是这个第一篇,是在 stack overflow 上看到的,怎么说呢,算是给自己一个寻找方向的博客,刚开始做的时候搜索都不知道怎么表达。

第二篇是给我一些灵感的博客,而第三篇基本上满足我的需求了,不过这里面有一点小问题,在下文代码处继续来说。

解决问题

获取DOM

在 React 中我们可以通过 useRef 来获取 dom 节点,如下进行 ref 绑定。

const statusLineRef = useRef(null);
<div ref={statusLineRef}>

代码分析

其中也有发现一点问题,如下改动:

因为在自己拖动的时候发现,如果我鼠标释放点在 div 里面倒正常,但是当我按着不动,将鼠标放在 div 之外的地方释放,那么,这个释放操作并不会取消掉,鼠标放回 div 中,发现还可以拖动,于是将 el 改成了 document。

以下是解决的代码,使用了 TS,对原作者代码有一定的改动。

useEffect(() => {
    const on = function(
        element: { addEventListener: (arg0: any, arg1: any, arg2: boolean) => void },
        event: any,
        handler: any
    ) {
        if (element && event && handler) {
            element.addEventListener(event, handler, false);
        }
    };

    const off = function(
        element: { removeEventListener: (arg0: any, arg1: any, arg2: boolean) => void },
        event: any,
        handler: any
    ) {
        if (element && event) {
            element.removeEventListener(event, handler, false);
        }
    };

    let targetDrag = {
        isDown: false,
        coord: {
            x: 0,
            y: 0,
        },
    };

    const scrollMousedown = (event: { pageX: number; pageY: number }) => {
        targetDrag.isDown = true;
        targetDrag.coord.x = event.pageX;
        targetDrag.coord.y = event.pageY;
    };
    const scrollMouseup = () => {
        targetDrag.isDown = false;
        targetDrag.coord.x = 0;
        targetDrag.coord.y = 0;
    };

    let el = statusLineRef.current as any;

    const scrollMousemove = (event: { pageX: number }) => {
        let movX = targetDrag.coord.x - event.pageX;
        targetDrag.coord.x = event.pageX;
        if (targetDrag.isDown) {
            el.scrollLeft = el.scrollLeft + movX;
        }
    };

    if (el) {
        on(el, 'mousedown', scrollMousedown);
        on(document, 'mouseup', scrollMouseup);
        on(el, 'mousemove', scrollMousemove);
    }
    return () => {
        if (el) {
            off(el, 'mousedown', scrollMousedown);
            off(document, 'mouseup', scrollMouseup);
            off(el, 'mousemove', scrollMousemove);
        }
    };
}, []);

不用TS的版本:

let statusLineRef = useRef();
useEffect(() => {
    const on = function(
        element,
        event,
        handler
    ) {
        if (element && event && handler) {
            element.addEventListener(event, handler, false);
        }
    };

    const off = function(
        element,
        event,
        handler
    ) {
        if (element && event) {
            element.removeEventListener(event, handler, false);
        }
    };

    let targetDrag = {
        isDown: false,
        coord: {
            x: 0,
            y: 0,
        },
    };

    const scrollMousedown = (event) => {
        targetDrag.isDown = true;
        targetDrag.coord.x = event.pageX;
        targetDrag.coord.y = event.pageY;
    };
    const scrollMouseup = () => {
        targetDrag.isDown = false;
        targetDrag.coord.x = 0;
        targetDrag.coord.y = 0;
    };

    let el = statusLineRef.current;

    const scrollMousemove = (event) => {
        let movX = targetDrag.coord.x - event.pageX;
        targetDrag.coord.x = event.pageX;
        if (targetDrag.isDown) {
            el.scrollLeft = el.scrollLeft + movX;
        }
    };

    if (el) {
        on(el, 'mousedown', scrollMousedown);
        on(document, 'mouseup', scrollMouseup);
        on(el, 'mousemove', scrollMousemove);
    }
    return () => {
        if (el) {
            off(el, 'mousedown', scrollMousedown);
            off(document, 'mouseup', scrollMouseup);
            off(el, 'mousemove', scrollMousemove);
        }
    };
}, []);

转自Chocolate,有删改。


A Student on the way to full stack of Web3.