javascript - 使用钩子(Hook)在 React 中拖放

标签 javascript reactjs react-hooks

我主要是一名后端工程师,一直在尝试为我在 React 中制作的 slider 实现简单的拖放操作,但失败了。

首先,我将向您展示不使用去抖的行为: no-debounce

这是去抖动的行为: with-debounce

我从 here 获取的去抖动稍作修改。

我认为我有两个问题,一个是快速闪烁,去抖应该解决,另一个是不正确的 left 我不知道如何修复。由于某种原因,onDrag, rect.left 也添加了父级的所有左边距 (100 + 10)。 Chrome 和 Safari 上会出现这种情况。

我的问题是,如何进行拖放操作?我究竟做错了什么?我的代码如下:

 import React, {
   Dispatch,
   MouseEvent,
   RefObject,
   SetStateAction,
   useEffect,
   useRef,
   useState
 } from "react";

 const useDebounce = (callback: any, delay: number) => {
   const latestCallback = useRef(callback);
   const latestTimeout = useRef(1);

   useEffect(() => {
     latestCallback.current = callback;
   }, [callback]);

   return (obj: any) => {
     const { event, args } = obj;
     event.persist();
     if (latestTimeout.current) {
       clearTimeout(latestTimeout.current);
     }

     latestTimeout.current = window.setTimeout(
       () => latestCallback.current(event, ...args),
       delay
     );
   };
 };

 const setPosition = (
   event: any,
   setter: any
 ) => {
     const rect = event.target.getBoundingClientRect();
     const clientX: number = event.pageX;
     console.log('clientX: ', clientX)
     // console.log(rect.left)
     setter(clientX - rect.left)
 };

 const Slider: React.FC = () => {
   const [x, setX] = useState(null);
   const handleOnDrag = useDebounce(setPosition, 100)

   return (
     <div style={{ position: "absolute", margin: '10px' }}>
       <div
         style={{ position: "absolute", left: "0", top: "0" }}
         onDragOver={e => e.preventDefault()}
       >
         <svg width="300" height="10">
           <rect
             y="5"
             width="300"
             height="2"
             rx="10"
             ry="10"
             style={{ fill: "rgb(96,125,139)" }}
           />
         </svg>
       </div>
       <div
         draggable={true}
         onDrag={event => handleOnDrag({event, args: [setX]})}
         // onDrag={event => setPosition(event, setX)}
         style={{
           position: "absolute",
           left: (x || 0).toString() + "px",
           top: "5px",
           width: "10px",
           height: "10px",
           padding: "0px",
         }}
       >
         <svg width="10" height="10" style={{display:"block"}}>
           <circle cx="5" cy="5" r="4" />
         </svg>
       </div>
     </div>
   );
 };

谢谢。

最佳答案

Draggable 和 onDrag 有其自身的问题。尝试使用简单的鼠标事件。

您可以在以下沙箱中找到工作代码

Edit twilight-shadow-og8v7

<小时/>

其来源如下

import React, { useRef, useState, useEffect, useCallback } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const isDragging = useRef(false);
  const dragHeadRef = useRef();
  const [position, setPosition] = useState(0);

  const onMouseDown = useCallback(e => {
    if (dragHeadRef.current && dragHeadRef.current.contains(e.target)) {
      isDragging.current = true;
    }
  }, []);

  const onMouseUp = useCallback(() => {
    if (isDragging.current) {
      isDragging.current = false;
    }
  }, []);

  const onMouseMove = useCallback(e => {
    if (isDragging.current) {
      setPosition(position => position + e.movementX);
    }
  }, []);

  useEffect(() => {
    document.addEventListener("mouseup", onMouseUp);
    document.addEventListener("mousedown", onMouseDown);
    document.addEventListener("mousemove", onMouseMove);
    return () => {
      document.removeEventListener("mouseup", onMouseUp);
      document.removeEventListener("mousedown", onMouseDown);
      document.removeEventListener("mousemove", onMouseMove);
    };
  }, [onMouseMove, onMouseDown, onMouseUp]);

  return (
    <div
      style={{
        flex: "1",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        height: "100vh"
      }}
    >
      <div
        style={{
          height: "5px",
          width: "500px",
          background: "black",
          position: "absolute"
        }}
      >
        <div
          ref={dragHeadRef}
          style={{
            left: `${position}px`,
            transition: 'left 0.1s ease-out',
            top: "-12.5px",
            position: "relative",
            height: "30px",
            width: "30px",
            background: "black",
            borderRadius: "50%"
          }}
        />
      </div>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

逻辑的关键是e.movementX,它返回自上次发生该事件以来鼠标沿 x 轴移动的距离量。用它来设置dragHeader的左侧位置

关于javascript - 使用钩子(Hook)在 React 中拖放,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59082724/

相关文章:

javascript - 如何添加多个 Outlet 组件 react router dom V6?

javascript - 使用 React hooks 将基于类的组件转换为功能组件

javascript - 是否可以使用 React 中的 useState() 钩子(Hook)在组件之间共享状态?

javascript - 何时在 Chrome/Web 扩展中断开 MutationObserver 的连接?

javascript - D3JS selectAll追加矩形

javascript - 使用 v8 和过滤器/查找比较两个对象数组的高效方法

reactjs - react 路由器 : Why is the useHistory undefined in react?

从按钮到函数的 JavaScript 事件

javascript - ClickAwayListener 不适用于折叠或淡入淡出过渡

reactjs - 检查 typescript 中的 never[]