[R3F] React Three Fiber+GSAP으로 오브젝트 클릭 시 카메라 이동하는 애니메이션 구현하기

loopydoopy·2023년 10월 21일
3
post-thumbnail

3D 기반의 웹을 개발하다보면 오보젝트 클릭 시 카메라가 이동하고 해당 오브젝트에 포커싱되는 기능을 한번쯤 구현하게 된다.
이때 GSAP 라이브러리를 사용하면 쉽게 구현할 수 있다.

GSAP?

GSAP(The GreenSock Animation Platform)는 타임라인 기반의 애니메이션 자바스크립트 라이브러리이다.
css와 바닐라 자바스크립트의 keyframe과 animation보다 세밀한 컨트롤이 가능하다는 장점이 있기 때문에 2d, 3d 인터랙티브 웹 사이트를 제작할 때 쉽게 사용할 수 있다.

gsap.to()

애니메이션 적용을 위해 gsap의 여러 메소드를 사용할 수 있고 아래 공식 문서에서 확인할 수 있다.
https://gsap.com/resources/get-started#creating-an-animation

"현재 상태"를 기준으로 어떤 상태로 애니메이션을 적용하는 경우 일반적으로 gsap.to() 메소드를 사용한다.
모델을 띄워놓은 상태에서 클릭하면 카메라 위치만 움직일 것이기 때문에 gsap.to() 메소드를 사용했다.


구현

우선 position, target 값을 초기에 설정해놓는다.
CameraControls 내에서 OrbitControls refpositiontarget값을 업데이트해주면서 애니메이션이 발생하는 방식이다.
추가한 3D 모델 에셋과 초기 카메라 세팅값에 맞게 애니메이션 적용 후 설정할 position, target값을 적절히 설정한다.
코드는 아래와 같고 duration을 통해 애니메이션 발생 속도를 설정하고 ease의 종류는 공식 문서에서 확인하여 설정할 수 있다.

View.jsx

import { Canvas } from '@react-three/fiber'
import React, { useState } from 'react'
import { OrbitControls } from '@react-three/drei'
import { House } from './House'
import { CameraControls } from './Camera'

export default function View() {
  const [position, setPosition] = useState({ x: 25, y: 4, z: -8})
  const [target, setTarget] = useState({x: 0, y: 0, z: 0})

  const handleOnclick = () => {
    setPosition({x: 5, y: 2, z: 0})
    setTarget({x: 3, y: 2, z: 0 })
  }

  return (
    <>
      <Canvas camera={{position:[25,4,-8]}}>
        <axesHelper args={[200, 200, 200]} />
        <ambientLight intensity={1} />
        <CameraControls position={position} target={target}/>
        <group rotation-y={-Math.PI / 2} onClick={handleOnclick}>
          <House/>
        </group>
      </Canvas>
    </>
  )
}

CameraControls.jsx

  import { OrbitControls } from "@react-three/drei";
  import { useThree } from "@react-three/fiber";
  import { useEffect, useRef } from "react";
  import gsap from 'gsap'

  
   const CameraControls = ({position, target}) => {
     const { camera } = useThree();
     const ref = useRef(null);

     function cameraAnimate() {
      if (ref.current) {
        gsap.timeline().to(
          camera.position, 
          {
            duration: 2,
            repeat: 0,
            x: position.x,
            y: position.y,
            z: position.z,
            ease: "power3.inOut",
        });

        gsap.timeline().to(
          ref.current.target,
          {
            duration: 2,
            repeat: 0,
            x: target.x,
            y: target.y,
            z: target.z,
            ease: "power3.inOut",
          }
        );
      }
    }

    useEffect(() => {
      cameraAnimate();
    }, [target, position]);

     return (
       <OrbitControls ref={ref} />
     );
   };

  export { CameraControls };

결과 화면


추가

해당 포스트에서는 현재 상태에서 카메라 이동 애니메이션을 한번만 적용시켰기 때문에 간단하게 구현했지만,
클릭 시 애니메이션을 적용할 오브젝트가 여러 개인 경우라면 인덱스 값마다 positiontarget 좌표를 미리 설정해놓은 후, handleOnClick 함수에 각 오브젝트를 식별할 수 있는 인덱스 값을 넘겨주고 CameraControls에서 애니메이션이 발생하도록 응용할 수 있다.

추가로, 포커싱 이후 다시 클릭했을 때 기존 상태로 positiontarget좌표를 설정하고 싶다면?
클릭했던 오브젝트의 인덱스와 새로 클릭하는 오브젝트의 인덱스가 같은지를 판단하고 같은 경우 이 함수를 호출하는 방식으로 응용할 수 있다.
원상태의 카메라 positintarget 좌표가 공통으로 쓰인다면setCamera와 같은 이름의 함수를 추가하여 필요할 때마다 setCamera 함수를 호출하는 방식으로 구현할 수 있다.


참고자료

Camera Animation using React Three Fiber and GSAP
https://vaisakhnp.hashnode.dev/camera-animation-using-react-three-fiber-and-gsap#heading-adding-animation-to-scene

1개의 댓글

comment-user-thumbnail
2024년 9월 21일

오 JS에도 animation tweening 라이브러리가 있군요!
이걸 three.js에 사용하는 건 신기하네요!

답글 달기