[스크롤 이벤트 페이지 제작하기]

JAMEe_·2024년 7월 1일

R3F

목록 보기
5/24

ScrollControls

drei 에서 제공해주는 스크롤 컨트롤

pages: 꽉차는 슬라이드의 페이지 갯수
damping: 스무스한 이동 효과 정도

<ScrollControls pages={8} damping={0.25}>
   <Dancer />
</ScrollControls>

감싸진 Dancer 컴포넌트 안에서 useScroll() 선언하여 스크롤 정보를 불러올 수 있다

GLB 로딩 처리

glb 모델을 불러오는동안의 로딩 작업
drei 에서 제공해주는 useProgress 또는 Loader 컴포넌트 사용

1️⃣ useProgress
비동기로 모델을 불러오니 Suspense 의 fallback 에서 로딩처리
useProgress 통해서 현재 진행 상태 확인
drei 에서 제공해주는 Html 컴포넌트를 사용하면 canvas 안에서도 html 을 불러올 수 있다

2️⃣ Loader
Canvas 컴포넌트 밖에서 선언되어야 하며
glb 모델을 불러올 컴포넌트는 Suspense fallback={null} 로 감싸져야 한다

  <Canvas
   ...
  >
    ...
    <Suspense fallback={null}>
      <TestMesh />
    </Suspense>
  </Canvas>
  <Loader />

gsap

애니메이션 라이브러리

간단한 사용방법

// 처음 위치에서 ~ 의 위치로 이동하라
gsap.to(".box",{ rotation: 27, x: 100, duration: 1})
// ~ 에서 처음 위치로 이동하라
gsap.from(".class",{ opacity: 0, y: 100, duration: 1})

간단한 카메라 무빙 애니메이션

const three = useThree();
// 2.5 초 동안 [-5,5,5] 에서 [0,6,12] 로 카메라 이동
gsap.fromTo(
    three.camera.position,
    {
      x: -5,
      y: 5,
      z: 5,
    },
    {
      duration: 2.5,
      x: 0,
      y: 6,
      z: 12,
    }
);
// 카메라가 180도에서 0도로 회전
// camera Controls 컴포넌트가 없어야함 e.g. OrbitControls
gsap.fromTo(
    three.camera.rotation,
    {
      z: Math.PI,
    },
    {
      duration: 2.5,
      z: 0,
    }
);

timeline: 애니메이션을 시간 순서에 따라 제어하고싶을때 사용

timeline = gsap.timeline();
timeline.from(
  dancerRef.current.rotation,
  {
    duration: 4,
    y: -4 * Math.PI,
  },
  // delay (number or string)
  // 선언되어 있지 않다면 앞선 애니메이션 끝나고 난 후 실행
  // "<" 는 앞선 애니메이션 delay 와 동일하게 실행해라 의미
  1
);

위 코드는 1초 후 4초동안 -720도를 도는 애니메이션이다.
timeline.seek(★시간) 은 timeline 의 애니메이션의 총 시간인 duration + delay 를 합친 시간 사이의 (★시간) 으로 위치한다.
즉, ★시간이 2.5 초라면 총 5초의 애니메이션에서 2.5 초에 해당하는 애니메이션 프레임 위치로 이동한다
이 방법을 이용해서 스크롤 시 glb 모델이 도는 동작을 할 수 있다.

useFrame(() => {
  // 만약 스크롤을 절반까지 한 경우
  // 0.5 * 5초 인 2.5초의 애니메이션 프레임 위치로 이동
    timeline.seek(scroll.offset * timeline.duration());
  });

※ 하나의 timeline 에 .from .from 계속 이어나가면서 선언할 수 있고, 이어진 duration 과 delay 를 모두 합한 값이 timeline.duration()

별 생성하기

const { positions } = useMemo(() => {
   const count = 500;
   // new Float32Array([1,2,3]) => 3 * 4 = 12 Bytes
   const positions = new Float32Array(count * 3);
   for (let i = 0; i < count * 3; i++) {
     // -12.5 ~ 12.5 범위
     positions[i] = (Math.random() - 0.5) * 25;
   }

   return { positions };
}, []);

<Points positions={positions.slice(0, positions.length / 3)}>
   <pointsMaterial
   size={0.5}
   color={new THREE.Color("#DC4F00")}
   // 원근에 따라 크기를 조정하고 싶을 때
   sizeAttenuation
   // 앞에 있는 별이 뒤에 별을 가리고 싶을 때
   depthWrite
   alphaMap={texture}
   transparent
   alphaTest={0.001}
   />
</Points>

※ 검정색은 transparent 선언 시 항상 투명하게 보임.
alphaTest 를 선언하면 투명하게 보일 알파의 범위를 설정할 수 있음.
e.g. 분홍색은 alphaTest={0.7} 이상에서 투명하게 보임.

특정 그룹을 도는 카메라 생성하기

지구가 태양 주위를 도는것처럼 댄서를 중심으로 공전하는 카메라 효과를 주고 싶을 때 사용
댄서 position을 copy해서 group 에 넣어주고 그 group 을 중심으로 카메라가 도는것
공전이 끝나면 메인 카메라는 자연스럽게 종료된 지점에 위치

const pivot = new THREE.Group();
pivot.position.copy(dancerRef.current.position);
pivot.add(three.camera);
three.scene.add(pivot);

...
timeline.to(pivot.rotation, {
    duration: 10,
    y: Math.PI,
    onUpdate: () => {
      // 자기 애니메이션 프레임에 도달했을 때
      console.log("doing");
    },
})

// 종료 시 항상 remove 해주어야함
return ()=>{
    three.scene.remove(pivot)
}

스크롤시 텍스트 이벤트


useFrame(()=>{
  // 총 8 페이지
  
  // scroll.range ( 0 ~ 1 )
  // 두번째 매개변수는 distance. 이동하는 거리의 양
  article01Ref.current.style.opacity = `${1 - scroll.range(0, 1 / 8)}`;
  
  // scroll.curve ( 0 - 1 - 0 )
  // 처음과 끝 0 리턴 중간은 1 리턴
  // 2/8 지점에서 1/8 만큼 이동하겠다 의미
  article03Ref.current.style.opacity = `${scroll.curve(2 / 8, 1 / 8)}`;
  
  // 현재 viewport 가 4/8 에서 + 3/8 까지의 범위 내에 있는지
  if (scroll.visible(4 / 8, 3 / 8)) {
     ...
  }
})

// 기본적으로 슬라이드 애니메이션 효과 적용되어 있음
<Scroll html>
  ...
</Scroll>

오디오 배치하기

<PositionalAudio
  position={[-24, 0, 0]}
  autoplay
  url="/audio/bgm.mp3"
  distance={50}
  loop
/>
profile
안녕하세요

0개의 댓글