Làm việc Modal 3D với Threejs trong reactjs bằng thư viện react-three-fiber
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