๋ฃจํธ ์์์ ํ๊ฒ ์์์ ๊ต์ฐจ์ ์ ๊ด์ฐฐํ๋ API
์
๋๋ค.
๋ฃจํธ ์์๋ฅผ ๋ทฐํฌํธ๋ก ํ๊ณ ํ๊ฒ ์์๋ฅผ ์ ๋๋ฉ์ด์ ์ ์คํํ ํ๊ทธ๋ก ์ง์ ํ๋ค๋ฉด ์คํฌ๋กค์ ์ํด์ ๋ทฐํฌํธ์ ํ๊ทธ๊ฐ ๊ต์ฐจํ๋ ๊ฒฝ์ฐ์ ์ํ๋ ์ ๋๋ฉ์ด์ ์ ์คํํ ์ ์์ต๋๋ค.
root
: ๋ฃจํธ ์์๋ฅผ ์ง์ ํ๋ ์์ฑ ( ์๋ต ์ ๋ทฐํฌํธ )rootMargin
: ๋ฃจํธ ์์์ ๋ฒ์๋ฅผ ํ์ฅํ๋ ์์ฑ ( ๋จ์ ์ง์ )threshold
: ๊ต์ฐจ์ ์ ๋ฒ์๋ฅผ ์ง์ ํ๋ ์์ฑ 0.0
~1.0
( ๋ฐฐ์ด๋ ๊ฐ๋ฅ )boundingClientRect
: ํ๊ฒ ์์์ ๊ฒฝ๊ณ ์ฌ๊ฐํ ์ ๋ณดintersectionRatio
: ๋ฃจํธ ์์์ ํ๊ฒ ์์์ ๊ต์ฐจ ๋น์จ ( 0.0
~1.0
)intersectionRect
: ?rootBounds
: ๋ฃจํธ ์์์ ๊ฒฝ๊ณ ์ฌ๊ฐํ ์ ๋ณดtarget
: ํ๊ฒ ์์time
: ๊ต์ฐจ ์๊ฐisIntersecting
: ํ๊ฒ ์์์ ๋ฃจํธ ์์์ threshold
๋งํผ์ ๊ต์ฐจ ์ฌ๋ถconst IO = new IntersectionObserver(callback, options);
IO.observe(element);
const onScroll = useCallback((entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
// ํน์ ํ๊ทธ๊ฐ ๋ทฐํฌํธ์ "threshold"๊ฐ๋งํผ ๋ณด์ฌ์ง๋ฉด ํ์ฌ ์ฝ๋ฐฑํจ์ ์คํ
}, [])
useEffect(() => {
if(!elementRef.current) return;
// ์ฝ๋ฐฑํจ์์ ์ต์
๊ฐ ์ง์
let observer = new IntersectionObserver(onScroll, { threshold: 0.1 });
// ํน์ ์์ ๊ฐ์ ์์
observer.observe(elementRef.current);
// ๊ฐ์ ์ข
๋ฃ
return () => observer?.disconnect();
}, [elementRef]);
import { useRef, useEffect, useCallback } from "react";
type Direction = "up" | "down" | "left" | "right";
type Props = {
direction?: Direction;
duration?: number;
delay?: number;
};
type ReturnType = {
ref: React.MutableRefObject<any>;
style: {
opacity: number;
transform: string | undefined;
};
};
const useScrollFadeIn = ({
direction = "up",
duration = 1,
delay = 0,
}: Props): ReturnType => {
// 2022/06/14 - ์ ๋๋ฉ์ด์
์ ์คํํ ํ๊ทธRef - by 1-blue
const elementRef = useRef<HTMLElement | null>(null);
// 2022/06/14 - ์ง์ ํ ๋ฐฉํฅ์ ๋ฐ๋ฅธ ํธ๋์ง์
๋ฐํ - by 1-blue
const handleDirection = useCallback((dir: Direction) => {
switch (dir) {
case "up":
return "translate3d(0, 50%, 0)";
case "down":
return "translate3d(0, -50%, 0)";
case "left":
return "translate3d(50%, 0, 0)";
case "right":
return "translate3d(-50%, 0, 0)";
default:
return "";
}
}, []);
// 2022/06/14 - IntersectionObserver์ ๋ฑ๋กํ ์ฝ๋ฐฑํจ์ - by 1-blue
const onScroll = useCallback(
([{ isIntersecting }]: IntersectionObserverEntry[]) => {
const { current } = elementRef;
if (!current) return;
// ์ง์ ํ ์๋ฆฌ๋จผํธ๊ฐ "threshold"๋งํผ์ ์ ์ธํ๊ณ ๋ทฐํฌํธ์ ๋ค์ด์๋ค๋ฉด ์คํ
if (isIntersecting) {
current.style.transitionProperty = "all";
current.style.transitionDuration = `${duration}s`;
current.style.transitionTimingFunction = "cubic-bezier(0, 0, 0.2, 1)";
current.style.transitionDelay = `${delay}s`;
current.style.opacity = "1";
current.style.transform = "translate3d(0, 0, 0)";
}
// ์ง์ ํ ์๋ฆฌ๋จผํธ๊ฐ "threshold"๋งํผ์ ์ ์ธํ๊ณ ๋ทฐํฌํธ ๋ฐ์ ์กด์ฌํ๋ค๋ฉด ์คํ
// ์๋ ๋ถ๋ถ์ ๋ฃ์ด์ฃผ๋ฉด ๊ต์ฐจ๋ฒ์ ์ดํ์ผ ๊ฒฝ์ฐ ์ ๋๋ฉ์ด์
์ด ๋ฐ๋๋ก ์คํ๋จ
else {
current.style.transitionProperty = "all";
current.style.transitionDuration = `${duration}s`;
current.style.transitionTimingFunction = "cubic-bezier(0, 0, 0.2, 1)";
current.style.transitionDelay = `${delay}s`;
current.style.opacity = "0";
current.style.transform = handleDirection(direction);
}
},
[delay, duration, handleDirection, direction]
);
// 2022/06/14 - IntersectionObserver ๋ฑ๋ก - by 1-blue
useEffect(() => {
if (!elementRef.current) return;
// ์ข์ฐ๋ก ์์ง์ด๋ ๋ฐฉ์์ ์ ํํ์ ๊ฒฝ์ฐ ์ฃผ์ํด์ผ ํ ์ ์ด ์ ์ฒด ํฌ๊ธฐ์ 50%๋ฅผ ์ด๋ํด์ ๋ทฐํฌํธ ๋ฐ์ผ๋ก ๋ง์ด ๋๊ฐ๋ฒ๋ฆฌ๋ฉด
// ์ ๋๋ฉ์ด์
์คํ์ด ์ ๋ ์ ์๋ค.
// ์๋ํ๋ฉด ์ด๋ฏธ ๋ทฐํฌํธ ๋ฐ์ผ๋ก ์ง์ ํ ๊ต์ฐจ๋ฒ์ ์ด์์ ๋๊ฐ๋ฒ๋ ธ๊ธฐ ๋๋ฌธ์ ์คํฌ๋กค๊ณผ ๊ด๊ณ์์ด ์คํ๋์ง ์์
// ๊ทธ๋ด ๊ฒฝ์ฐ "threshold" ๊ฐ์ ์ค์ด๊ฑฐ๋, "rootMargin"์ ๋๋ฆฌ๋ฉด ๋๋ค.
let observer = new IntersectionObserver(onScroll, { threshold: 0.1, rootMargin: "20px" });
observer.observe(elementRef.current);
return () => observer?.disconnect();
}, [onScroll]);
return {
ref: elementRef,
style: { opacity: 0, transform: handleDirection(direction) },
};
};
export default useScrollFadeIn;
// <article {...useScrollFadeIn({ direction: "right", duration: 1.6 })}> ๊ฐ์ ํ์์ผ๋ก ์ฌ์ฉ