Trilhas
Vamos mergulhar no mundo das trilhas! As trilhas são uma ótima maneira de adicionar uma sensação de movimento à sua cena. Elas podem ser usadas para criar uma variedade de efeitos, como trilhas de luz, trilhas de fumaça ou até mesmo a trilha de um objeto em movimento.
Aqui está o projeto final que construiremos juntos:
Começaremos criando um efeito simples de trilha usando um cursor de trilha personalizado. Em seguida, exploraremos o componente Trail do drei para criar os cometas que você viu na pré-visualização.
Projeto Inicial
O projeto inicial contém muitos elementos que já abordamos em lições anteriores:
- O componente ScrollControls para lidar com o scroll e o movimento da câmera e animações associadas. Se precisar relembrar, você pode conferir a lição dedicada ao Scroll.
- Efeitos de pós-processamento como Bloom e Vignette, com uma adorável adição do efeito GodRays. Confira a lição de Pós-processamento se precisar relembrar.
- O
<StarrySky />
que construímos na lição de Partículas com parâmetros ajustados.
Além disso, usei o Tailwind CSS para projetar rapidamente a interface do usuário. Se não estiver familiarizado com o Tailwind CSS, você pode pular a parte da interface do usuário e se concentrar na parte do Threejs.
Os modelos WawaCoin e WawaCard são feitos internamente e estão disponíveis no projeto inicial. Usei o MeshTransmissionMaterial do drei para criar esse visual futurista.
Sinta-se à vontade para transformar a cena ao seu gosto. Você pode reutilizar livremente qualquer parte do projeto em seus próprios projetos.
Esqueci de mencionar, mas o conteúdo do site é puramente fictício. Eu não estou lançando uma nova criptomoeda. (Ainda não? 👀)
Cursor customizado com rastro
Vamos começar criando um efeito simples de rastro seguindo o cursor.
Crie um novo arquivo components/Cursor.jsx
e adicione o seguinte código:
import { useFrame } from "@react-three/fiber"; import { useControls } from "leva"; import { useRef } from "react"; export const Cursor = () => { const { color, intensity, opacity, size } = useControls("Cursor", { size: { value: 0.2, min: 0.1, max: 3, step: 0.01 }, color: "#dfbcff", intensity: { value: 4.6, min: 1, max: 10, step: 0.1 }, opacity: { value: 0.5, min: 0, max: 1, step: 0.01 }, }); const target = useRef(); useFrame(({ clock }) => { if (target.current) { const elapsed = clock.getElapsedTime(); target.current.position.x = Math.sin(elapsed) * 5; target.current.position.y = Math.cos(elapsed * 2) * 4; target.current.position.z = Math.sin(elapsed * 4) * 10; } }); return ( <> <group ref={target}> <mesh> <sphereGeometry args={[size / 2, 32, 32]} /> <meshStandardMaterial color={color} transparent opacity={opacity} emissive={color} emissiveIntensity={intensity} /> </mesh> </group> </> ); };
É uma esfera simples que segue uma onda senoidal. Você pode ajustar o tamanho, cor, intensidade e opacidade do cursor usando os controles Leva.
Por enquanto, usamos um movimento fixo para simplificar a visualização do rastro. Iremos substituí-lo pela posição do mouse depois.
Adicione o componente Cursor
ao componente Experience
:
// ... import { Cursor } from "./Cursor"; export const Experience = () => { // ... return ( <> <Cursor /> {/* ... */} </> ); }; // ...
Podemos ver uma esfera em movimento, que será o alvo do nosso rastro.
Componente SimpleTrail
O group é o alvo que nosso rastro seguirá. Vamos criar um novo componente components/SimpleTrail.jsx
para criar o efeito de rastro:
import { useRef } from "react"; import * as THREE from "three"; export function SimpleTrail({ target = null, color = "#ffffff", intensity = 6, numPoints = 20, height = 0.42, minDistance = 0.1, opacity = 0.5, duration = 20, }) { const mesh = useRef(); return ( <> <mesh ref={mesh}> <planeGeometry args={[1, 1, 1, numPoints - 1]} /> <meshBasicMaterial color={color} side={THREE.DoubleSide} transparent={true} opacity={opacity} depthWrite={false} /> </mesh> </> ); }
Os parâmetros são os seguintes:
- target: o ref do alvo a ser seguido.
- color: a cor do rastro.
- intensity: a intensidade da parte emissiva do rastro.
- numPoints: o número de posições que armazenaremos no rastro. (Quanto maior o número, mais longo o rastro).
- height: a altura do rastro.
- minDistance: a distância mínima entre dois pontos.
- opacity: a opacidade do rastro.
- duration: o tempo antes de o rastro começar a desaparecer a partir do seu final.
Não se preocupe se você ainda não entende todos os parâmetros. Vamos explicá-los enquanto implementamos o rastro.
Importe o componente SimpleTrail
no componente Cursor
:
// ... import { SimpleTrail } from "./SimpleTrail"; export const Cursor = () => { // ... return ( <> <group ref={target}>{/* ... */}</group> <SimpleTrail target={target} color={color} intensity={intensity} opacity={opacity} height={size} /> </> ); };
A mesh é composta por um <planeGeometry />
com um número de segmentos igual a numPoints
. Vamos atualizar a posição de cada segmento para seguir o alvo.
Visualmente, como o tamanho do nosso plano é 1x1, podemos ver um quadrado, mas por causa do número de segmentos, poderemos manipular os vértices para criar o efeito de rastro.
Vamos ver lado a lado um plane com um segmento e um plane com 20 segmentos:
<group position-x={5}> <mesh position-x={4} scale-y={5}> <planeGeometry args={[1, 1, 1, numPoints - 1]} /> <meshBasicMaterial color={"red"} wireframe /> </mesh> <mesh position-x={2} scale-y={5}> <planeGeometry args={[1, 1, 1, 1]} /> <meshBasicMaterial color={"red"} wireframe /> </mesh> </group>
Este código é apenas para fins de visualização. Você pode removê-lo após entender o conceito.
Nós os escalamos no eixo y para ver a diferença no número de segmentos.
Você pode ver que o plano à esquerda tem apenas 4 vértices enquanto o plano à direita tem muitos mais. Vamos manipular esses vértices para construir o efeito de rastro.
Poderíamos usar uma line em vez de um plane para criar o rastro, mas usar um plane nos permite criar um efeito interessante (Funciona melhor para vento, por exemplo).
O componente Trail do drei usa uma line, nós não queremos recodificar a mesma coisa.
Manipulando os vértices
Atualizaremos a posição dos vértices do plano para seguir o alvo ao longo do tempo.
Primeiro, precisaremos armazenar todas as posições do alvo em um array. Usaremos um ref para armazenar as posições.
// ... import * as THREE from "three"; export function SimpleTrail( { // ... } ) { const mesh = useRef(); const positions = useRef( new Array(numPoints).fill(new THREE.Vector3(0, 0, 0)) ); // ... }
Esse array sempre terá um comprimento de numPoints
e armazenará as posições do alvo.
Quando o alvo se mover, adicionaremos a nova posição à frente do array, empurrando as outras posições para trás.
Para implementar isso, usaremos o hook useFrame para atualizar a posição dos vértices.
// ... import { useFrame } from "@react-three/fiber"; export function SimpleTrail( { // ... } ) { // ... useFrame(() => { if (!mesh.current || !target?.current) { return; } const curPoint = target.current.position; const lastPoint = positions.current[0]; const distanceToLastPoint = lastPoint.distanceTo(target.current.position); if (distanceToLastPoint > minDistance) { positions.current.unshift(curPoint.clone()); positions.current.pop(); } }); // ... }
Primeiro, calculamos a distância entre o último ponto e o ponto atual. Se a distância for maior que minDistance
, adicionamos o ponto atual à frente do array com unshift
e removemos o último ponto com pop
.
Agora, precisamos atualizar a posição dos vértices do plano para seguir as posições do alvo.
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.