-
React로 Scroll Reveal Animation 구현하고 컴포넌트화 하기React 2024. 4. 30. 14:36
샤랄라 애니메이션 저번 게시물에서 Scroll Reveal Animation을 React/Next.js로 구현하기 앞서 동작 원리를 알아보기 위해 JavaScript로 구현해 보았습니다. 이번에는 실제 프로젝트에 적용하기 위해 React로 구현하고 컴포넌트화 했습니다!
JavaScript로 Scroll Reveal Animation 구현하기
Next.js 로 프로젝트 중 토스 홈페이지를 보고 스크롤 이벤트 시 요소들이 여러 방향으로 나타나는 애니메이션을 프로젝트 소개 페이지(Intro Page)에 적용하고 싶었습니다. 그래서 React로 만들기에
dev-ea-jung.tistory.com
✅ 구현하고 싶은 내용
props로 animation direction을 받을 수 있는 <ScrollRevealAnimationWrapper /> 컴포넌트를 만들려고 합니다.
애니메이션을 적용하고 싶은 요소가 있으면 감싸주고 animation direction 전달해주면 원하는 방향으로 이동하는 애니메이션이 적용됩니다.
✅ 구현 과정
우선 저번 게시물에서 만들었던 뷰포트 안에 요소가 들어왔는지 검사하는 함수를 checkIsInViewport() 훅으로, 스크롤 이벤트를 감지하는 함수를 useWindowScrollEvent() 훅으로 뺐습니다.
export const checkIsInViewport = (elem: DOMRect | null): boolean => { if (!elem || !window) { return false } const elementTop = elem.top const elementBottom = elem.bottom return elementBottom > 0 && elementTop <= window.innerHeight }
import { useEffect } from 'react' export const useWindowScrollEvent = (listener: () => void): void => { useEffect(() => { window.addEventListener('scroll', listener) return () => { window.removeEventListener('scroll', listener) } }, []) }
저는 <ScrollRevealAnimationWrapper />을 CSS module 방법을 이용하여 만들어 주었어요.
작동방식은 다음과 같습니다.
- useWindowScrollEvent() 훅이 스크롤 이벤트를 감지하여 이벤트 리스너를 작동시킵니다.
- 스크롤 이벤트가 동작하는 동안 애니메이션 이벤트를 적용하고 싶은 요소가 뷰포트에 보이면 이를 checkIsInViewport()가 감지합니다.
- 원하는 요소가 뷰포트 상으로 들어오면 checkIsInViewport()는 true를 반환하고, animation staete 또한 true로 전환됩니다.
- animation가 true가 되면(&&) style[animationDirection + 'Animation'] 스타일이 적용되면서 애니메이션 스타일이 추가됩니다.
- 선택한 animation direction(top, bottom, right, left)에 맞게 해당 요소에 애니메이션이 적용됩니다.
// ScrollRevealAnimationWrapper.tsx import React, { useRef, useState } from 'react' import { checkIsInViewport } from '../_hooks/checkIsInViewport' import { useWindowScrollEvent } from '../_hooks/useWindowScrollEvent' import style from './ScrollRevealAnimWrapper.module.css' type props = { children: React.ReactNode animationDirection: 'top' | 'bottom' | 'right' | 'left' } const ScrollRevealAnimationWrapper: React.FC<props> = ({ animationDirection, children, }) => { const [animation, setAnimation] = useState(true) const areaRef = useRef<HTMLDivElement>(null) const handleScrollAnimation = () => { const element = areaRef.current?.getBoundingClientRect() if (element) { console.log(checkIsInViewport(element)) setAnimation(checkIsInViewport(element)) } } useWindowScrollEvent(handleScrollAnimation) return ( <div className={`${style.container} ${ animation && style[animationDirection + 'Animation'] }`} > <div ref={areaRef}>{children}</div> </div> ) } export default ScrollRevealAnimationWrapper
// ScrollRevealAnimWrapper.module.css .container { opacity: 0; } .topAnimation { animation: top 2s both; } .bottomAnimation { animation: bottom 2s both; } .rightAnimation { animation: right 2s both; } .leftAnimation { animation: left 2s both; } @keyframes top { from { transform: translateY(5rem); opacity: 0; } to { transform: translateY(0); opacity: 1; } } @keyframes bottom { from { transform: translateY(-5rem); opacity: 0; } to { transform: translateY(0); opacity: 1; } } @keyframes right { from { transform: translateX(-5rem); opacity: 0; } to { transform: translateX(0); opacity: 1; } } @keyframes left { from { transform: translateX(5rem); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
✅ 결과물
✅ 추가 할 만한 내용 🧐
우선 기능 구현을 중심으로 코드를 짰지만 콘솔 탭을 보면 스크롤 이벤트에 대한 이벤트 리스너가 불필요하게 많이 호출되는 것을 알 수 있었습니다.
그래서 쓰로틀링을 적용해서 최적화해 보면 좋을 것 같다고 생각했습니다!
쓰로틀링 : 짧은 시간 동안 연속해서 발생한 이벤트들을 일정 시간 단위(delay)로 그룹화하여, 처음 또는 마지막 이벤트 핸들러만 호출하도록 하는 것
반응형'React' 카테고리의 다른 글
[React] useState() 훅과 클로저 (0) 2024.05.06 JSX는 어떻게 자바스크립트에서 변환될까? (0) 2024.04.23 React는 왜 등장하게 되었을까? (0) 2023.12.22 브라우저 렌더링 과정을 이해하려면 DOM을 알아야 한다? (0) 2023.12.20 메모이제이션을 이용해서 렌더링 성능을 개선할 수 있을까? (0) 2023.12.19