오늘 제가 구현하고 싶은 기능은 아래 사진처럼 항상 떠 있는 말풍선을 스크롤 했을 때 FadeIn 되도록 하는 것입니다. 처음에 생각한 방법은 현재 스크롤 된 간격을 구해서, 특정 위치에 도달하면 display: none으로 설정된 말풍선이 보여지도록 바꾸는 것이었는데, 찾아보니 hooks 를 사용하여 간편하게 구현할 수 있다고 하여 hooks를 이용한 방식으로 구현하였습니다.
제가 custom hooks를 사용한 가장 큰 이유는 반복되는 로직을 다양한 곳에서 재사용하기 위함입니다.
현재 제가 개발 중인 웹에서는 FadeIn 효과가 한 곳에서만 사용되지만, 보통 규모가 큰 웹에서는 같은 animation을 다양한 곳에서 적용하게 됩니다. 그래서 custom hooks를 사용하여 animation을 구현해 둔다면 다양한 곳에서 같은 animation을 쉽게 적용할 수 있습니다.
만약 제가 개발중인 웹에서 페이지가 추가되고 같은 애니메이션을 활용할 필요가 생긴다면 같은 커스텀 훅을 가져와서 쉽게 구현할 수 있습니다.
아래 코드에서는 다음과 같은 기능을 합니다. 이벤트가 발생하기 전에는 ref가 가리키고 있는 element를 opacity= 0, translate3d(0, 50%, 0) 으로 숨겨 놓다가 (up 방향 기준), 이벤트가 발생하면 onScroll 함수에서 opacity=1, translate3d(0, 0, 0) 로 바꿔주어 element를 제자리에 보여줍니다.
const useScrollFadeIn = () => {
const element = useRef();
const onScroll = useCallback(
([entry]:any) => {
const { current }:any = element;
if (entry.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)';
}
},
[delay, duration],
);
useEffect(() => {
let observer: any;
if (element.current) {
observer = new IntersectionObserver(onScroll, { threshold: 0.7 });
observer.observe(element.current);
}
return () => observer && observer.disconnect();
}, [onScroll]);
return {
ref: element,
style: { opacity: 0, transform: handleDirection(direction) },
};
};
handleDirection 부분에는 적용하고 싶은 translate3d 값 을 넣어주면 됩니다. 저는 props로 받은 방향에 따라 다른 translate3d 값을 넣어주기위해 handleDirection 이라는 함수를 만들어두고 값을 넣어주었습니다.
위에서 FadeIn 기능을 구현할 때 IntersectionObserver 라는 것이 사용되었습니다.
Intersection Observer는 타겟 엘리먼트와, 타겟 엘리먼트의 부모나 뷰포트가 교차하는 부분의 변화를 비동기적으로 관찰하는 API입니다.
Intersection Observer는 비동기적입니다. intersection이 일어날 때 인자로 넘겨준 callback을 실행시켜줍니다.
observer = new IntersectionObserver(onScroll, { threshold: 0.7 });
observer.observe(element.current);
IntersectionObserver에는 파라미터로 특정 스크롤 시점에 실행 할 함수와 Observer 세팅 값들을 넘겨 줍니다.
나는 아직도 hooks가 넘 어렵다..
그래도 윈도우에 리스너를 달아서 스크롤된 위치를 구하고.. 특정 위치가 될 때마다 각 element의 css를 조절해주려고 했던 나의 초기 생각보다는 IntersectionObserver 와 hook을 사용한 위의 방법이
편리함이나 코드 재사용성면에서 더 좋은 것 같다.
또한 display 속성이 아닌 opacity를 이용해서 element를 숨기는 방식과, 윈도우에서 스크롤 값을 구하는 것이 아니라 threshold 를 이용하여 이벤트를 발생시킨다는 방식도 새로웠던 것 같다.
끗