+3

Làm việc Modal 3D với Threejs trong reactjs bằng thư viện react-three-fiber

Mayfest2023

Hiển thị một file .jbx (modal 3d) sử dụng thư viện Three.js trong ReactJS

Để hiển thị một file .jbx (modal 3d) sử dụng thư viện Three.js trong ReactJS, chúng ta có thể tạo một React component và sử dụng thư viện three.js để tạo ra một scene 3D để hiển thị modal.

Đầu tiên, cài đặt thư viện three.js và thư viện react-three-fiber bằng cách chạy lệnh sau trong terminal:

npm install three react-three-fiber

Sau đó, tạo một component React với tên là "ThreeJSModal" như sau:

import React, { useRef, useState } from "react";
import { useLoader } from "react-three-fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

const ThreeJSModal = () => {
  const [modalLoaded, setModalLoaded] = useState(false);
  const modalRef = useRef();

  // Sử dụng useLoader để load file .jbx
  const gltf = useLoader(GLTFLoader, "/path/to/your/modal.jbx");

  // Callback khi file đã được load
  const onModalLoaded = () => {
    setModalLoaded(true);
  };

  return (
    <>
      {modalLoaded && (
        <>
          {/* Sử dụng OrbitControls để có thể quay vật thể */}
          <OrbitControls
            enableDamping
            dampingFactor={0.5}
            enableZoom={false}
            ref={modalRef}
          />
          {/* Hiển thị vật thể */}
          <primitive object={gltf.scene} onReady={onModalLoaded} />
        </>
      )}
    </>
  );
};

export default ThreeJSModal;

Trong đoạn code trên, chúng ta sử dụng useLoader để load file .jbx bằng GLTFLoader và useRef để lưu tham chiếu đến OrbitControls. OrbitControls cho phép người dùng xoay vật thể để xem nó từ nhiều góc độ khác nhau. Chúng ta sử dụng useState để theo dõi khi file đã được load và chỉ hiển thị vật thể khi file đã được load hoàn tất. Cuối cùng, chúng ta sử dụng primitive để hiển thị vật thể và onReady để đánh dấu khi file đã được load.

Sau khi đã tạo xong component, bạn có thể sử dụng nó trong ứng dụng React của mình bằng cách import và đưa nó vào trong DOM.

import ThreeJSModal from "./ThreeJSModal";

function App() {
  return (
    <div>
      <ThreeJSModal />
    </div>
  );
}

export default App;

Chú ý rằng bạn cần phải thay đổi đường dẫn "/path/to/your/modal.jbx" để trỏ đúng đến file .jbx của bạn.

Kết hợp với texture riêng

Để kết hợp với texture riêng dạng png, bạn có thể sử dụng TextureLoader để load texture và gán nó vào vật thể sử dụng map trong Three.js.

Ví dụ, giả sử bạn có một texture là file "texture.png" trong thư mục public của ứng dụng của bạn. Bạn có thể sử dụng nó như sau:

import React, { useRef, useState } from "react";
import { useLoader } from "react-three-fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { TextureLoader } from "three/src/loaders/TextureLoader";

const ThreeJSModal = () => {
  const [modalLoaded, setModalLoaded] = useState(false);
  const modalRef = useRef();
  
  // Sử dụng useLoader để load file .jbx
  const gltf = useLoader(GLTFLoader, "/path/to/your/modal.jbx");
  
  // Sử dụng TextureLoader để load texture
  const texture = useLoader(TextureLoader, "/texture.png");

  // Callback khi file đã được load
  const onModalLoaded = () => {
    setModalLoaded(true);
  };

  return (
    <>
      {modalLoaded && (
        <>
          {/* Sử dụng OrbitControls để có thể quay vật thể */}
          <OrbitControls
            enableDamping
            dampingFactor={0.5}
            enableZoom={false}
            ref={modalRef}
          />
          {/* Hiển thị vật thể và texture */}
          <primitive object={gltf.scene}>
            <meshStandardMaterial map={texture} />
          </primitive>
        </>
      )}
    </>
  );
};

export default ThreeJSModal;

Trong ví dụ trên, chúng ta sử dụng TextureLoader để load file "texture.png". Sau đó, chúng ta gán texture này vào vật thể sử dụng map trong meshStandardMaterial. Bằng cách này, vật thể sẽ có texture "texture.png" kết hợp với file .jbx đã load.

Run animation của modal

Để chạy animation đầu tiên trong mảng animation của file .jbx, bạn có thể sử dụng useRef để tham chiếu đến vật thể của modal sau đó sử dụng phương thức mixer.clipAction() của AnimationMixer trong Three.js để chọn và chạy animation đầu tiên trong mảng animation.

