ํ์ฌ ๊ท๋ํ ์ฒญ๋ ํน์ ๋์ฅ๊ด๋ฆฌ์๋ฅผ ์ํ ๋์ฅ ๋ฐ์ดํฐ ๊ด๋ฆฌ ์๋น์ค๋ฅผ ๊ตฌํ ์ค์ ์์ต๋๋ค.
ํต์ฌ ๊ธฐ๋ฅ ์ค ํ๋์ธ ์์ธํ์ด์ง์์ โ๐ย ๋ด ์๋ฌผ ์์ธ๋ฅผ ํ ๋์" ํญ์ ์ฌ์ฉ์๊ฐ ๋ฑ๋กํด๋์ ์๋ฌผ ์ ์ฒด ์ฐจํธ๋ฅผ ์ค์์ดํ๋ก ๋๊ธฐ๋ฉฐ ํ ๋์ ๋ณด์ฌ์ฃผ๋ ๊ธฐ๋ฅ์ ๊ตฌํํ์ต๋๋ค.
๊ฐ๋ฐ ์ผ์ ์ react-slick
, swiper
์ ๊ฐ์ ์บ๋ฌ์
๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋์
๋ ๊ณ ๋ คํ์ผ๋ ์ค์์ดํ์ ์ญ๋์ ์ธ ์ ๋๋ฉ์ด์
์ด ๋ค์ด๊ฐ๋ฉด ๊ฐ๋
์ฑ์ด ๋จ์ด์ง๊ณ , ์ํ๋ ๋ ์ด์์์ผ๋ก ๊ตฌํํ๊ณ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ด ์ง์ ๊ตฌํํ๊ธฐ๋ก ๊ฒฐ์ !
// MarketPrice.js
const MarketPrice = () => {
const scrollRef = useRef(null);
const [isDrag, setIsDrag] = useState(false);
const [startX, setStartX] = useState();
const onDragStart = (e) => {
e.preventDefault();
setIsDrag(true);
setStartX(e.pageX + scrollRef.current.scrollLeft);
};
const onDragEnd = () => {
setIsDrag(false);
};
const onDragMove = (e) => {
if (isDrag) {
const { scrollWidth, clientWidth, scrollLeft } = scrollRef.current;
scrollRef.current.scrollLeft = startX - e.pageX;
if (scrollLeft === 0) {
setStartX(e.pageX);
} else if (scrollWidth <= clientWidth + scrollLeft) {
setStartX(e.pageX + scrollLeft);
}
}
};
return (
...
<MyCropsChartWrap
onMouseDown={onDragStart}
onMouseUp={onDragEnd}
onMouseMove={onDragMove}
onMouseLeave={onDragEnd}
ref={scrollRef}
>
<MyCropsMarketPriceCard checkedInputs={checkedInputs} />
</MyCropsChartWrap>
...
)
}
1๏ธโฃย ์ข์ฐ ์ฌ๋ผ์ด๋ ์คํฌ๋กค์ ์์ง์์ ํด๋น DOM์ย scrollLeft๋ก ์์ง์. ย
scrollLeft๋ฅผ ์ป๊ธฐ ์ ํดย useRef๋ฅผ ์ฌ์ฉํ์ฌ DOM์ ์ ๊ทผ
1๏ธโฃ onMouseDown
๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ ๋๋ฅด๊ณ ์๋ ์ํ
2๏ธโฃ onMouseUp
๋ง์ฐ์ค ์ผ์ชฝ ๋ฒํผ ๋ ์ํ
3๏ธโฃย onMouseMove
ํด๋ฆญ ์ฌ๋ถ์ ๊ด๊ณ์์ด ๋ง์ฐ์ค๋ฅผ ์์ง์ด๋ ์ํ
4๏ธโฃ onMouseLeave
DOM์์ ๋ง์ฐ์ค๊ฐ ๋ฒ์ด๋ฌ๋์ง ์ฒดํฌํ๋ ์ด๋ฒคํธ
1๏ธโฃ DOM.scrollWidth
์คํฌ๋กค ํ ์ ์๋ ์ด ๊ธธ์ด
2๏ธโฃ DOM.clientWidth
์ค์ ํ max width ( ํ๋ฉด์ ๋ณด์ด๋ ์คํฌ๋กค์ ๊ธธ์ด )
3๏ธโฃ DOM.scrollLeft
์คํฌ๋กค ๊ฐ์ฅ ์ผ์ชฝ (DOM.scrollLeft = 0
ย ) ๋ถํฐ ์ด๋ํ ์คํฌ๋กค ๊ธธ์ด,์ฆย DOM.scrollLeft
ย ๊ธธ์ด๋งํผ ์คํฌ๋กค ์ด๋
4๏ธโฃ mouseEvent.pageXonMouseDown
์ x ์ขํ
๐ ์ง๋์น๊ฒ ๋ง์ ๋ ๋๋ง
๐ padding-left ๋ก ์ธํด ๋ง์ง๋ง์นด๋๊ฐ ์งค๋ ค ๋ณด์ด๋ ๋ฌธ์
Throttle
๋ก ์ ์ดonScroll
์ด๋ฒคํธ์ ๊ฐ์ด onMouseMove
๋ ์ ๋ง์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๊ณ ,
์ง๋์น๊ฒ ๋ง์ ์ด๋ฒคํธ๋ฅผ ์ ์ดํ๊ธฐ ์ํด Throttle
๋ฅผ ์ฌ์ฉํด์ ํด๊ฒฐํ์ต๋๋ค.
Throttle ๊ณผ Debounce
Throttle
- ์ฌ๋ฌ ๋ฒ ๋ฐ์ํ๋ ์ด๋ฒคํธ๋ฅผ ์ผ์ ์๊ฐ ๋์ ํ๋ฒ๋ง ์คํ ๋๋๋ก ๋ง๋๋ ๊ฐ๋
- ์ด๋ฒคํธ ๋ฐ์ ์์ ์ ์ผ์ ์๊ฐ ๋์ ๊ธฐ๋ค๋ฆฌ๊ณ , ์ด๋ฒคํธ ๋๋ ํ ์ฌ์ฐจ ๊ธฐ๋ค๋ฆฐ๋ค.
Debounce
- ์ฌ๋ฌ ๋ฒ ๋ฐ์ํ๋ ์ด๋ฒคํธ์์ ๊ฐ์ฅ ๋ง์ง๋ง ์ด๋ฒคํธ ๋ง์ ์คํ ํ๋๋ก ๋ง๋๋ ๊ฐ๋
- ๋ง์ง๋ง ์ด๋ฒคํธ์์ ์ผ์ ์๊ฐ๋์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ค๋ฉด, ๋ ์ผ์ ์๊ฐ์ ๊ธฐ๋ค๋ฆฐ๋ค.
์ฑ๋ฅ ์์ผ๋ก๋ ๋๋ฐ์ด์ฑ์ด ์ ๋ฆฌํ์ง๋ง ๋๋ฐ์ด์ฑ์ ์๋ฌด๋ฆฌ ๋ง์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํด๋ ๋ชจ๋ ๋ฌด์ํ๊ณ ํน์ ์๊ฐ ์ฌ์ด์ ์ด๋ค ์ด๋ฒคํธ๋ ๋ฐ์ํ์ง ์์์ ๋ ๋ฑ ํ๋ฒ๋ง ๋ง์ง๋ง ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํค๋ ๊ธฐ๋ฒ ์ด๊ธฐ ๋๋ฌธ์ ๋๋ฐ์ด์ฑ์ ์ ์ฉํ ๊ฒฝ์ฐ, ์ฌ๋ฌ ์ด๋ฒคํธ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ์ค์์ดํ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์์ต๋๋ค.
์ค๋กํ์ ์ ์ด๋ X ๋ฐ๋ฆฌ ์ด๋ง๋ค ์ ๊ธฐ์ ์ผ๋ก ๊ธฐ๋ฅ ์คํ์ ๋ณด์ฅํ๋ฏ๋ก ์ค๋กํ์ ์ ์ฉํ์ต๋๋ค.
delay๋ฅผ ์ผ๋ฐ์ ์ธ 250ms๋ก ์ค์ ํ๋ฉด ๋๊ธฐ๋ ๋๋์ด ๋ค์ด 100ms๋ก ์ค์ ํ์ต๋๋ค.
// MarketPrice.js
const MarketPrice = () => {
const scrollRef = useRef(null);
const [isDrag, setIsDrag] = useState(false);
const [startX, setStartX] = useState();
const onDragStart = (e) => {
e.preventDefault();
setIsDrag(true);
setStartX(e.pageX + scrollRef.current.scrollLeft);
};
const onDragEnd = () => {
setIsDrag(false);
};
const onDragMove = (e) => {
if (isDrag) {
const { scrollWidth, clientWidth, scrollLeft } = scrollRef.current;
scrollRef.current.scrollLeft = startX - e.pageX;
if (scrollLeft === 0) {
setStartX(e.pageX);
} else if (scrollWidth <= clientWidth + scrollLeft) {
setStartX(e.pageX + scrollLeft);
}
}
};
// ๐ ์ฌ๊ธฐ์ ์ค๋กํ ์ถ๊ฐ!
const throttle = (func, ms) => {
let throttled = false;
return (...args) => {
if (!throttled) {
throttled = true;
setTimeout(() => {
func(...args);
throttled = false;
}, ms);
}
};
};
const delay = 100;
const onThrottleDragMove = throttle(onDragMove, delay);
return (
...
<MyCropsChartWrap
onMouseDown={onDragStart}
onMouseUp={onDragEnd}
onMouseMove={isDrag ? onThrottleDragMove : null}
onMouseLeave={onDragEnd}
ref={scrollRef}
>
<MyCropsMarketPriceCard checkedInputs={checkedInputs} />
</MyCropsChartWrap>
...
)
}
์ค์์ดํ๋๋ ๋์์ธ์ ์นด๋ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ธ๋ div์ padding-left
๋ฅผ ์ ์ฉํ์ต๋๋ค.
๊ทธ๋ฌ๋๋ padding-left
๊ธธ์ด๋งํผ ์งค๋ ค๋ณด์ด๋ ๋ง์ง๋ง์นด๋
clientWidth
๋ฅผ ๊ธฐ์ค์ผ๋ก scrollํ ๊ธธ์ด ๋งํผ๋ง ์ค์์ดํ๋๊ธฐ ๋๋ฌธ์ ์นด๋ ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ธ๋ div์ padding-left
๊ธธ์ด๋งํผ ๋ง์ง๋ง ์นด๋๊ฐ ์๋ ค๋ณด์ด๋ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์นด๋ ๋ฆฌ์คํธ ๋ฐฐ์ด ๋ง์ง๋ง์ div๋ฅผ ์ถ๊ฐํด์ ์ํ๋ ๊ธธ์ด๋งํผ ์ค์์ดํ๋๋๋ก ์์ ํ์ต๋๋ค.
// MyCropsMarketPriceCard.js
const MyCropsMarketPriceCard = () => {
// 1๏ธโฃ ๊ธฐ์กด ๋ฐ์ดํฐ ํ์์ ๋น๊ฐ์ ๋ด์ ๊ฐ์ฒด๋ฅผ ๋ฐฐ์ด์ ์ถ๊ฐ
const data = { id: 0, name: "", category: "", type: "" };
const usersNewCropList = [];
userInfo !== undefined &&
userInfo?.crops.map((list) => {
return usersNewCropList.push(list);
});
usersNewCropList.push(data);
return (
usersNewCropList.map((list, index) => {
return (
<React.Fragment key={index}>
// 2๏ธโฃ ์ผํญ์ฐ์ฐ์๋ก list์ ๋ง์ง๋ง index ํฌ๋ช
๋ฐ์ค ์ ์ฉ
{usersNewCropList.length !== index + 1 ? (
<Wrap>
...
</Wrap>
)
) : (
<EndWrap>
...
</EndWrap>
)}
</React.Fragment>
);
})
)
})
}
๐งโ๐ป ๋ง์ฐ์ค ๋๋๊ทธ๋ก ์ข์ฐ ์คํฌ๋กค ๊ตฌํ
๐งโ๐ป Debounce ์ Throttle ์ฐจ์ด