๊ฐ๋ก๋ก ์ฌ๋ผ์ด๋๊ฐ ์๊ธฐ๋ ์ด๋ฏธ์ง ์ฌ๋ผ์ด๋๋ฅผ ๋ง๋๋๋ฐ ์ด๋ฏธ์ง๋ 4๊ฐ ์ ๋ ์๊ณ , ๊ฐ ์ด๋ฏธ์ง๊ฐ ๋ณด์ฌ์ง ๋๋ง๋ค ์ฌ๋ผ์ด๋ ์์ ์๋ ์ค๋ช ์ด ๋ฐ๋๋ ๊ฒ์ ๊ตฌํํด์ผ ํ๋ค.
<ImageSlider>
<ImageContainer ref={imageRef}>
{items.map((item, index) => (
<ImageWrapper key={index}>
<Img
src={`/images/img_renewal${index}.png`}
/>
<ImageTitle>{item.title}</ImageTitle>
</ImageWrapper>
))}
</ImageContainer>
</ImageSlider>
์ฒ์์๋ observer.observe(imageRef.current)๋ก ํด์ ImageContainer๋ฅผ ํ๊ฒ์์๋ก ์ผ๊ณ threshold๋ฅผ [0, 0.25, 0.5, 0.75, 1] ์ด๋ฐ ์์ผ๋ก ๋ฐฐ์ด๋ก ์ค ๋ค์ threshold์ ๋ช๋ฒ์งธ ์ธ๋ฑ์ค๋ฅผ ์ง๋๊ณ ์๋์ง๋ฅผ ์์๋ด์ ๊ฐ์ ๋ณ๊ฒฝํด์ฃผ๊ณ ์ ํ์๋ค. ์ด๋ฏธ์ง ํ๋ํ๋๋ฅผ ๊ด์ฐฐํ๋ ๋ฐฉ๋ฒ์ด ์ง๊ด์ ์ผ๋ก ๋ซ๋ค๊ณ ์๊ฐํ์ผ๋ ๊ฐ๊ฐ์ ์ด๋ฏธ์ง์ ref๋ฅผ ์ฃผ๋ ๋ฐฉ์์ ์๋ ๊ฑฐ ๊ฐ๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฐ๋ฐ threshold์ ์ธ๋ฑ์ค๋ฅผ ์์๋ด๋ ๋ฉ์๋๊ฐ ๋น์ฐํ ์์ ๊ฑฐ๋ผ๊ณ ์๊ฐํ์ง๋ง ๊ทธ๋ฐ ๋ฉ์๋๋ ์กด์ฌํ์ง ์์๊ธฐ ๋๋ฌธ์ intersectionRatio๋ฅผ ์ด์ฉํด ์ธ๋ฑ์ค๋ฅผ ๊ตฌํ๋ ํจ์๋ฅผ ๋ง๋ค์๊ณ ... ๋ฉ๋ฆฌ ๋์๊ฐ๋ ๋๋์ด ๋ค์์ผ๋ ํด๊ฒฐํ ์ ์์ ๊ฑฐ๋ผ๊ณ ์๊ฐํ๋๋ฐ...
useEffect(() => {
const observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
const { intersectionRatio, isIntersecting } = entry;
const thresholdIndex = getThresholdIndex(intersectionRatio);
if (isIntersecting)
setDesc(items[thresholdIndex]?.desc);
});
},
{ threshold: [0, 0.25, 0.5, 0.75, 1] }
);
if (imageRef.current) observer.observe(imageRef.current);
return () => {
if (imageRef.current) observer.unobserve(imageRef.current);
};
}, []);
const getThresholdIndex = ratio => {
const thresholds = [1, 0.75, 0.5, 0.25, 0];
let minDiff = 1;
let minDiffIndex = 0;
for (let i = 0; i < thresholds.length; i++) {
const diff = Math.abs(ratio - thresholds[i]);
if (diff < minDiff) {
minDiff = diff;
minDiffIndex = i;
}
}
return minDiffIndex;
};
๊ทธ๋ฐ๋ฐ ๋ฌธ์ ๋ ์ฌ๋ผ์ด๋์ ๋ง์ง๋ง ์์๊ฐ ๋ทฐํฌํธ ์์ ๋ค์ด์ค๋๋ฐ๋ isIntersecting์ด false๋ก ๋จ๋ ๊ฒ์ด์๋ค. ๋ค๋ฅธ ์์๋ค์ threshold์ ๋ฐ๋ผ ์ฝ๋ฐฑ์ด ์คํ๋ ๋๋ง๋ค true๋ก ์ ์ฐํ๋๋ฐ ๋ง์ง๋ง ์์๋ง false๋ก ๋๋ ๊ฒ์ ์๋ฌด๋ฆฌ ๊ณ ๋ฏผํด๋ด๋ ํด๊ฒฐ์ด ์ ๋์๋ค.
๊ทธ๋์ ๋ค์ ์ฐ์ต์ฅ์ ๊ทธ๋ฆผ์ ๊ทธ๋ฆฌ๋ฉด์ ๊ฐ๋
์ ์ ๋ฆฌํด๋ณด์๋๋ฐ, ๊ฐ์ฅ ๊น๋ํ ๋ฐฉ๋ฒ์ ์ญ์ ์ด๋ฏธ์ง ์์ ๊ฐ๊ฐ์ observeํ๋ ๊ฒ์ด๋ผ๊ณ ํ๋จํ๊ณ , ref๋ ๊ธฐ์กด์ฒ๋ผ Container์๋ง ์ฃผ๋ ๋์ querySelectorAll์ ํตํด ์ด๋ฏธ์ง ์์๋ฅผ ๊ฐ์ ธ์ ๊ฐ๊ฐ์ ํ๊ฒ์์๋ก ์ผ์๋ค.
useEffect(() => {
const observer = new IntersectionObserver(
entries => {
entries.forEach(entry => {
if (entry.isIntersecting)
setDesc(items[entry.target.id]?.desc);
});
},
{ root: null, threshold: 0.9 }
);
if (imageRef.current) {
const images = imageRef.current.querySelectorAll('img');
Array.from(images).forEach((image: HTMLImageElement) =>
observer.observe(image)
);
}
return () => {
if (imageRef.current) observer.disconnect();
};
}, []);
cleanup ํจ์๋ unobserve๊ฐ ์๋๋ผ ๋ชจ๋ ์์์ ๊ด์ฐฐ์ ์ค์ง์ํค๋ disconnect๋ก ๋ณ๊ฒฝํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ธ๋ฑ์ค๋ฅผ ๊ตฌํ๋ ๋ณต์กํ ํจ์ ๋์ ๋ฆฌํด๋ฌธ ์์์ ๊ฐ ์ด๋ฏธ์ง์ index๋ฅผ id๊ฐ์ผ๋ก ์ฃผ๊ณ isIntersecting์ด true๊ฐ ๋๋ ๊ฒฝ์ฐ target์ id๋ฅผ ํตํด ์ค๋ช ์ ๋ฐ๊ฟ์ฃผ์๋ค. threshold๋ ์ด๋ฏธ์ง๊ฐ 90ํผ์ผํธ ์ ๋ ๊ฐ์์ฑ์ด ํ์ธ๋์์ ๋๋ง๋ค ์ฝ๋ฐฑ์ ์คํํ ์ ์๋๋ก ํด์ฃผ์๋ค. ์ฒ์ํ๋ ๋ฐฉ์์ ์ด๋ฏธ์ง๊ฐ ์ ์ฒด ์ปจํ ์ด๋์์ ๋๋ต ์ด์ ๋ ํผ์ผํธ์ ์๋ค๊ณ ์ผ๋งค๋ก ์ ํด์ ๋ฐ๋๋ ํฌ์ธํธ๋ ์ด์ํ๋๋ฐ, ๊ฐ๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ํ๊ฒ์์๋ก ์ผ์ผ๋ ์ง๊ด์ ์ด๊ณ ๊น๋ํ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋์๋ค. ์คํฌ๋กค ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํฌ ๋ ํ๊ฒ์์๋ฅผ ์ด๋ค ๊ฑธ๋ก ํ ์ง์ ๋ํด์ ์ ํํ ํ๋จ์ด ํ์ํ๋ ๊ฑฐ ๊ฐ๋ค. ์ธ๋ฑ์ค ๊ตฌํ๋ ํจ์๋ฅผ ๋ง๋ค ๋๋ถํฐ ๋ณต์กํ๊ฒ ๋์๊ฐ๋ค๋ ๋๋์ด ๋ค์๋๋ฐ, ์ญ์ ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ์กด์ฌํ๋ค.