์ฒ์์ผ๋ก ๋ชจ๋ฐ์ผ ํผ์คํธ ์น์ ๊ตฌํํ๊ฒ ๋์๋ค. ์ฒ์์๋ touch
์ด๋ฒคํธ๋ ๋ ์ด๋ป๊ฒ ๊ตฌํํ๋ ๊ฑฐ์ง? ํ๊ณ ์์ฒญ ๊ฒ์ ๋จน์์๋๋ฐ ๋๋ถ๋ถ์ touch
์ด๋ฒคํธ๋ ํด๋ฆญ๊ณผ ๋์ผํ๊ฒ ๊ตฌํํ๋ฉด ๋ฉ๋๋ค ! ๋ผ๊ณ ํด์ ํด ! ํ๊ณ ์์ฌํ๋ ๊ธฐ์ต์ด ์๋ค.
๊ทธ๋ฐ๋ฐ !!! ๋ชจ๋ฐ์ผ์์๋ ํฐ์น๋ก ์์ฐ์ค๋ฝ๊ฒ ์ข์ฐ๋ก ์คํฌ๋กค์ด ๋๋ ์นดํ ๊ณ ๋ฆฌ ์ปดํฌ๋ํธ ๊ฐ ๋ฐ์คํฌํ์ผ๋ก ๋์ด์ค๋ฉด ์คํฌ๋กค ๋ฐ๋ฅผ ์ด์ฉํ์ง ์๊ณ ๋ ์คํฌ๋กค์ ํ ์ ์๋ค๋ ๊ฑธ ์๊ฒ๋์๋ค. ํด๋น ์ปดํฌ๋ํธ๋ฅผ ๋ด๋นํ์ ๋ถ์ด '์คํฌ๋กค๋ฐ๋ฅผ ์จ๊ธฐ๊ณ ์ถ์์ง๋ง ์จ๊ธฐ๋ฉด ์คํฌ๋กค์ด ์๋ฉ๋๋ค' ๋ผ๊ณ ํ์ ์ ๊ทธ๊ฒ ๋ฌด์จ ์๋ฆฌ์ง?? ํ๊ณ ๋น์์๋ ๋ด ๊ตฌํ๋ ๋ฐ๋น ์ ๋์ด๊ฐ๋ค๊ฐ ํ๋ก์ ํธ ๋ง๋ฌด๋ฆฌ ํ ์๊ฐ์ ๋ด์ด์ ๋ฆฌํฉํ ๋ง์ ํด๋ณด๊ฒ ๋์๋ค.
์คํฌ๋กค๋ฐ๋ฅผ ์จ๊ธฐ๋ ๊ฑด ๊ฐ๋จํ๋ค.
// app > globals.css
.scrollbar-hide {
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}
.scrollbar-hide::-webkit-scrollbar {
display: none; /* Chrome, Safari, Opera */
}
๋ฅผ ์์ฑํ๊ณ ์คํฌ๋กค ๋ฐ๋ฅผ ์จ๊ธฐ๋ ค๋ ์ปดํฌ๋ํธ์ ํด๋์ค๋ช
์ผ๋ก scrollbar-hide
๋ฅผ ๋ฌ์์ฃผ์๋ค.
๊ทธ๋ฌ๊ณ ๋์๋ ์คํฌ๋กค์ด ์๋๋ค๊ณ ์๊ฐํ๋ฉด์ ๋ค๋ฅธ ๋ถ๋ถ ๋ฆฌํฉํ ๋ง ์์
์ ์ ํ๊ณ ์์๋๋ฐ...
๋ฌธ๋ ๋๋ ํธ๋ ํจ๋๋ฅผ ์ฐ๊ณ ์๋ค๋ ์ฌ์ค์ ๊นจ๋ฌ์๋ค....!
์ผ๋ฐ ๋ง์ฐ์ค๋ ๊ฐ๋ก ์คํฌ๋กค์ ์ด๋ป๊ฒ ํ์ง........?
๊ทธ๋์ ํด๋ฆญ์ผ๋ก ์ฎ๊ธฐ๋ ค๊ณ ์๋ฌด๋ฆฌ ํด๋ด๋ ์๋์ด์ ์ด๊ฑฐ ๋๋ฌธ์ ํ์ ๋ง์์ด์๊ตฌ๋ ํ๋ฉด์
๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ ์์ํ๋ค...!
์ผ๋จ ๋ง์ฐ์ค ๋๋๊ทธ๋ก ์คํฌ๋กค ํค์๋๋ก ๊ตฌ๊ธ๋ง์ ์์ฒญ ํด๋ณด๋ฉด์ ๋ช๊ฐ์ ํฌ์คํ ์ ์ฐพ์๋ด๊ฒ ๋์๋ค.
Event Listeners
mousedown
mousemove
mousemove
์ด๋ฒคํธ์๋ค ์คํฌ๋กค์ ๋ฐ์์์ผ์ฃผ๋ ๋ก์ง์ ๋ฃ์ด์ผ ํจ !mouseup
mouseleave
mouseup
๊ณผ ๋์ผํ๊ฒ ๋๋๊ทธ ๋์์ด ์ข
๋ฃ๋์ด์ผ ํ ๊ฒ !๋ด๊ฐ ์์ฑํ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
const scrollRef = useRef<HTMLDivElement>(null); // DOM์ ์ ๊ทผํ๊ธฐ ์ํ ref ๊ฐ
const [isDragging, setIsDragging] = useState(false); // ๋๋๊ทธ ์ค์ธ๊ฐ ?
const [startX, setStartX] = useState(0); // ๋๋๊ทธ ์์ ์ง์
const [scrollLeft, setScrollLeft] = useState(0); // ํ์ฌ ์คํฌ๋กค ์์น: ์๋ ์
๋ฐ์ดํธ
const handleMouseDown = (e: React.MouseEvent) => {
if (!scrollRef.current) return;
setIsDragging(true); // ๋๋๊ทธ( ์คํฌ๋กค ) ์์
setStartX(e.pageX - scrollRef.current.offsetLeft); // ํด๋ฆญ ์์น๋ฅผ ๊ธฐ์ค์ ์ง์
setScrollLeft(scrollRef.current.scrollLeft); // ์์์ scrollLeft ๊ฐ ์ง์
};
const handleMouseMove = (e: React.MouseEvent) => {
if (!isDragging || !scrollRef.current) return; // ๊ณผํ ๊ณ์ฐ ๋ฐฉ์ง !
e.preventDefault();
e.stopPropagation();
const x = e.pageX - scrollRef.current.offsetLeft; // ํ์ฌ ๋ง์ฐ์ค X ์์น ๊ณ์ฐ
const walk = (x - startX) * 2; // ์ด๋ ๊ฑฐ๋ฆฌ( ๋ถ๋๋ฌ์ด ์ด๋ )
scrollRef.current.scrollLeft = scrollLeft - walk; // ์คํฌ๋กค ๊ฑฐ๋ฆฌ๋งํผ ์คํฌ๋กค ์ด๋
};
const handleMouseUp = () => {
setIsDragging(false); // ๋๋๊ทธ ์ค๋จ !
};
// ...codes
<div ref={scrollRef}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
className={`scrollbar-hide scroll-smooth ${isDragging ? 'cursor-grabbing' : 'cursor-grab'}`}`}
๐ฉโ๐ป ์ต์ข ์ ๋ฆฌ
scrollLeft
๋ div์ ํ์ฌ ์ํ ์คํฌ๋กค ์์น(px)- ์ ์ ๊ฐ ๋ง์ฐ์ค๋ก ํด๋ฆญ(
mousedown
)ํ๋ฉดscrollLeft
๋ฅผ ์ ์ฅ- ๋ง์ฐ์ค๋ฅผ ์ด๋(
mousemove
)ํ ๋ ๊ธฐ์กดscrollLeft
๊ฐ์์ ์๋ก์ด ์์น๋ฅผ ๊ณ์ฐํ์ฌ ์คํฌ๋กค ์ด๋- ๋ง์ฐ์ค๋ฅผ ๋ผ๋ฉด(
mouseup
) ํน์ ๋ง์ฐ์ค๊ฐ ๋ ๋๋ฉด(mouseleave
),isDragging
์false
๋ก ์ค์ ํ์ฌ ๋๋๊ทธ ์ข ๋ฃ.
๐ ์ฐธ๊ณ ํ ๋งํฌ