Value · 06
Stagger distribution
错峰分布 · staggerAssign start times by index or distance to give a group a readable rhythm.
Starting everything together reads as a flash, while list order may not fit a grid. A stagger function gives each element a distance, then maps that distance to a delay. Origin, grid, axis and easing decide how the wave spreads.
Formula
dᵢ = distance(cellᵢ, origin) delayᵢ = ease(dᵢ/maxD) · amount or delayᵢ = ease(dᵢ/maxD) · maxD · each
Parameters
| This page | Mature libraries | |
|---|---|---|
| each / amount | 0.08 s / 0.9 s | GSAP each or amounteach spaces neighbours; amount spans the whole group and takes precedence. |
| from / grid / axis | start / 7×5 / both | GSAP center, edges, end, random, index; grid, axisChanging the origin changes the order of attention. |
Doing it well
- Stagger a two-dimensional layout by spatial distance, rather than treating every row as one long list.GSAP src/gsap-core.js:452–500
- Use
amountto control total spread; useeachto preserve spacing between neighbours.GSAP src/gsap-core.js:488–500 - Choose an origin that suits the composition.
cardwallstaggers from the center as scattered cards settle into place.www-motion/rewrite/cardwall/app.js:22
In our cases
55 cases use stagger. cardwall brings cards home from the center with each:.028, then gives the headline letters their own timing; the delay pattern also organizes visual hierarchy.
Used in 55 cases
01Momentum Slider02Drag Gallery03Formation04Cardwall05Riffle06Revolver07Rotunda09Lens Bloom10Frieze11Aperture12Folio15Hardback16Picasso Ecommerce17Assembly18Fathom19Emulsion20Portfolio Photographer21Reverie
+37 more
22Boutique23Nova24Alive25Shape Trail26Membrane27Liquid Type29Pixels30Cipher32Cutout33Crema34Aster35Spring37Amour38Fluid Gallery39Marathon43Pantheon48Moments49Burst50Dimension51Morph52Meow53Relay54Lumen55Serene56Fleur57Atrium59Helix60Crate61Prism62Flashlight63Viewfinder64Sand65Arrange66Tunnel67Reel68Type69Loupe
Reference implementation · basics.js
/**
* Delay for element i of n, GSAP/Motion style.
* each: seconds between neighbours · amount: total spread (wins over each)
* from: 'start' | 'center' | 'end' | 'edges' | 'random' | index · grid: [cols, rows] · ease: shapes the wave front
*/
export function stagger({ each = 0.05, amount, from = 'start', grid, axis, ease, seed = 1 } = {}) {
const cache = new Map();
return (i, n) => {
if (!cache.has(n)) {
const [cols, rows] = grid || [n, 1];
const origin = typeof from === 'number'
? [from % cols, Math.floor(from / cols)]
: from === 'end' ? [cols - 1, rows - 1]
: from === 'center' || from === 'edges' ? [(cols - 1) / 2, (rows - 1) / 2]
: [0, 0];
let d = Array.from({ length: n }, (_, k) => {
const dx = (k % cols) - origin[0], dy = Math.floor(k / cols) - origin[1];
return axis === 'x' ? Math.abs(dx) : axis === 'y' ? Math.abs(dy) : Math.hypot(dx, dy);
});
const max = Math.max(...d) || 1;
if (from === 'edges') d = d.map(v => max - v);
if (from === 'random') { const r = mulberry32(seed); d = d.map(() => r() * max); }
const unit = amount != null ? amount / max : each;
cache.set(n, d.map(v => (ease ? ease(v / max) * max : v) * unit));
}
return cache.get(n)[i];
};
}