vaweGitHub

MovesReveal a title

Vertical cut reveal

look: swiss2.4 svertical-cut-reveal.md

Vertical cut reveal: final frame of the text reveal animation
Tap the clip to play it.

When to use

Use when a line of type must arrive hard-edged and fast: every character (or word, or line) rises from under its own invisible edge, one after another, and the stagger can start at the first item, the last, the middle or any index. It is a port of the vertical-cut-reveal component of fancy, so the numbers below are the original's. Clip: vertical-cut-reveal.mp4. Demo: demo/vertical-cut-reveal.html.

The function is in the demo page (about 30 lines): copy it with the CSS. Time is the seek: there is no trigger, at is the second the animation starts.

The code

css
.vcr { display: flex; flex-wrap: wrap; white-space: pre-wrap; }
.vcr.lines { flex-direction: column; }
.vcr-word { display: inline-flex; overflow: hidden; }       /* the clip box: one per word */
.vcr-char { position: relative; white-space: pre-wrap; }
.vcr-glyph { display: inline-block; }                       /* the part that moves */

Easing and timing

The clip runs 2.4 s. The look is swiss.

Notes

js
import { motionSpring } from '../../core/motion/motion-spring.js';
// verticalCutReveal(el, text, { splitBy, staggerFrom, stagger, reverse, spring, delay, at, seed }) returns frame(t)
const spring = { stiffness: 200, damping: 21 };
const lines = [
  verticalCutReveal(a, 'Cut from below,', { splitBy: 'characters', stagger: 0.025, spring, at: 0.1 }),
  verticalCutReveal(b, 'then from above,', { splitBy: 'characters', stagger: 0.025, staggerFrom: 'last', reverse: true, delay: 0.5, spring, at: 0.1 }),
];
vawe.onFrame((t) => lines.forEach((f) => f(t)));
// inside: y = (reverse ? -100 : 100) * (1 - motionSpring(spring, 100)(t - at - delay - itemDelay)), as translateY(y%)

The numbers (the original's defaults)

  • Spring: stiffness 190, damping 22, mass 1 by default; the demo uses 200 and 21. The spring is the motion library's, in core/motion/motion-spring.js: it settles in about 0.5 s and overshoots by under 1 percent.
  • staggerDuration 0.2 s by default (words). For splitBy: 'characters' use 0.025 s.
  • staggerFrom: first, last, center, random or an index. Center is floor(total / 2), and total counts the spaces between words, but the item index does not: for characters the centre sits about one item to the right of the true middle. Random draws one centre per item from the seeded seed.
  • reverse starts each item at -100% (it falls into place) instead of 100%.
  • Structure: one overflow: hidden box per word, one inline-block glyph inside it. The box is the line height, so a tight line-height clips descenders. Give the line 1.1 or more.
  • splitBy: characters, words, lines (the container becomes a column) or any separator string.
  • delay adds to every item (the original's transition.delay).

Fidelity

Checked against the live component at 60 fps with a virtual clock. The y of every glyph matches the original to 0.2 percent of the line height, and each line's layout box matches to 0.06 px. The same frames match to an SSIM of 0.98 once the page's own buttons are masked; the rest is font smoothing.

The common mistake

Moving the clip box instead of the glyph, or clipping the whole line with one mask. The cut is per word: the glyph slides inside its own box, so words that have not started are invisible while the others already stand. For one mask over a whole line use mask-rise.

Credit: port of fancy by Daniel Petho, MIT, https://github.com/danielpetho/fancy (component vertical-cut-reveal, https://www.fancycomponents.dev/docs/components/text/vertical-cut-reveal).

Sound

Sound: none; a type reveal has no event to mark.