Fogos de Artifício
Bem-vindo à Sky Adventure, uma empresa futurista que oferece os melhores fogos de artifício da galáxia! 🎇
Vamos criar um site 3D para exibir nossos fogos de artifício usando Three.js, React Three Fiber e nosso motor VFX.
Esse é o projeto que vamos construir juntos!
Projeto inicial
Nosso projeto inicial já inclui o seguinte:
- Uma configuração básica de React Three Fiber com uma adorável ilha flutuante de onde lançaremos nossos fogos de artifício.
- Efeitos de pós-processamento para fazer brilhar as luzes do modelo (e posteriormente dos fogos de artifício).
- Uma interface simples feita com Tailwind CSS com três botões para posteriormente lançar os fogos de artifício.
Aqui está o que obtemos quando executamos o projeto inicial.
Fogos de Artifício
Para criar os fogos de artifício, usaremos o motor VFX que construímos na lição anterior. Este motor nos permite criar e gerenciar múltiplos sistemas de partículas com comportamentos diferentes.
useFireworks
Para gerenciar os fogos de artifício de maneira eficiente, vamos criar um hook customizado chamado useFireworks
. Este hook lidará com a criação e gestão dos fogos de artifício.
Vamos adicionar a biblioteca zustand ao nosso projeto:
yarn add zustand
Agora, na pasta chamada hooks
, crie um novo arquivo chamado useFireworks.js
:
import { create } from "zustand"; const useFireworks = create((set, get) => { return { fireworks: [], }; }); export { useFireworks };
Uma store simples com um array vazio de fogos de artifício.
Vamos adicionar um método para criar um fogo de artifício:
// ... 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
adicionará um novo fogo de artifício à store com:
id
: um identificador único para usar como chave no componente React.position
: onde o fogo de artifício começará.velocity
: a direção e a velocidade do fogo de artifício antes de explodir.delay
: o tempo antes de o fogo de artifício explodir.color
: um array de cores para usar nas partículas da explosão do fogo de artifício.
Agora podemos conectar esse hook à nossa UI para lançar fogos de artifício.
Abra UI.jsx
e vamos conectar o método addFirework
aos botões:
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> ); };
Para verificar se funciona, vamos criar um componente Fireworks.jsx
. Vamos simplesmente logar os fogos de artifício no console por enquanto:
import { useFireworks } from "../hooks/useFireworks"; export const Fireworks = () => { const fireworks = useFireworks((state) => state.fireworks); console.log(fireworks); };
E vamos adicioná-lo ao componente Experience
:
// ... 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> {/* ... */} </> ); };
Nós importamos o componente Fireworks
e o adicionamos ao lado do SkyIsland em nosso componente <Float />
.
Ao pressionar os botões, podemos ver no console que os fogos de artifício são devidamente adicionados à store.
Antes de representá-los na cena, precisamos gerenciar o ciclo de vida dos fogos de artifício. Atualmente, eles são adicionados, mas nunca removidos.
No método addFirework
do nosso hook useFireworks
, podemos adicionar um setTimeout
para remover o fogo de artifício após um certo tempo.
Primeiro, precisamos saber quando o fogo de artifício é criado. Podemos adicionar uma propriedade time
ao objeto do fogo de artifício:
{ id: `${Date.now()}-${randInt(0, 100)}-${state.fireworks.length}`, // ... time: Date.now(), },
Em seguida, chamamos setTimeout
para remover os fogos de artifício que explodiram e desapareceram:
addFirework: () => { set((state) => { // ... }); setTimeout(() => { set((state) => ({ fireworks: state.fireworks.filter( (firework) => Date.now() - firework.time < 4000 // Max delay de 2 segundos + tempo de vida máximo das partículas de 2 segundos ), })); }, 4000); },
Filtramos os fogos de artifício que foram adicionados há mais de 4 segundos. Desta forma, mantemos os fogos de artifício que ainda estão ativos.
Ajuste o tempo de acordo com as configurações finais que você usará para os fogos de artifício.
Ao pressionar os botões, podemos ver no console que os fogos de artifício são devidamente removidos após um certo tempo.
Agora podemos mergulhar na parte que você estava esperando: criando os fogos de artifício na cena!
Motor de VFX (Wawa VFX)
Para não copiar/colar o componente da lição anterior, publiquei o motor de VFX como um pacote no npm: Wawa VFX. Você pode instalá-lo executando:
yarn add wawa-vfx@^1.0.0
Ao usar
@^1.0.0
, garantimos que sempre utilizamos a versão principal 1 do pacote, mas incluindo as versões mais recentes menores e de correções. Dessa forma, podemos nos beneficiar dos recursos mais recentes e correções de bugs sem alterações que quebram a compatibilidade.
Agora podemos usar <VFXParticles />
e <VFXEmitter />
em nosso projeto!
Na experiência, vamos adicionar o componente VFXParticles
à cena:
// ... 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> </> ); };
Adicionamos o componente VFXParticles
com as seguintes configurações:
100000
partículas. Como, ao atingirmos o limite, as partículas mais antigas serão removidas, isso deve ser mais do que suficiente para muitos fogos de artifício ao mesmo tempo.gravity
para simular a gravidade nas partículas.-9.8
é a gravidade na Terra. (Mas espere, estamos no espaço! 👀)renderMode
parabillboard
para sempre enfrentar a câmera.intensity
para3
para fazer as partículas brilharem no céu noturno.
Onde você coloca o componente
<VFXParticles />
não é muito importante. Apenas certifique-se de que ele esteja no nível superior da cena.
E vamos adicionar um componente VFXEmitter
ao lado do VFXParticles
para moldar a explosão no modo debug
e ter acesso aos controles visuais:
// ... 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 /> {/* ... */} </> ); };
Certifique-se de definir a prop emitter
com o mesmo name
que o componente VFXParticles
e definir debug
como true
para ver os controles.
*Elabore uma primeira versão da explosão de fogos de artifício. *💥
Quando estiver satisfeito com as configurações, você pode remover a prop debug
do componente VFXEmitter
, pressionar o botão de exportação e colar as configurações no componente 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], }} />
Temos tudo pronto para conectar os fogos de artifício ao motor de VFX!
Explosão de Fogos de Artifício
Dentro do arquivo Fireworks.jsx
, vamos criar um componente Firework
que representará um fogo de artifício:
// ... 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> </> ); };
Simplesmente corte e cole o VFXEmitter
do componente Experience
para o componente Firework
.
Nós o envolvemos em um <group />
para poder mover o fogo de artifício na cena com base na position
e 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.