// Motion Prompt #005 · MIT License (c) 2026 Shohei Ohtaki · https://showheyohtaki.github.io/motion-prompt/#005 // // Mask reveal / マスクリビール // Pack: motion (Motion graphics moves) · element 05 · Category: Entrances & text // What happens: Headline text slides out from behind an invisible edge marked by a single line. // Motion spec: // - One pass per 2.35 s. Divider line (coral, 380 × 6, y = 124): grows from the centre at 0.1–0.5 s (cubic ease-in-out), shrinks away at 2.05–2.3 s. // - Top text (bold 98 px, white): clipped to the area above the line (y 8–119); moves up into place from 120 px below at 0.35–1.0 s, quintic ease-out. // - Bottom text (32 px, yellow): clipped to the area below the line (y 129–181); moves down into place from 60 px above at 0.55–1.15 s, quintic ease-out. // - Exit: top text moves 150 px up at 1.7–2.05 s (backIn, s = 1.2, dipping slightly first); bottom text moves 70 px down at 1.75–2.05 s (cubic ease-in). // - Opacity never changes; the text is hidden only by leaving the clip area. // When to use it: Use it for titles and headings that should appear in a clean, composed way. // Tags: mask, reveal, clip, wipe, title, text, slide-up, headline // Size: 539x208 px · Cycle: 4.7 s // Needs: kit motion -> https://showheyohtaki.github.io/motion-prompt/api/v1/kit/motion.js // Live demo: https://showheyohtaki.github.io/motion-prompt/#005 // // cells[i](k, w, h, u): k = CanvasRenderingContext2D (already cleared and wrapped in save/restore by the caller), w,h = size, u = seconds in [0, cycle). The picture depends only on u. // Element code starts here. CELLS, constants and helpers live in the kit. // @element 05 // 05 マスクリビール(見えない枠=線の向こうから、見出しがせり上がる) CELLS[4] = (k, w, h, u) => { stage(k); const T = CYC / 2, lu = mod(u, T), X = w / 2, LY = 124; const lg = eIO(P(lu, 0.1, 0.5)) * (1 - eIO(P(lu, 2.05, 2.3))); // 上の段:線の下から上へ const up = (1 - eOut5(P(lu, 0.35, 1.0))) * 120 + backIn(P(lu, 1.7, 2.05), 1.2) * -150; k.save(); k.beginPath(); k.rect(0, 8, w, LY - 8 - 5); k.clip(); txt(k, 'Hello!', X, LY - 14 + up, `800 98px ${DISP}`, '#fff', 'center', 'alphabetic', '1px'); k.restore(); // 下の段:線の上から下へ const dn = (1 - eOut5(P(lu, 0.55, 1.15))) * -60 + eIn(P(lu, 1.75, 2.05)) * 70; k.save(); k.beginPath(); k.rect(0, LY + 5, w, 52); k.clip(); txt(k, 'はじめまして', X, LY + 44 + dn, `700 32px ${JP}`, C.yel, 'center', 'alphabetic', '6px'); k.restore(); if (lg > 0.005) { k.fillStyle = C.coral; rr(k, X - 190 * lg, LY - 3, 380 * lg, 6, 3); k.fill(); } };