개발/React : React Three Fiber

【React Three Fiber】#03 애니메이션 구현하기

고독한 쵸이 2022. 9. 29. 00:02
반응형

이번 글에서는 React Three Fiber를 이용해 간단한 애니메이션을 구현해보자.

 

이전 글은 아래를 참고해주세요.

 

【React Three Fiber】#02 오브젝트 변형하기

이전 글은 아래를 참고해주세요. 【React Three Fiber】#01 시작하기(초기 설정, 정육면체 렌더링) 【React Three Fiber】#01 시작하기(초기 설정, 정육면체 렌더링) 현재 일본 파견회사에서 3년 이상(4년 차)

solitary-choi.tistory.com

2편과 마찬가지로, 이번에도 1편에서 작성했던 Basic.tsx을 복사해 컴포넌트명만 변경해준다.

 

Animation.tsx

/* eslint-disable react/no-unknown-property */

import { Canvas } from "@react-three/fiber";
import React from "react";
import classes from "../styles/Global.module.css";

const Cube = () => {
  const object = (
    <mesh>
      <boxGeometry args={[1, 1, 1]} />
      <meshBasicMaterial color="#e91e63" />
    </mesh>
  );
  return object;
};

const Animation = () => {
  return (
    <div className={classes.canvas}>
      <Canvas camera={{ position: [1, 1, 3] }}>
        <Cube />
      </Canvas>
    </div>
  );
};

export default Animation;

App.tsx

import Animation from "./components/Animation";
import React from "react";

function App() {
  return <Animation />;
}

export default App;

 

useFrame()

1 프레임마다 콜백 함수를 실행시키는 React Three Fiber 커스텀 훅이다. 

여기서의 1 프레임은 60 fps이며, 이는 1초당 60회 콜백 함수를 실행시킨다는 것을 의미한다.

 

Position

그러면 useFrame을 사용해, 우선은 오브젝트를 오른쪽으로 움직이게 해 보자.

2편에서 언급했듯, mesh의 속성을 수정하고 싶다면 useRef를 사용하면 된다.

import { Canvas, useFrame } from "@react-three/fiber";
import React, { useRef } from "react";

const Cube = () => {
  const cubeRef = useRef<THREE.Mesh>(null);

  useFrame((state) => {
    if (!cubeRef.current) {
      return;
    }
    cubeRef.current.position.x += 0.01;
  });

  const object = (
    <mesh ref={cubeRef}>
      <boxGeometry args={[1, 1, 1]} />
      <meshBasicMaterial color="#e91e63" />
    </mesh>
  );
  return object;
};

위의 코드를 실행하면 아래와 같이 오른쪽으로 오브젝트가 이동하는 것을 확인할 수 있다.

오브젝트가 오른쪽으로 이동

getElapsedTime()

이번에는 경과시간을 이용해서 오브젝트를 움직여보자.

useFrame의 state에 내장되어 있는 clock의 getElapsedTime()을 사용하면 clock이 생성되고 나서 경과된 시간을 구할 수 있다.

 useFrame((state) => {
    if (!cubeRef.current) {
      return;
    }
    const elapsedTime = state.clock.getElapsedTime();
    cubeRef.current.position.y = elapsedTime;
  });

위의 코드를 실행해 오브젝트가 위로 이동하는 것을 확인해보자.

오브젝트가 위로 이동

sin(x)나 cos(x)를 사용함으로써 오브젝트가 상하로 움직임을 반복하게도 할 수 있다. 

 

sin(x)

0(중간지점)에서부터 시작해, 상하로 반복된다.

sinx 그래프
출처: 구글 검색

코드로 작성하면 이런 느낌이다.

cubeRef.current.position.y = Math.sin(elapsedTime);

다음과 같이 오브젝트가 중간지점에서부터 시작해 상하로 움직임을 반복하는 것을 볼 수 있다.

오브젝트가 상하로 움직임 반복

cos(x)

1(최상단)에서부터 시작해, 상하로 반복된다.

cosx 그래프
출처: 구글 검색

아래 코드를 실행하면, 오브젝트가 오른쪽에서부터 시작해 좌우로 반복해서 움직이게 된다.

cubeRef.current.position.x = Math.cos(elapsedTime);

 

오브젝트가 좌우로 움직임 반복

참고로, ref의 position속성을 변경하는 것이 아닌, state에 내장되어 있는 카메라의 속성을 변경해도 거의 흡사한 애니메이션 효과를 줄 수 있다.

아래의 코드를 실행하면 정육면체가 원을 그리며 회전하게 된다.

state.camera.position.y = Math.sin(elapsedTime);
state.camera.position.x = Math.cos(elapsedTime);

오브젝트 회전

camera.lookAt()

원점 좌표(0, 0, 0)를 지정한다.

위의 코드와 함께 사용하여, 지정한 오브젝트를 중심으로 카메라를 회전시킬 수 있다.

state.camera.lookAt(cubeRef.current.position);

오브젝트 회전
아까보다 덜 정신사납고 여유로운 움직임에 마음이 안정된다.

 

Rotation

이때까지 position을 변경해서 오브젝트를 움직이게 해 봤는데, rotation을 변경해 회전시키는 것도 가능하다.

방법은 position과 흡사하게, useFrame에서 ref의 rotation속성을 변경하면 된다.

cubeRef.current.rotation.y += 0.01;

그러면 아래와 같이 오브젝트가 빙글빙글 돌아가는 것을 확인할 수 있다.

느릿느릿 돌아가는 정육면체가 왠지 모를 안정감을 준다.

오브젝트 회전

아까 정의한 elapsedTime과 라디안을 이용해 엄청난 스피드로 회전하게 할 수도 있다. (솔직히 정신사납다.)

cubeRef.current.rotation.y = elapsedTime * Math.PI * 2;

오브젝트 회전
웹에서 보면 이 정도로 삐걱대지는 않는데, 캡쳐를 하니 회전하는 게 아니라 2D도형이 변화하는 것 같이 보인다.

 

gsap

GreenSock에서 제작한 자바스크립트 애니메이션 라이브러리라고 한다.

얼핏 봐서는 three.js뿐만 아니라 2D에서도 사용할 수 있는 듯하다.

다음 커맨드를 실행해 gsap를 설치해주자.

npm install --save gsap

그리고 다음 코드를 작성해주자.

이번에는 애니메이션을 한 번만 실행할 예정이니, useFrame대신 useEffect를 사용한다.

아까 사용했던 useFrame은 주석 처리를 하거나 지워도 상관없다.

 

gsap.to()

목적지의 상태를 지정한다.

다음 코드의 경우, 1초 뒤 1초 동안 오른쪽으로 2만큼 이동한 후, 2초 뒤 1초 동안 원점으로 돌아오게 한다는 의미이다.

const Cube = () => {
  const cubeRef = useRef<THREE.Mesh>(null);

  useEffect(() => {
    if (!cubeRef.current) {
      return;
    }
    gsap.to(cubeRef.current.position, { duration: 1, delay: 1, x: 2 });
    gsap.to(cubeRef.current.position, { duration: 1, delay: 2, x: 0 });
  }, []);

  const object = (
    <mesh ref={cubeRef}>
      <boxGeometry args={[1, 1, 1]} />
      <meshBasicMaterial color="#e91e63" />
    </mesh>
  );
  return object;
};

오브젝트 이동

 

 

つづく

반응형