vaweGitHub

MovesReveal a title

Anchor cycle

look: signal4.6 sanchor-cycle.md

Anchor cycle: final frame of the text reveal animation
Tap the clip to play it.

When to use

Use when the claim is "it works for everyone" or "everything changes, this stays". One phrase is pinned and never moves; a tape beside it is slapped over with a new word on each hard cut, slow at first and then faster until it is a flurry, and the last phrase holds. Then a brand lockup drops in below the still anchor. The stillness of the anchor is what the viewer reads. Clip: anchor-cycle.mp4. Demo: demo/anchor-cycle.html.

The code

css
.row { position: absolute; left: 14vh; top: 42%; translate: 0 -50%; width: max-content; display: flex; align-items: center; gap: 5vh; }
.anchor, .tape { flex: none; white-space: nowrap; text-transform: uppercase; }
.tape { padding: 1.6vh 5vh 1.2vh; border-radius: 4.6vh; background: var(--accent); color: var(--on-accent); }
.tape.b { background: var(--tile-alt); color: #fff; }   /* alternate two tones on the cuts */

Easing and timing

The clip runs 4.6 s. The look is signal.

The clip uses 2 named eases from core/motion/presets.js. Each is a pair of After Effects handles: influence is how far along the segment the handle reaches, and speed is how fast the value moves at the key, as a multiple of the segment's average speed. Motion in the docs explains both.

EaseLeaving a keyArriving at a key
land12% influence, speed 4.875% influence, speed 0
pop12% influence, speed 4.835% influence, speed -1.5

Notes

js
import '../../core/engine/page-api.js';
// the hold shrinks by about a fifth per swap: a roll call that turns into a flurry, then one held phrase
const states = ['Chefs', 'Pilots', 'Nurses', 'Teachers', 'Founders', 'Designers', 'Farmers', 'Every team'];
const holds = [0.55, 0.45, 0.36, 0.29, 0.23, 0.18, 0.14];   // seconds per state; the last one holds
const start = 0.85, edges = holds.reduce((a, h) => [...a, a[a.length - 1] + h], [start]);
const tilt = [-1.6, 1.2, -0.8, 1.6, -1.2, 0.8, -1.6];

vawe.onFrame((t) => {
  const i = t < start ? -1 : edges.filter((e) => t >= e).length - 1;   // the state index is a pure function of t
  tape.style.visibility = i < 0 ? 'hidden' : 'visible';
  if (i < 0) return;
  tape.textContent = states[i];                                         // a hard cut: no fade, no slide
  tape.classList.toggle('b', i % 2 === 1 && i < states.length - 1);
  tape.style.rotate = i === states.length - 1 ? '0deg' : `${tilt[i]}deg`;   // slapped on at a tilt, squared on the last
});
lock.animate([{ translate: '0 3vh', opacity: 0 }, { translate: '0 0', opacity: 1 }], { duration: 480, delay: 3850, easing: EASE.pop, fill: 'both' });

The anchor enters once, in the first 0.6 s, and then has no transform of any kind: no drift, no breathe. The tape grows to the right, away from it, and the row is sized for the longest state so nothing reaches the frame edge or touches the anchor. Changing the width of the tape on each cut is the point; do not tween it. Eight states read as "everyone", three read as a list. The flurry ends on the longest phrase and holds for about a second before the lockup joins, so the eye has a beat of quiet after the acceleration. Name the class .tape, not .chip: .chip is a fixed-size square in demo/demo.css.

Looks (ground, type, radius, accent) live in demo/demo.css; this snippet keeps neutral tokens.

Sound

Sound: none under the cycle; pluck at 3.85 s into the move, when the lockup lands (default gain).