[CSS] 화면에 컴포넌트가 나타났을 때 실행되는 애니메이션 custom hook으로 만들기

2024. 3. 22. 18:24
반응형

세로로 긴 스크롤을 쭉쭉 내리다가, 특정 컴포넌트가 mount 됐을 때 애니메이션이 실행되도록 해야한다.

같은 방식으로 동작하는 컴포넌트들이 여기저기 여러번 쓰여서, 커스텀 훅으로 만들어 쓰기로 했다.

 

(Next.js14 & Styled-components 사용 환경이다.)

 

 

Custom hook

// useScrollAnimation.tsx


'use client';

import { useEffect, useState, RefObject } from 'react';

const useScrollAnimation = (ref: RefObject<HTMLElement>) => {
  const [isInViewport, setIsInViewport] = useState(false);

  useEffect(() => {
    if (!ref.current) return;

    const callback = (entries: IntersectionObserverEntry[]) => {
      entries.forEach((entry) => {
        // 요소가 뷰포트에 나타났을 때
        if (entry.isIntersecting) {
          setIsInViewport(true);
        }
        // 요소가 뷰포트를 벗어났을 때
        else {
          setIsInViewport(false);
        }
      });
    };

    const options = { root: null, rootMargin: '0px', threshold: 0 };
    const observer = new IntersectionObserver(callback, options);
    observer.observe(ref.current); // 요소 관찰 시작

    console.log(isInViewport);

    return () => {
      observer.disconnect(); // 컴포넌트 언마운트시 관찰 중단
    };
  }, []);

  return { isInViewport };
};

export default useScrollAnimation;

 

 

(참고: 키프레임 모음집)

// keyframes.ts

import { keyframes } from 'styled-components';

export const Floating = keyframes`
  0% { transform: translateY(0);}
  50% { transform: translateY(-25px);}
  100% { transform: translateY(0);}
`;

export const FadeInUp = keyframes`
  0% { opacity: 0; transform: translate3d(0, 75%, 0);}
  to { opacity: 1; transform: translateZ(0);}
`;

 

 

위에서 만든 커스텀훅을 가져다가 사용하는 부분

import styled from 'styled-components;
import useScrollAnimation from 'hooks/useScrollAnimation';
import { FadeInUp } from 'styles/keyframes';
import { useRef } from 'react';


const MyComponent = () => {
  const ref = useRef<HTMLDivElement | null>(null);
  const { isInViewport } = useScrollAnimation(ref);
  
  ...
  
  return (
  	<div ref={ref}>이 컴포넌트가 화면에 나타나는게 애니메이션 시작하라는 신호!
	  <Target>애니메이션이 적용될 컴포넌트</Target>
    </div>
  )
}


...

const Target = styled.div<{$isAnimate: boolean}>`
  ${(props) => props.$isAnimate &&
  	css ` animation: ${FadeInUp} 1s`
  }
`
반응형

BELATED ARTICLES

more