Búsqueda de sitios web

reactjs - Hacer clic y arrastrar para desplazarse con el ratón, Componente de React con TypeScript


Estoy tratando de crear un componente React reutilizable que me permita desplazarme horizontalmente usando un ratón al hacer clic y arrastrar hacia la izquierda y la derecha, tal como lo harías con un trackpad o cualquier pantalla táctil.

Comportamiento esperado: El div debe desplazarse hacia la derecha e izquierda siguiendo el arrastre del ratón

Comportamiento obtenido: Hacer clic y arrastrar no desplaza. Pero al colocar el ratón sobre el Div en cuestión y moverlo hacia los lados sí desplazará el div, hasta cierto punto.

He intentado tantas ideas pero todavía no puedo hacer que funcione.

He intentado varias formas de hacerlo funcionar... intentando ajustar la barra de desplazamiento (parece que no es posible) con y sin eventListeners... Pero en general diría que mi código podría funcionar con algunos ajustes.

Estoy intentando adaptar este ejemplo que funciona para mi componente de React: Ejemplo que funciona pero no con React

Estoy creando un componente Draggable que envolverá cualquier div de la aplicación que tenga desbordamiento horizontal desde la vista. La lógica de apuntar al div hijo desde Draggable está bien (usando useRef)

Aquí hay un CodePen reproduciendo en el que estoy: CodePen con mi código

Aquí está mi componente:

import React, { useState, ReactNode, useRef, RefObject, useEffect } from "react";

type DraggableProps = {
  innerRef: RefObject<HTMLDivElement>;
  rootClass: string;
  children: ReactNode;
};

export const Draggable = ({ innerRef, rootClass = "", children }: DraggableProps) => {
  const slider = innerRef.current as HTMLElement;
  const ourRef = useRef<HTMLDivElement>(null);
  const [pageX, setPageX] = useState(0);
  const [scrollX, setScrollX] = useState(0);
  const [isScrolling, setIsScrolling] = useState(false);

  useEffect(() => {
    handleMouseDown()
  },[isScrolling])

  const handleMouseDown = () => {
      setIsScrolling(true);
    slider?.addEventListener("mousedown", (e) => {
      console.log("Mouse down => event", e);
      setPageX(e.pageX - slider.offsetLeft);
      setScrollX(slider?.scrollLeft);
    });
    slider?.addEventListener("mouseleave", () => {
      console.log("Mouse leave");
      slider.removeEventListener("mousedown", () => {console.log("remove mouse down");
      });
      slider.removeEventListener("mousemove", () => {console.log("remove mouse move");
      });
      setIsScrolling(false);
    });
    slider?.addEventListener("mouseup", () => {
      console.log("Mouse up");
      slider.removeEventListener("mousedown", () => {console.log("remove mouse down");
      });
      slider.removeEventListener("mousemove", () => {console.log("remove mouse move");
      });
      setIsScrolling(false);
    });
    slider?.addEventListener("mousemove", (e) => {
      if (!isScrolling) return;
      e.preventDefault();
      console.log("Mouse move => pageX", pageX);

      const x = e.pageX - slider?.offsetLeft;
      const walk = x - pageX;
      slider.scrollLeft = scrollX - walk;
    });  
  };

  return (
    <div ref={ourRef} onMouseDown={handleMouseDown} className={rootClass}>
      {children}
    </div>
  );
};

Respuesta #1

const Draggable = ({rootClass = "", children }) => {
  const ourRef = useRef(null);
    const [isMouseDown, setIsMouseDown] = useState(false);
    const mouseCoords = useRef({
        startX: 0,
        startY: 0,
        scrollLeft: 0,
        scrollTop: 0
    });
  const [isScrolling, setIsScrolling] = useState(false);
    const handleDragStart = (e) => {
        if (!ourRef.current) return
      const slider = ourRef.current.children[0];
        const startX = e.pageX - slider.offsetLeft;
        const startY = e.pageY - slider.offsetTop;
        const scrollLeft = slider.scrollLeft;
        const scrollTop = slider.scrollTop;
        mouseCoords.current = { startX, startY, scrollLeft, scrollTop }
        setIsMouseDown(true)
        document.body.style.cursor = "grabbing"
    }
    const handleDragEnd = () => {
        setIsMouseDown(false)
        if (!ourRef.current) return
        document.body.style.cursor = "default"
    }
    const handleDrag = (e) => {
        if (!isMouseDown || ! ourRef.current) return;
        e.preventDefault();
        const slider = ourRef.current.children[0];
        const x = e.pageX - slider.offsetLeft;
        const y = e.pageY - slider.offsetTop;
        const walkX = (x - mouseCoords.current.startX) * 1.5;
        const walkY = (y - mouseCoords.current.startY) * 1.5;
        slider.scrollLeft = mouseCoords.current.scrollLeft - walkX;
        slider.scrollTop = mouseCoords.current.scrollTop - walkY;
        console.log(walkX, walkY)
    }

  return (
    <div ref={ourRef} onMouseDown={handleDragStart} onMouseUp={handleDragEnd} onMouseMove={handleDrag} className={rootClass + "flex overflow-x-scroll"}>
      {children}
    </div>
  );
};

He modificado tu código a una versión funcional que puedes encontrar aquí https://codepen.io/sandstone991/pen/vYzvZNa

Artículos relacionados