Created
February 7, 2025 17:37
-
-
Save theosanderson/1a9bfd65d882bbe2444fd2f68e37db20 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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