Reputation: 5150
I've made this horizontal swiper, and it scrolls left and right just fine, but when I try to drag left and right it does nothing. I want to be able to scroll left and right and drag left and right.
If I scroll left or right, then move my cursor, it scrolls all the way to the left.
import React, { useRef, useState, useEffect } from "react";
export default function Swiper() {
const ref = useRef(null);
const [startX, setStartX] = useState<number>(0);
const [startScrollLeft, setStartScrollLeft] = useState<number>(0);
const [myMouseDown, setMyMouseDown] = useState<boolean>(false);
const handleDown = (e: any) => {
console.log("down");
if (!ref.current.contains(e.target)) return;
setMyMouseDown(true);// <=============================== This makes it true
setStartX(e.pageX - ref.current.offsetLeft);
setStartScrollLeft(ref.current.scrollLeft);
};
const handleMove = (e: any) => {
console.log(myMouseDown);// Why this always false when I mouse down and move?
e.preventDefault();
if (!ref.current.contains(e.target)) return;
const mouseX = e.pageX - ref.current.offsetLeft;
const moveX = mouseX - startX;
if (myMouseDown) {
console.log("move"); // <==================================== never fires!
ref.current.scrollLeft = startScrollLeft - moveX;
}
};
const handleUp = () => {
console.log("up");
setMyMouseDown(false); // <=========================== This makes it false
};
useEffect(() => {
document.addEventListener("mouseup", handleUp);
document.addEventListener("mousedown", handleDown);
document.addEventListener("mousemove", handleMove);
return () => {
document.removeEventListener("mouseup", handleUp);
document.removeEventListener("mousedown", handleDown);
document.removeEventListener("mousemove", handleMove);
};
}, []);
const handleScroll = (e: any) => {
const { scrollWidth, scrollLeft, clientWidth } = e.target;
if (scrollLeft + clientWidth === scrollWidth) console.log("end");
if (scrollLeft === 0) console.log("start");
};
return (
<header className="container">
{startX} - {startScrollLeft} - {JSON.stringify(myMouseDown)}
<ul onScroll={handleScroll} ref={ref}>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
</ul>
<style jsx>{`
ul {
overflow-x: auto;
overflow-y: hidden;
white-space: nowrap;
height: 260px;
cursor: grab;
padding: 0;
display: grid;
grid-gap: 20px;
grid-template-columns: repeat(8, 260px);
}
ul::-webkit-scrollbar {
background: #ebeced;
height: 6px;
margin: 0 20px;
}
ul::-webkit-scrollbar-thumb {
background: #c8cad0;
}
li {
display: inline-block;
vertical-align: top;
width: 230px;
height: 230px;
white-space: normal;
background: grey;
}
`}</style>
</header>
);
}
Upvotes: 1
Views: 744
Reputation: 1670
You need to update your dependencies in the useEffect
hook:
useEffect(() => {
document.addEventListener("mouseup", handleUp);
document.addEventListener("mousedown", handleDown);
document.addEventListener("mousemove", handleMove);
return () => {
document.removeEventListener("mouseup", handleUp);
document.removeEventListener("mousedown", handleDown);
document.removeEventListener("mousemove", handleMove);
};
}, [handleDown, handleMove]);
then you need to use the useCallback hook in your handlers:
const handleDown = useCallback((e: any) => {
console.log("down");
setMyMouseDown(true); // <=============================== This makes it true
if (!ref.current.contains(e.target)) return;
setStartX(e.pageX - ref.current.offsetLeft);
setStartScrollLeft(ref.current.scrollLeft);
}, []);
const handleMove = useCallback(
(e: any) => {
e.preventDefault();
console.log("myMouseDown", myMouseDown); // Why this always false when I mouse down and move?
if (!ref.current.contains(e.target)) return;
const mouseX = e.pageX - ref.current.offsetLeft;
const moveX = mouseX - startX;
if (myMouseDown) {
console.log("move"); // <==================================== never fires!
ref.current.scrollLeft = startScrollLeft - moveX;
}
},
[myMouseDown, startScrollLeft, startX]
);
Working code: https://codesandbox.io/s/pedantic-sanne-mfp9i?file=/src/App.tsx
Btw. Setting state on each event is suboptimal. You should use useRef instead of useState here:
const startX = useRef<number>(0);
startX.current = e.pageX - ref.current.offsetLeft;
const moveX = mouseX - startX.current;
Upvotes: 1