화면에 들어왔을 때 애니메이션 동작하기 | react | IntersectionObserver

고광필·2022년 7월 9일
1

Front

목록 보기
20/33
post-thumbnail
post-custom-banner

모 기업의 채용 과제에 사용했던 화면에 들어왔을 때 애니메이션 동작을 실행하도록 하는 기능을 포스팅하겠습니다

기본 애니메이션

import React from 'react'
import styled from '@emotion/styled'

const ContentDiv = styled.div`
  height: 200px;
  font-size: 36px;
  margin: 20px;
  display: flex;
  align-items: center;
  justify-content: center;

  animation-name: opacity;
  animation-duration: 5000ms;

  @keyframes opacity {
    from {
      opacity: 0;
    }
    to {
      opacity: 1;
    }
  }
`

const App = () => {
  return <ContentDiv>글자 등장!</ContentDiv>
}

export default App

기본적인 애니메이션 코드입니다
새로고침하면 나타난것처럼 동작합니다

여러 사이트에서 스크롤을 내려서 화면에 잡혔을 때 슝~ 하고 움직이는 애니메이션을 본적이 있으신가요?

위 코드의 애니메이션을 화면에 잡혔을 때 동작하도록 바꾸겠습니다

useIntserctionObserver

사용자에게 많은 데이터를 보여주는 방법이라는 지난 글에서 IntersectionObserver를 다룬적이 있습니다
특정 DOM이 사용자가 보는 화면 (viewport)에 들어왔을 때 감지할 수 있습니다

useIntersectionObserver라는 훅을 만들어서 사용해보겠습니다

import { useEffect, useRef, useState } from 'react'
import type { RefObject } from 'react'

const useIntersectionObsever = (targetRef: RefObject<HTMLDivElement>) => {
  const [isInViewport, setIsInViewport] = useState(false)
  const observer = useRef<IntersectionObserver>()

  useEffect(() => {
    if (!observer.current) {
      const observerCallback = (entries: IntersectionObserverEntry[]) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            setIsInViewport(true)
          }
        })
      }

      observer.current = new window.IntersectionObserver(observerCallback, {
        threshold: 0
      })
    }

    if (targetRef.current) {
      observer.current.observe(targetRef.current)
    }

    return () => {
      if (observer.current) {
        observer.current.disconnect()
      }
    }
  }, [targetRef])

  return isInViewport
}

export default useIntersectionObsever

dom에 접근하는 ref를 인자로 받아서, state를 반환하는 훅입니다
observer에 잡히면 (viewport에 들어왔으면) state를 true로 만듭니다

import React, { useRef } from 'react'
import styled from '@emotion/styled'
import useIntersectionObsever from './hooks/useIntersectionObsever'

const ContentDiv = styled.div`
  height: 200px;
  font-size: 36px;
  margin: 20px;
  display: flex;
  align-items: center;
  justify-content: center;

  &.animation {
    animation-name: opacity;
    animation-duration: 5000ms;

    @keyframes opacity {
      from {
        opacity: 0;
      }
      to {
        opacity: 1;
      }
    }
  }
`

const App = () => {
  const ref = useRef<HTMLDivElement>(null)
  const isInViewport = useIntersectionObsever(ref)

  return (
    <>
      <div style={{ height: '2000px' }} />
      <ContentDiv ref={ref} className={isInViewport ? 'animation' : ''}>
        글자 등장!
      </ContentDiv>
    </>
  )
}

export default App

App.tsx는 useObserver 훅으로 반환받은 값이 true면 클래스를 추가합니다
해당 클래스가 있을 때 애니메이션이 동작하도록 style을 수정합니다

스크롤을 내리면 클래스가 수정되고, 애니메이션이 동작합니다

profile
이해하는 개발자를 희망하는 고광필입니다.
post-custom-banner

0개의 댓글