<Particles> sends out particles in two ways:
- Bursts, fired from code: a sparkle when a coin is earned, confetti for a milestone, a firework.
- Streams, running while you want them: smoke from a chimney, a fountain, snow, a sparkler.
Between bursts they cost the CPU nothing, and the scene goes back to sleep once the last particle fades.
A burst#
import { Box, Particles, type ParticlesHandle, Scene } from "@apricot/react";
import { useRef } from "react";
export function Reward() {
const sparks = useRef<ParticlesHandle>(null);
return (
<Scene style={{ flex: 1 }} look="soft">
<Box
size={0.8}
material="gold"
onPress={() => sparks.current?.burst("sparkle", { at: [0, 0.6, 0] })}
/>
<Particles ref={sparks} />
</Scene>
);
}burst(name, changes) fires a ready-made burst, with your changes on top. burst(options) fires one of your own:
sparks.current?.burst("confetti", { at: [0, 1, 0], colors: ["#ff5a7a", "#ffd166"] });
sparks.current?.burst({ at: [0, 1, 0], count: 30, speed: [1, 2], spread: 45, life: 0.8, size: 0.05, gravity: 3 });| Burst | Looks like |
|---|---|
"sparkle" |
Gold glints rising a little and fading: a coin, a reward |
"firework" |
A firework's shell bursting: sparks every way, slowing and falling |
"pop" |
A small pop: something placed, a button's reward |
"confetti" |
Paper confetti thrown up, fluttering down: a celebration |
A stream#
Set emit to run a stream; set it to false (or remove it) to stop. Changes to it follow as it runs.
import { EMITTERS, Particles } from "@apricot/react";
export const Chimney = ({ lit }: { lit: boolean }) => (
<Particles emit={lit && { ...EMITTERS.smoke, at: [1.7, 1.6, -0.8] }} />
);emit takes a name (emit="snow") or options. Spread a ready-made stream from EMITTERS to change it, as above.
| Stream | Looks like |
|---|---|
"smoke" |
Soft grey smoke rising, spreading and drifting: a chimney, a campfire |
"fountain" |
Water thrown up and falling back |
"snow" |
Snow falling slowly over an area (give it area, and at above the ground) |
"sparkler" |
A sparkler's glints, thrown out and falling: hold it at the tip |
Streams can also be started from code: ref.current.emit(name or options, changes) returns an Emitter, with set(options) to change it as it goes (move it with at), stop() to end it (those already out finish their lives) and running.
import { Particles, type ParticlesHandle, type Emitter } from "@apricot/react";
import { useEffect, useRef } from "react";
export function Sparkler({ tip }: { tip: [number, number, number] }) {
const particles = useRef<ParticlesHandle>(null);
const stream = useRef<Emitter | null>(null);
useEffect(() => {
stream.current = particles.current?.emit("sparkler", { at: tip }) ?? null;
return () => stream.current?.stop();
}, []);
useEffect(() => {
stream.current?.set({ at: tip });
}, [tip]);
return <Particles ref={particles} glow />;
}Shaping particles#
Bursts and streams take the same options (streams have rate instead of count). A value written as a range [min, max] gives each particle a random value in it.
| Option | Default | Meaning |
|---|---|---|
at |
[0, 0, 0] |
Where they start, in the particles' own space |
area |
none | [x, y, z] size of a box round at they start anywhere in: snow over a garden, smoke off a fire |
count |
40 |
How many in a burst |
rate |
20 |
How many a second, for a stream |
colors |
["#ffffff"] |
Each particle takes one |
fadeTo |
none | A colour they turn to over their life: embers cooling, smoke greying |
speed |
[1, 2] |
How fast they fly out, metres per second |
direction |
[0, 1, 0] (up) |
The way they fly |
spread |
180 |
Degrees round direction they spread: 180 is every way, 10 a narrow jet |
life |
[0.6, 1.2] |
Seconds each lives |
size |
0.05 |
Metres |
grow |
1 |
Size at the end of its life against its start: 3 smoke spreading, 0 shrinking away |
spin |
0 |
Degrees per second. A range like [-90, 90] turns some one way, some the other |
shape |
"dot" |
"dot", a soft round dot, or "square", a square that flutters as it turns, like paper |
gravity |
0 |
Pull down, metres per second². 9.8 is a dropped stone; below 0 they rise, like smoke |
wind |
[0, 0, 0] |
A steady push, metres per second²: [1, 0, 0] is a breeze along x |
drag |
0 |
How quickly they slow in the air: 2–4 for a firework's sparks |
How they look#
| Prop | Default | Meaning |
|---|---|---|
glow |
false |
true adds them to what is behind, so they glow: fireworks at night. false draws them over it, which also shows on a light scene |
texture |
a soft dot | A picture each particle shows (a leaf, a star), tinted by its colour. Load it with useTexture |
max |
1000 |
The most alive at once. More reuse the oldest |
Plus position, rotation and scale, which move all of them (and at is in that space).
Leaves blown off a tree, with a picture of a leaf:
import { Particles } from "@apricot/react";
import { useTexture } from "@apricot/react/model";
export function FallingLeaves() {
const leaf = useTexture(require("./assets/leaf.png"));
return (
<Particles
texture={leaf}
emit={{
rate: 6,
at: [0, 3, 0],
area: [2, 0.5, 2],
colors: ["#d9822b", "#b5502a"],
fadeTo: "#7a4a2a",
speed: 0.2,
direction: [0, -1, 0],
life: [4, 6],
size: 0.12,
spin: [-90, 90],
gravity: 0.6,
wind: [0.4, 0, 0.1],
drag: 1.5,
}}
/>
);
}Tips#
- One
<Particles>can fire many bursts and run several streams; use more than one when they need differentglowortexture. - Keep
<Particles>outside a<Bundle>: their particles move every frame. - Changing
max,glowortexturemakes a new set of particles (streams started from code start again). Keep them steady.
Related#
- Particles reference.
- Falling objects: solid objects that bounce and pile up.