Pháo hoa
Chào mừng đến với Cuộc phiêu lưu trên bầu trời, một công ty tương lai cung cấp màn pháo hoa tuyệt nhất trong thiên hà! 🎇
Chúng ta sẽ tạo ra một trang web 3D để giới thiệu màn pháo hoa của chúng ta bằng cách sử dụng Three.js, React Three Fiber và động cơ VFX của chúng ta.
Đây là những gì chúng ta sẽ cùng nhau xây dựng!
Dự án khởi động
Dự án khởi động của chúng ta đã bao gồm các thành phần sau:
- Thiết lập cơ bản của React Three Fiber với một hòn đảo nổi đẹp mắt từ đó chúng ta sẽ phóng pháo hoa.
- Hiệu ứng xử lý hậu kỳ để làm nổi bật ánh sáng từ mô hình (và sau này là pháo hoa).
- Giao diện đơn giản được làm bằng Tailwind CSS với ba nút để sau này phóng pháo hoa.
Đây là những gì chúng ta nhận được khi chạy dự án khởi động.
Pháo hoa
Để tạo ra pháo hoa, chúng ta sẽ sử dụng động cơ VFX mà chúng ta đã xây dựng ở bài học trước. Động cơ này cho phép chúng ta tạo và quản lý nhiều hệ thống hạt với các hành vi khác nhau.
useFireworks
Để quản lý hiệu quả pháo hoa, chúng ta sẽ tạo một custom hook gọi là useFireworks
. Hook này sẽ xử lý việc tạo và quản lý pháo hoa.
Hãy thêm thư viện zustand vào dự án của chúng ta:
yarn add zustand
Bây giờ, trong một thư mục tên là hooks
, tạo một file mới gọi là useFireworks.js
:
import { create } from "zustand"; const useFireworks = create((set, get) => { return { fireworks: [], }; }); export { useFireworks };
Một kho chứa đơn giản với một mảng trống chứa pháo hoa.
Hãy thêm một phương thức để tạo pháo hoa:
// ... import { randFloat, randInt } from "three/src/math/MathUtils.js"; const useFireworks = create((set) => { return { fireworks: [], addFirework: () => { set((state) => { return { fireworks: [ ...state.fireworks, { id: `${Date.now()}-${randInt(0, 100)}-${state.fireworks.length}`, position: [0, 0, 0], velocity: [randFloat(-8, 8), randFloat(5, 10), randFloat(-8, 8)], delay: randFloat(0.8, 2), color: ["skyblue", "pink"], }, ], }; }); }, }; }); // ...
addFirework
sẽ thêm pháo hoa mới vào kho chứa với:
id
: một định danh duy nhất để sử dụng làm key trong thành phần React.position
: vị trí mà pháo hoa bắt đầu.velocity
: hướng và tốc độ của pháo hoa trước khi nổ.delay
: thời gian trước khi pháo hoa nổ.color
: một mảng các màu để dùng cho các hạt nổ của pháo hoa.
Chúng ta có thể nối kết hook này vào giao diện UI của chúng ta để kích hoạt pháo hoa.
Mở UI.jsx
và kết nối phương thức addFirework
với các nút:
import { useFireworks } from "../hooks/useFireworks"; export const UI = () => { const addFirework = useFireworks((state) => state.addFirework); return ( <section className="fixed inset-0 z-10 flex items-center justify-center"> {/* ... */} <div // ... > {/* ... */} <div className="flex gap-4"> <button // .. onClick={addFirework} > 🎆 Classic </button> <button // .. onClick={addFirework} > 💖 Love </button> <button // .. onClick={addFirework} > 🌊 Sea </button> </div> {/* ... */} </div> </section> ); };
Để kiểm tra xem nó có hoạt động không, hãy tạo một thành phần Fireworks.jsx
. Chúng ta sẽ đơn giản chỉ ghi log pháo hoa ra console vào lúc này:
import { useFireworks } from "../hooks/useFireworks"; export const Fireworks = () => { const fireworks = useFireworks((state) => state.fireworks); console.log(fireworks); };
Và thêm nó vào trong thành phần Experience
của chúng ta:
// ... import { Fireworks } from "./Fireworks"; export const Experience = () => { // ... return ( <> {/* ... */} <Float speed={0.6} rotationIntensity={2} position-x={4} floatIntensity={2} > <Fireworks /> <Gltf src="/models/SkyIsland.glb" /> </Float> {/* ... */} </> ); };
Chúng ta import thành phần Fireworks
và thêm nó bên cạnh SkyIsland trong <Float />
.
Khi nhấn các nút, chúng ta có thể thấy trong console rằng pháo hoa được thêm đúng cách vào trong kho.
Trước khi hiển thị chúng trong cảnh 3D, chúng ta cần xử lý vòng đời của pháo hoa. Hiện tại, pháo hoa được thêm vào nhưng không bao giờ bị loại bỏ.
Trong addFirework
của useFireworks
hook, chúng ta có thể thêm một setTimeout
để loại bỏ pháo hoa sau một khoảng thời gian nhất định.
Đầu tiên, chúng ta cần biết khi nào pháo hoa xuất hiện. Chúng ta có thể thêm một thuộc tính time
vào đối tượng pháo hoa:
{ id: `${Date.now()}-${randInt(0, 100)}-${state.fireworks.length}`, // ... time: Date.now(), },
Sau đó gọi setTimeout
để loại bỏ pháo hoa đã nổ và đã mờ dần:
addFirework: () => { set((state) => { // ... }); setTimeout(() => { set((state) => ({ fireworks: state.fireworks.filter( (firework) => Date.now() - firework.time < 4000 // Max delay of 2 seconds + Max lifetime of particles of 2 seconds ), })); }, 4000); },
Chúng ta lọc các pháo hoa đã được thêm vào hơn 4 giây trước. Bằng cách này, chúng ta giữ lại những pháo hoa còn hoạt động.
Điều chỉnh thời gian theo cấu hình cuối cùng bạn sẽ sử dụng cho pháo hoa.
Khi nhấn các nút, chúng ta có thể thấy trong console rằng pháo hoa được loại bỏ đúng cách sau một khoảng thời gian nhất định.
Bây giờ, chúng ta có thể đi sâu vào phần mà bạn đang mong chờ: tạo pháo hoa trong cảnh 3D!
Công cụ VFX (Wawa VFX)
Để không phải sao chép/dán thành phần từ bài học trước, tôi đã xuất bản công cụ VFX dưới dạng một package trên npm: Wawa VFX. Bạn có thể cài đặt nó bằng cách chạy:
yarn add wawa-vfx@^1.0.0
Bằng cách sử dụng
@^1.0.0
, chúng ta đảm bảo luôn sử dụng phiên bản chính 1 của package nhưng bao gồm các phiên bản phụ và vá lỗi mới nhất. Bằng cách này, chúng ta có thể tận dụng các tính năng mới nhất và sửa lỗi mà không gặp thay đổi phá vỡ.
Giờ đây, chúng ta có thể sử dụng <VFXParticles />
và <VFXEmitter />
trong dự án của mình!
Trong phần thực nghiệm, hãy thêm thành phần VFXParticles
vào cảnh:
// ... import { VFXParticles } from "wawa-vfx"; export const Experience = () => { const controls = useRef(); return ( <> {/* ... */} <VFXParticles name="firework-particles" settings={{ nbParticles: 100000, gravity: [0, -9.8, 0], renderMode: "billboard", intensity: 3, }} /> <EffectComposer> <Bloom intensity={1.2} luminanceThreshold={1} mipmapBlur /> </EffectComposer> </> ); };
Chúng ta thêm thành phần VFXParticles
với các cài đặt sau:
100000
hạt. Khi chúng ta đạt giới hạn, các hạt cũ nhất sẽ bị xóa, số lượng này nên đủ cho nhiều pháo hoa cùng lúc.gravity
để mô phỏng lực hấp dẫn trên các hạt.-9.8
là lực hấp dẫn trên Trái Đất. (Nhưng chờ đã, chúng ta đang ở trong không gian! 👀)renderMode
làbillboard
để luôn hướng về phía camera.intensity
là3
để làm cho các hạt sáng lên trong bầu trời đêm.
Vị trí đặt thành phần
<VFXParticles />
không quá quan trọng. Chỉ cần đảm bảo nó ở cấp độ cao nhất của cảnh.
Và hãy thêm thành phần VFXEmitter
bên cạnh VFXParticles
để định hình vụ nổ trong chế độ debug
và có quyền truy cập vào các điều khiển hiển thị:
// ... import { VFXEmitter, VFXParticles } from "wawa-vfx"; export const Experience = () => { const controls = useRef(); return ( <> {/* ... */} <VFXParticles name="firework-particles" settings={{ nbParticles: 100000, gravity: [0, -9.8, 0], renderMode: "billboard", intensity: 3, }} /> <VFXEmitter emitter="firework-particles" debug /> {/* ... */} </> ); };
Hãy chắc chắn rằng giá trị emitter
được đặt giống với name
của thành phần VFXParticles
và đặt debug
là true
để thấy các điều khiển.
Phác thảo một phiên bản đầu tiên của vụ nổ pháo hoa. 💥
Khi bạn đã hài lòng với các cài đặt, bạn có thể loại bỏ thuộc tính debug
từ thành phần VFXEmitter
, nhấn nút xuất và dán các cài đặt vào thành phần VFXEmitter
.
<VFXEmitter emitter="firework-particles" settings={{ nbParticles: 5000, delay: 0, spawnMode: "burst", colorStart: ["skyblue", "pink"], particlesLifetime: [0.1, 2], size: [0.01, 0.4], startPositionMin: [-0.1, -0.1, -0.1], startPositionMax: [0.1, 0.1, 0.1], directionMin: [-1, -1, -1], directionMax: [1, 1, 1], startRotationMin: [degToRad(-90), 0, 0], startRotationMax: [degToRad(90), 0, 0], rotationSpeedMin: [0, 0, 0], rotationSpeedMax: [3, 3, 3], speed: [1, 12], }} />
Chúng ta đã sẵn sàng để kết nối pháo hoa với công cụ VFX!
Pháo hoa nổ
Bên trong file Fireworks.jsx
, hãy tạo một component Firework
đại diện cho một quả pháo hoa:
// ... import { useRef } from "react"; import { VFXEmitter } from "wawa-vfx"; export const Fireworks = () => { // ... }; const Firework = ({ velocity, delay, position, color }) => { const ref = useRef(); return ( <> <group ref={ref} position={position}> <VFXEmitter emitter="firework-particles" settings={{ nbParticles: 5000, delay: 0, spawnMode: "burst", colorStart: ["skyblue", "pink"], particlesLifetime: [0.1, 2], size: [0.01, 0.4], startPositionMin: [-0.1, -0.1, -0.1], startPositionMax: [0.1, 0.1, 0.1], directionMin: [-1, -1, -1], directionMax: [1, 1, 1], startRotationMin: [degToRad(-90), 0, 0], startRotationMax: [degToRad(90), 0, 0], rotationSpeedMin: [0, 0, 0], rotationSpeedMax: [3, 3, 3], speed: [1, 12], }} /> </group> </> ); };
Đơn giản là cắt/dán VFXEmitter
từ component Experience
sang component Firework
.
Chúng ta bọc nó trong một thẻ <group />
để có thể di chuyển pháo hoa trong không gian dựa trên position
và velocity
.
React Three Fiber: The Ultimate Guide to 3D Web Development
✨ You have reached the end of the preview ✨
Go to the next level with Three.js and React Three Fiber!
Get full access to this lesson and the complete course when you enroll:
- 🔓 Full lesson videos with no limits
- 💻 Access to the final source code
- 🎓 Course progress tracking & completion
- 💬 Invite to our private Discord community
One-time payment. Lifetime updates included.