reactでドラッグアンドドロップを実装する
2021年 07月 14日 水曜日
動機
reactを使ってドラッグアンドドロップを実装したいときに, npmのライブラリで使えそうなのは以下のものがある.
- https://github.com/react-dnd/react-dnd
- https://github.com/react-grid-layout/react-draggable
- https://github.com/atlassian/react-beautiful-dnd
ただし,
- ドラッグアンドドロップを実装するのにそんなライブラリがいるのか疑問がある.
- こまかくカスタマイズできるドラッグアンドロップ機能が欲しかった.
という理由があったので自前で実装してみた. 本体は50行程度で実装できた.
type DragBoxProp = {
    width: number,
    height: number,
    initX: number,
    initY: number
}
const DragBox = ({ width, height, initX, initY }: DragBoxProp) => {
    const [x, setX] = useState(initX);
    const [y, setY] = useState(initY);
    const isDrg = useRef(false);
    const ox = useRef(0);
    const oy = useRef(0);
    useEffect(() => {
        setX(initX);
        setY(initY);
    }, [initX, initY]);
    const onMove = useCallback((e: any) => {
        if (!isDrg.current) return;
        let bd = e.target.ownerDocument.scrollingElement;
        const px = e.clientX + bd.scrollLeft - ox.current;
        const py = e.clientY + bd.scrollTop - oy.current;
        setX(px);
        setY(py);
    }, [setX, setY]);
    const _onUp = useCallback(async (e: any) => {
        ox.current = 0;
        oy.current = 0;
        isDrg.current = false;
        let el = e.target.ownerDocument;
        el.removeEventListener('mousemove', onMove, { capture: true });
        el.removeEventListener('mouseup', _onUp, { capture: true });
    }, [onMove, x, y]);
    const _onDown = useCallback(async (e: any) => {
        ox.current = e.nativeEvent.offsetX;
        oy.current = e.nativeEvent.offsetY;
        isDrg.current = true;
        let el = e.target.ownerDocument;
        el.addEventListener('mouseup', _onUp, { capture: true });
        el.addEventListener('mousemove', onMove, { capture: true });
    }, [_onUp, onMove, x, y]);
    return (
        <div onMouseUp={_onUp} onMouseDown={_onDown} style={{ width: width, height: height, background: "red", position: 'absolute', left: x, top: y, }} />
    );
}動くプロジェクトはこちら.
https://github.com/mk-system/react-drag

はまったポイント
普通に実装すると, カーソルがコンポーネントからはみでたタイミングで反応しなくなってしまう.
そのため親のコンポーネントに動的にcallbackをフックする必要があった.
この記事をシェア