Channel · 23
Text content
文字内容 · text-contentAnimate the characters or number itself while keeping the final meaning available to readers.
Text is an animation channel too: counters, indices and scramble reveals replace characters over time. Feeding every scrambled frame to a screen reader can trigger repeated announcements; changing digit widths can make nearby layout wobble. Separate the visual layer from the semantic result, then announce the final value once.
Formula
scramble(text, p, {seed,time})
index < p·length → final character
otherwise → seeded flicker character
counter = round(lerp(start, end, p))Parameters
| This page | Mature libraries | |
|---|---|---|
| progress | 0 → 1 | — |
| seed | 7 | —The same progress and time give the same characters. |
| digits | tabular digits toggle | — |
| announcement | announce final value once | GSAP SplitText aria: auto labels the original text |
Doing it well
- Hide the frame-by-frame visual characters from assistive technology and keep the original phrase as the container label; announce completion once. GSAP SplitText's default
aria: autoplaces the original text in the parent'saria-label.GSAP src/SplitText.ts:355, 373 - Use
font-variant-numeric: tabular-numsso changing digits occupy equal widths; the site's dimension labels use it too.www-motion/rewrite/dimension/index.html:64,70 - Lock revealed characters to the source phrase and flicker only the unrevealed tail. Scrubbing backward and forward then remains understandable.
- Update a counter when its value changes.
cipherties its section count to the active panel rather than rebuilding the whole text tree.www-motion/rewrite/cipher/app.js:4
In our cases
51 studies change text content. cipher updates the active section count, and formation replaces a detail panel's title, client and year when it opens. These change content itself, unlike simply moving a text element.
Used in 51 cases
Reference implementation · basics.js
/** Deterministic scramble: characters left of the reveal front are final, the rest flicker. */
export function scramble(text, progress, { chars = '!<>-_\\/[]{}—=+*^?#', seed = 7, time = 0 } = {}) {
const front = progress * text.length;
let out = '';
for (let i = 0; i < text.length; i++) {
if (i < front || text[i] === ' ') { out += text[i]; continue; }
const r = mulberry32(seed + i * 31 + Math.floor(time * 20))();
out += chars[Math.floor(r * chars.length)];
}
return out;
}