프로그래밍 공부/React

[React] Greensock을 사용한 애니메이션

Kevinkb 2022. 1. 14. 05:30

greensock을 사용하는 이유?

스크롤 기반 애니메이션은 기본적인 CSS와 JS로 수행 가능해 애니메이션 라이브러리를 사용하는 것이 낭비라고 생각될 수 있습니다. 하지만 greensock을 사용하면 브라우저 버그나 불일치 등의 호환성 문제를 해결하고 애니메이션이 모듈화되어 코드의 가독성, 재사용성을 높일 수 있습니다. 또한, 스크롤 위치에 기반한 애니메이션은 성능상의 이슈가 있기 때문에 스크롤 이벤트가 너무 자주 발생하지 않도록 방지해야 하는데 매번 스크롤 리스너를 조절하는 것은 상당히 번거롭습니다. 그렇기 때문에 호환성, 사용성이 좋고 가볍게 최적화가 되어 있는 greensock 라이브러리를 사용했습니다.

라이브러리 설치

npm install gsap

import

gsapScrollTrigger를 임포트 해줬습니다. ScrollTrigger는 스크롤 기반 애니메이션을 위해 사용합니다. 일반적인 애니메이션은 gsap만으로 충분합니다.

import gsap, { ScrollTrigger } from 'gsap/all';

DOM 요소 지정

GSAP 애니메이션을 사용하기 위해서는 DOM 요소에 접근할 수 있어야 합니다. 리액트 컴포넌트에서 DOM 요소에 접근하기 위해 useRef를 사용해 DOM 요소를 참조하는 변수를 생성합니다.

const Greensock = () => {
  const box1 = useRef();

  return <img src={img1} alt="img1" ref={box1} />;
};

애니메이션 적용 및 CleanUp

GSAP 애니메이션을 적용하기 위해서 DOM 요소가 지정 되고 DOM이 랜더링 되어야 하기 때문에 리액트 컴포넌트에서는 랜더링 후 실행되는 useEffect의 콜백 함수 안에서 애니메이션 적용이 이뤄져야 하고 CleanUp 함수에서 gsap 애니메이션을 삭제하여 메모리 누수를 방지합니다.

const Greensock = () => {
  const box1 = useRef();

  useEffect(() => {
    let animation = gsap.to(box1.current, { rotation: "+=360" });

    return () => {
        animation.kill();
    }
  }, [])

  return <img src={img1} alt="img1" ref={box1} />;
};

스크롤 트리거

스크롤 트리거는 다음과 같이 적용할 수 있습니다. 스크롤 트리거를 적용하고 markers 속성 값을 true로 설정하면 위의 스크린샷과 같이 인디케이터를 표시해 애니메이션의 타이밍을 시각적으로 보다 예측하기 편하게 작업 할 수 있습니다. 작업 후 해당 속성은 삭제합니다.

// 스크롤 트리거 등록
gsap.registerPlugin(ScrollTrigger);

gsap.to(box.current, {
  // 스크롤 트리거 적용
  scrollTrigger: {
    trigger: box.current,
    toggleActions: 'play reset play reset',
    markers: true,
    start: 'top center',
    end: 'bottom center',
  },
  x: 500,
});

toggleActions

toggleActions은 "onEnter, onLeave, onEnterBack, onLeaveBack" 네 가지로 구성됩니다. 스크롤과 요소의 위치에 기반해 조건에 맞는 액션이 실행되는 것 입니다.

onEnterscroller-start 인디케이터가 start를 지나 startend 사이에 존재할 때,
onLeavescroller-end 인디케이터가 end를 지나 startend 사이를 벗어났을 때,
onEnterBackscroller-start 인디케이터가 start를 지나 startend 사이를 벗어났을 때,
onLeaveBackscroller-end 인디케이터가 end를 지나 startend 사이에 존재할 때를 뜻 합니다.

액션 키워드는 다음과 같습니다.
"play", "pause", "resume", "reset", "restart", "complete", "reverse", and "none".

설명을 위한 예시 코드입니다.

import React, { useEffect, useRef } from 'react';
import img1 from '../../images/img1.png';
import img2 from '../../images/img2.png';
import img3 from '../../images/img3.png';
import img4 from '../../images/img4.png';
import gsap, { ScrollTrigger } from 'gsap/all';
import styled from 'styled-components';

const Wrapper = styled.div`
  display: grid;
  padding: 100px 0;
`;

const Img = styled.img`
  margin-bottom: 100px;
  transition: 0.5s linear;
`;

const Greensock = () => {
  const box1 = useRef();
  const box2 = useRef();
  const box3 = useRef();
  const box4 = useRef();

  useEffect(() => {
    const boxes = [box1, box2, box3, box4];
    const animations = new Array(4);
    // 스크롤 트리거 등록
    gsap.registerPlugin(ScrollTrigger);

    boxes.forEach((box, idx) => {
      animations[idx] = gsap.to(box.current, {
        // 스크롤 트리거 적용
        scrollTrigger: {
          trigger: box.current,
          toggleActions: 'play reset play reset',
          markers: true,
          start: 'top center',
          end: 'bottom center',
        },
        x: 500,
      });
    });

    return () => {
      animations.forEach((animation) => animation.scrollTrigger.kill());
    };
  }, []);

  return (
    <Wrapper>
      <Img src={img1} alt="img1" ref={box1} />;
      <Img src={img2} alt="img2" ref={box2} />;
      <Img src={img3} alt="img3" ref={box3} />;
      <Img src={img4} alt="img4" ref={box4} />;
    </Wrapper>
  );
};

export default Greensock;

모든 애니메이션과 기능을 설명하기엔 GSAP의 기능이 정말 많기 때문에 기본적으로 리액트에 어떻게 적용하는지가 중요합니다. 기능 자체는 markes와 같은 속성이 있기 때문에 뷰를 직접 보면서 테스트하다 보니 점점 감을 잡아가는 것을 느낄 수 있었다.


참고자료

GSAP + React, First Steps & Handy Techniques.
ScrollTrigger 공식문서
[GSAP] 애니메이션 사용법 (기초)
[GSAP] 애니메이션 사용법 (중급 - ScrollTrigger)