Skip to content

Instantly share code, notes, and snippets.

@theosanderson
Created February 7, 2025 17:37
Show Gist options
  • Save theosanderson/1a9bfd65d882bbe2444fd2f68e37db20 to your computer and use it in GitHub Desktop.
Save theosanderson/1a9bfd65d882bbe2444fd2f68e37db20 to your computer and use it in GitHub Desktop.
import { useEffect, useRef, useState } from 'react';
export default function LargeComponent() {
const scrollRef = useRef(null);
const trackRef = useRef(null);
const [scrollLeft, setScrollLeft] = useState(0);
const [maxScroll, setMaxScroll] = useState(0);
const [handleWidth, setHandleWidth] = useState(0);
const [dragging, setDragging] = useState(false);
const [startX, setStartX] = useState(0);
const [startScrollLeft, setStartScrollLeft] = useState(0);
useEffect(() => {
function updateSizes() {
if (scrollRef.current && trackRef.current) {
const clientWidth = scrollRef.current.clientWidth;
const scrollWidth = scrollRef.current.scrollWidth;
setMaxScroll(scrollWidth - clientWidth);
const trackWidth = trackRef.current.offsetWidth;
setHandleWidth((clientWidth / scrollWidth) * trackWidth);
}
}
updateSizes();
window.addEventListener('resize', updateSizes);
return () => window.removeEventListener('resize', updateSizes);
}, []);
const handleScroll = (e) => {
setScrollLeft(e.target.scrollLeft);
};
const onMouseDownHandle = (e) => {
setDragging(true);
setStartX(e.clientX);
setStartScrollLeft(scrollLeft);
e.preventDefault();
};
const onMouseMove = (e) => {
if (!dragging) return;
if (scrollRef.current && trackRef.current) {
const trackWidth = trackRef.current.offsetWidth;
const clientWidth = scrollRef.current.clientWidth;
const scrollWidth = scrollRef.current.scrollWidth;
const maxScrollVal = scrollWidth - clientWidth;
const deltaX = e.clientX - startX;
const scrollDelta = (deltaX / (trackWidth - handleWidth)) * maxScrollVal;
scrollRef.current.scrollLeft = startScrollLeft + scrollDelta;
}
};
const onMouseUp = () => {
if (dragging) {
setDragging(false);
}
};
useEffect(() => {
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
return () => {
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', onMouseUp);
};
}, [dragging, startX, startScrollLeft, handleWidth]);
let handlePosition = 0;
if (trackRef.current && maxScroll > 0) {
const trackWidth = trackRef.current.offsetWidth;
handlePosition = (scrollLeft / maxScroll) * (trackWidth - handleWidth);
}
return (
<div>
<div
ref={scrollRef}
onScroll={handleScroll}
className="overflow-x-scroll"
>
<div style={{ width: '5000px', height: '5000px' }}>
<div className="h-full w-full bg-gradient-to-br from-blue-500 to-purple-600 p-8">
<div className="h-full w-full border-8 border-white rounded-xl flex items-center justify-center">
<h1 className="text-6xl font-bold text-white">
5000 x 5000 Component
</h1>
</div>
</div>
</div>
</div>
<div
ref={trackRef}
className="fixed bottom-4 left-0 right-0 mx-auto h-3 w-4/5 bg-gray-200 rounded-full"
>
<div
onMouseDown={onMouseDownHandle}
className="absolute top-0 left-0 h-full bg-blue-500 rounded-full cursor-grab active:cursor-grabbing"
style={{
width: `${handleWidth}px`,
transform: `translateX(${handlePosition}px)`,
}}
/>
</div>
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment