Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
447 views
in Technique[技术] by (71.8m points)

javascript - 使用钩子在React中拖放(Drag and Drop in React using hooks)

I'm mainly a backend engineer and have been trying to implement, and failing, a simple drag and drop for a slider I am making in React.

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

First I will show you the behavior without using debounce: no-debounce

(首先,我将向您展示不使用反跳的行为: 无反跳)

And here is the behavior with debounce: with-debounce

(这是debounce的行为: with-debounce)

The debounce I took from here with a little modification.

(我从这里进行了一些修改后的反跳。)

I think I have two problems, one is fast flickering, which debounce should solve, and the other is the incorrect left which I cannot figure how to fix.

(我认为我有两个问题,一个是快速闪烁,应该解决抖动问题,另一个是我不知道如何解决的不正确left 。)

For some reason, onDrag, rect.left has all the left margins of the parents (100 + 10) added to it as well.

(由于某些原因,onDrag,rect.left也添加了父母的所有左边界(100 + 10)。)

This happens on Chrome and Safari.

(这发生在Chrome和Safari上。)

My question is, how do I make this drag and drop work?

(我的问题是,如何使这种拖放工作?)

What am I doing wrong?

(我究竟做错了什么?)

My code is below:

(我的代码如下:)

 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>
   );
 };

Thank you.

(谢谢。)

  ask by plumSemPy translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Draggable and onDrag hast its own woes.

(Draggable和onDrag有自己的麻烦。)

Tried my hand with simple mouse events.

(用简单的鼠标事件尝试了我的手。)

You can find a working code in the following sandbox

(您可以在以下沙箱中找到有效的代码)

编辑twilight-shadow-og8v7


The source for it is as follows

(来源如下)

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);

The crux of the logic is e.movementX which returns the amount of distance moved along x axis by the mouse since the last occurrence of that event.

(逻辑的症结是e.movementX ,它返回自上次发生该事件以来鼠标沿x轴移动的距离量。)

Use that to set the left position of the dragHeader

(使用它来设置dragHeader的左侧位置)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...