我正在尝试从头开始实现拖动选择反应但卡在滚动中

问题描述 投票:0回答:1

这是我为实现拖动选择所做的代码。

项目生成代码


const items = [
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
    37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70,
    71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103,
    104,
].map((i) => ({ item: i, selected: i === 1 }));

这是负责选择的实际代码

    const [data, setData] = useState(items);
    const [isSelecting, setIsSelecting] = useState(false);
    const [start, setStart] = useState<Coords>({ x: 0, y: 0, screenX: 0, screenY: 0 });
    const [end, setEnd] = useState<Coords>({ x: 0, y: 0, screenX: 0, screenY: 0 });
    const ref = useRef<HTMLDivElement>(null);
    useEffect(() => {
        function handleMouseDown(e: any) {
            if (e.target.closest(".selectable")) return;
            setIsSelecting(true);
            setStart({ x: e.clientX, y: e.clientY, screenX: e.screenX, screenY: e.screenY });
            setEnd({ x: e.clientX, y: e.clientY, screenX: e.screenX, screenY: e.screenY });
            setData((data) => [...data.map((item) => ({ ...item, selected: false }))]);
        }
        ref.current?.addEventListener("mousedown", handleMouseDown);
        return () => {
            ref.current?.removeEventListener("mousedown", handleMouseDown);
        };
    }, [ref]);

    function handleMouseMove(e: React.MouseEvent<HTMLDivElement>) {
        if (!isSelecting) return;
        console.log("START");
        console.log({ clientX: start.x, clientY: start.y, screenX: start.screenX, screenY: start.screenY });
        console.log("END");
        console.log({ clientX: e.clientX, clientY: e.clientY, screenX: e.screenX, screenY: e.screenY });
        setEnd({ x: e.clientX, y: e.clientY, screenX: e.screenX, screenY: e.screenY });
        const selected = [...data];
        const elements = document.getElementsByClassName("selectable");
        for (let i = 0; i < elements.length; i++) {
            const rect = elements[i].getBoundingClientRect();
            const elementRect = {
                left: rect.left + window.pageXOffset,
                top: rect.top + window.pageYOffset,
                right: rect.right + window.pageXOffset,
                bottom: rect.bottom + window.pageYOffset,
            };
            if (
                ((elementRect.left >= Math.min(start.x, end.x) && elementRect.left <= Math.max(start.x, end.x)) ||
                    (elementRect.right >= Math.min(start.x, end.x) && elementRect.right <= Math.max(start.x, end.x))) &&
                ((elementRect.top >= Math.min(start.y, end.y) && elementRect.top <= Math.max(start.y, end.y)) ||
                    (elementRect.bottom >= Math.min(start.y, end.y) && elementRect.bottom <= Math.max(start.y, end.y)))
            ) {
                selected[i].selected = true;
            } else {
                selected[i].selected = false;
            }
        }
        setData(selected);
    }

    function handleMouseUp() {
        setIsSelecting(false);

        reset();
    }
    const reset = () => {
        setStart({ x: 0, y: 0, screenX: 0, screenY: 0 });
        setEnd({ x: 0, y: 0, screenX: 0, screenY: 0 });
    };

生成叠加层

const overlayStyle: any = {
        position: "absolute",
        backgroundColor: colors.slate[800],
        opacity: 0.5,
        border: "1px dotted",
        borderColor: colors.slate[300],
        left: `${Math.min(start.x, end.x) - (ref.current?.offsetLeft || 0)}px`,
        top: `${Math.min(start.y, end.y) - (ref.current?.offsetTop || 0) + (ref.current?.scrollTop || 0)}px`,
        width: `${Math.abs(end.x - start.x)}px`,
        height: `${Math.abs(end.y - start.y) + (ref.current?.scrollTop || 0)}px`,
        display: isSelecting ? "block" : "none",
        pointerEvents: "none",
    };

JSX 模板


    return (
        <>
            <div onMouseUp={handleMouseUp} onMouseMove={handleMouseMove} className="relative p-4 overflow-auto bg-slate-600 h-96" ref={ref}>
                <ul className="flex flex-wrap gap-2">
                    {data.map((item) => (
                        <li
                            onClick={() => {
                                console.log("a");
                            }}
                            className={cx(
                                "flex items-center justify-center w-24 text-white hover:border-4 hover:border-slate-500 rounded-lg select-none aspect-square  bg-slate-700 selectable cursor-pointer",
                                { "border-sky-500 border-4 hover:border-sky-500": item.selected }
                            )}
                            key={item.item}
                        >
                            Item {item.item}
                        </li>
                    ))}
                </ul>
                <div style={overlayStyle}></div>
            </div>

            <button
                onClick={() => {
                    console.log(ref);
                }}
            >
                1a
            </button>
        </>
    );

一切正常,

但问题是当元素很重要并显示滚动时,它不会像选择叠加一样工作。

感谢您的宝贵时间!

javascript reactjs typescript select drag
1个回答
0
投票

实际上,您是在比较文档位置和屏幕位置。如果你的元素占据了整个屏幕,你的 clientX 和 screenX 对于鼠标来说基本上是一样的。

当一个元素滚出屏幕顶部时,它将有一个负的客户坐标。您正在添加滚动位置,它给出了元素在文档中的位置。

但是,您的鼠标坐标并没有以相同的方式偏移。

您应该能够通过将滚动偏移量添加到鼠标坐标来将鼠标坐标保存为文档偏移量,然后您将比较所有内容作为文档坐标。

© www.soinside.com 2019 - 2024. All rights reserved.