mvoom
← all teardowns

Deck Gallery Gallery

196 elements fade — quick — when they enter the viewport — on their own clock, not tied to scroll position

original piece by

deck.gallery↗

#decks

Summary

origin
deck.gallery
selector
#decks
family
reveal
passes
rest, ground, motion, scrub, feel, interaction
read directly
100 %
WHAT WAS MEASURED

mechanism

2
distinct mechanisms. That is what actually has to be built, and what sets the price.
1
unique easing curve. Each one is a design decision to get right.
214
elements in motion. Scope raises the price, but in logarithm: repeating is not the expensive part.
THE CHARACTER OF THE MOTION
fastslow

2 mechanisms · 1 curve

one recipemany

2 distinct mechanisms

intimatecrowd

214 elements in motion

measuredinferred

all read directly from the engine

WHAT COULD NOT BE MEASURED

Nothing was left unexplained in this piece. What the engine cannot read — what happens inside a WebGL canvas — is declared opaque instead of invented.

The code that does this

Generated from the measurement, untouched by hand. One recipe per mechanism: the elements that move the same way share it, and their delay comes from their position.

1/**
2 * Gallery — las recetas medidas.
3 *
4 * Estos números salieron de la página, no de una estimación. Son el archivo que se toca para
5 * ajustar el movimiento: nada de aquí necesita entrar en el JSX.
6 *
7 * specVersion 1.0 · capturado a 60.7 fps
8 */
9
10export type Recipe = {
11 /** una receta por MECANISMO; el marcado la comparte con `data-mv` */
12 id: string;
13 trigger: string;
14 scrollRangePx?: [number, number];
15 /** `null` en un scrub: su tiempo es el scroll, no el reloj */
16 durationMs: number | null;
17 /**
18 * Un retraso por elemento, en el orden del marcado.
19 *
20 * Es lo único que distingue a los miembros de un escalonado, así que va como lista en vez
21 * de repetir la receta entera por cada uno. Si sólo hay un valor, lo comparten todos.
22 */
23 delaysMs: number[];
24 easing: string;
25 iterations: number;
26 fill: string;
27 keyframes: Record<string, number | string>[];
28 /** el estado final, para quien pidió no ver movimiento */
29 restingStyle: Record<string, number | string>;
30};
31
32export const RECIPES: Recipe[] = [
33 {
34 // img 05 — medido — Web Animations API · 186 elementos
35 id: "mech_01",
36 trigger: "viewport-enter",
37 durationMs: 300,
38 delaysMs: [0],
39 easing: "cubic-bezier(0.22, 1, 0.36, 1)",
40 iterations: 1,
41 fill: "backwards",
42 keyframes: [{"offset":0,"opacity":"0"},{"offset":1,"opacity":"1"}],
43 restingStyle: {"opacity":"1"},
44 },
45 {
46 // img 192 — medido — Web Animations API · 18 elementos
47 id: "mech_02",
48 trigger: "load",
49 durationMs: 300,
50 delaysMs: [0],
51 easing: "cubic-bezier(0.22, 1, 0.36, 1)",
52 iterations: 1,
53 fill: "backwards",
54 keyframes: [{"offset":0,"opacity":"0"},{"offset":1,"opacity":"1"}],
55 restingStyle: {"opacity":"1"},
56 },
57];
57 líneas · sin dependencias · Web Animations API