vaweGitHub

Video componentsChange between shots

Shape morph wipe

Change between shotsswiss1.5 s

shape-morph-wipe.md

Shape morph wipe: final frame of the transition animation
00:00.0000:01.50
Tap the clip to play it.

When to use

Use when the next shot should grow out of a shape already on screen (a status dot, an avatar, a button) and become the frame: the mask is born as that shape, grows as a circle, then widens and squares off into a rounded card and on past the frame edges. Unlike iris-wipe, the shape itself changes on the way. The detail that sells it is the order: size first, shape second. The circle stays round for the first third, then the width outruns the height and the corners tighten, so the viewer sees a circle turn into a card, not a box that was always a box.

The code

js
import '../../core/engine/page-api.js';
import { easeFn } from '../../core/motion/presets.js';
const E = easeFn('land'), W = innerWidth, H = innerHeight, clamp = (u) => Math.min(1, Math.max(0, u));
const whip = easeFn('swap');
const grow = (u) => 0.35 * u + 0.65 * whip(u), lerp = (p, q, u) => p + (q - p) * u;
const r = dot.getBoundingClientRect(), o = { x: r.left + r.width / 2, y: r.top + r.height / 2, d: r.width };
const T0 = 0.4, DUR = 0.85, PAD = 0.04 * H;                         // PAD: the box ends past every edge, so no corner shows shot A
vawe.onFrame((t) => {
  const u = clamp((t - T0) / DUR), g = grow(u);
  const h = lerp(o.d, H + 2 * PAD, g);                               // a circle of diameter h grows first
  const w = h + (W - H) * grow(clamp((u - 0.3) / 0.7));             // then it widens into the frame's shape
  const cx = lerp(o.x, W / 2, g), cy = lerp(o.y, H / 2, g);
  const rad = (Math.min(w, h) / 2) * lerp(1, 0.07, E(clamp((u - 0.35) / 0.55)));   // round, then a card corner
  next.style.visibility = t < T0 ? 'hidden' : 'visible';
  next.style.clipPath = `inset(${cy - h / 2}px ${W - cx - w / 2}px ${H - cy - h / 2}px ${cx - w / 2}px round ${rad}px)`;
  nextInner.style.transform = `scale(${lerp(1.12, 1, E(u))})`;       // the new shot settles inside the window
  prev.style.transform = `scale(${lerp(1, 0.94, g)})`;               // the old shot steps back under it
});

Easing

Each ease is a pair of After Effects handles from core/motion/presets.js. Every curve as CSS.

EaseLeaving a keyArriving at a key
land12% influence, speed 4.875% influence, speed 0
swap75% influence, speed 075% influence, speed 0

Notes

inset(... round r) is one clip shape that is a circle when the box is square and r is half its side, and a card when r is small, so one property carries the whole morph. Start from a shape at least 4 percent of the frame high, or the first third is invisible. The new shot’s content moves toward the viewer (1.12 to 1) while the old one moves away (1 to 0.94): two depths, one direction.

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

Sound

Sound: none; the shape grows from a dot the eye is already on, and the new shot’s own entrance takes the cue.

Chain it into a film.

Copy chains of video components from the recipes.