Ví dụ, giả sử bạn có một file "modal.jbx" chứa một mảng các animation, và bạn muốn chạy animation đầu tiên trong mảng đó khi modal được tải lên, bạn có thể sử dụng mã sau:

import React, { useRef, useState, useEffect } from "react";
import { useLoader } from "react-three-fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

const ThreeJSModal = () => {
  const [modalLoaded, setModalLoaded] = useState(false);
  const modalRef = useRef();
  const mixerRef = useRef();

  // Sử dụng useLoader để load file .jbx
  const gltf = useLoader(GLTFLoader, "/path/to/your/modal.jbx");

  // Callback khi file đã được load
  const onModalLoaded = () => {
    setModalLoaded(true);
  };

  // Chạy animation đầu tiên khi modal được tải lên
  useEffect(() => {
    if (modalLoaded) {
      // Tìm animation đầu tiên trong mảng animation
      const [firstAnimation] = gltf.animations;

      // Khởi tạo mixer để chạy animation
      mixerRef.current = new THREE.AnimationMixer(modalRef.current);

      // Chọn animation đầu tiên và chạy nó
      const action = mixerRef.current.clipAction(firstAnimation);
      action.play();
    }
  }, [modalLoaded]);

  return (
    <>
      {modalLoaded && (
        <>
          {/* Sử dụng OrbitControls để có thể quay vật thể */}
          <OrbitControls
            enableDamping
            dampingFactor={0.5}
            enableZoom={false}
            ref={modalRef}
          />
          {/* Hiển thị vật thể */}
          <primitive object={gltf.scene} ref={modalRef} />
        </>
      )}
    </>
  );
};

export default ThreeJSModal;

Trong ví dụ trên, chúng ta sử dụng useEffect để chạy animation đầu tiên trong mảng animation khi modal được tải lên. Đầu tiên, chúng ta tìm animation đầu tiên trong mảng animation bằng cách lấy phần tử đầu tiên trong mảng gltf.animations. Sau đó, chúng ta khởi tạo một AnimationMixer và sử dụng mixer.clipAction() để chọn và chạy animation đầu tiên.

Chúng ta cũng sử dụng useRef để tham chiếu đến vật thể của modal thông qua modalRef và tham chiếu đến AnimationMixer thông qua mixerRef.

Chạm để thay đổi animation

Để thay đổi animation khi người dùng nhấp vào modal 3D, bạn có thể sử dụng sự kiện onClick của mesh trong Three.js để chuyển đổi giữa các animation trong mảng gltf.animations của file .jbx.

Ví dụ, giả sử bạn có một file "modal.jbx" chứa một mảng các animation, và bạn muốn chuyển đổi giữa các animation khi người dùng nhấp vào modal, bạn có thể sử dụng mã sau:

import React, { useRef, useState, useEffect } from "react";
import { useLoader } from "react-three-fiber";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

const ThreeJSModal = () => {
  const [modalLoaded, setModalLoaded] = useState(false);
  const modalRef = useRef();
  const mixerRef = useRef();
  const [animationIndex, setAnimationIndex] = useState(0);

  // Sử dụng useLoader để load file .jbx
  const gltf = useLoader(GLTFLoader, "/path/to/your/modal.jbx");

  // Callback khi file đã được load
  const onModalLoaded = () => {
    setModalLoaded(true);
  };

  // Chạy animation khi animationIndex thay đổi
  useEffect(() => {
    if (modalLoaded) {
      // Tìm animation tương ứng với animationIndex trong mảng animation
      const animation = gltf.animations[animationIndex];

      // Chọn animation và chạy nó
      const action = mixerRef.current.clipAction(animation);
      action.play();
    }
  }, [animationIndex]);

  // Sự kiện onClick để chuyển đổi giữa các animation
  const onClick = () => {
    setAnimationIndex((animationIndex + 1) % gltf.animations.length);
  };

  return (
    <>
      {modalLoaded && (
        <>
          {/* Sử dụng OrbitControls để có thể quay vật thể */}
          <OrbitControls
            enableDamping
            dampingFactor={0.5}
            enableZoom={false}
            ref={modalRef}
          />
          {/* Sử dụng sự kiện onClick để chuyển đổi giữa các animation */}
          <mesh onClick={onClick} ref={modalRef}>
            <primitive object={gltf.scene} />
          </mesh>
        </>
      )}
    </>
  );
};

export default ThreeJSModal;

Trong ví dụ trên, chúng ta sử dụng useState để lưu trữ chỉ số của animation đang được chạy. Mỗi lần người dùng nhấp vào modal, chúng ta sẽ tăng chỉ số này lên một đơn vị và lấy phần dư với độ dài của mảng animation để chuyển đổi giữa các animation. Khi chỉ số animationIndex thay đổi, chúng ta sử dụng useEffect để chọn animation tương ứng từ mảng animation và chạy nó bằng mixer.clipAction().


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.