Revert "feat(registry): the video-primitive moves, documented and customisable (#3090)" (#3162)

This reverts commit 3b53bfd2f7.
This commit is contained in:
Miguel Ángel
2026-08-10 14:47:47 -04:00
committed by GitHub
parent 3b53bfd2f7
commit c86d4013f5
2328 changed files with 5906 additions and 458525 deletions
@@ -1,36 +0,0 @@
# per-word-rise
Words or characters rise into place in a controlled blur-to-sharp cascade, settle with a short decel tail on landing, and hold truly still until the frame cuts.
3.5s authored, elastic HOLD, exit `none` by default.
## Variables
| id | type | default | notes |
| -------- | ------ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `text` | string | `WORDS IN MOTION` | The displayed line. Font size auto-fits to character count. |
| `split` | enum | `word` | `word` or `char`: the unit that rises. |
| `cues` | string | `` (empty) | Comma-separated seconds (from mount start) for each unit's landing, e.g. `0.4,0.8,1.6`. Units beyond the list extrapolate at the list's own gap. Empty keeps the authored cascade. |
| `accent` | enum | `green` | Text color: green rides `--brand`, blue rides `--accent`, violet rides `--accent-2`. |
| `exit` | enum | `none` | `none` holds until the cut; `fade` departs opacity-only; `up` is the staggered upward fade (the pre-Wave-J default ending). |
## Mount
```html
<div
class="clip"
data-composition-id="per-word-rise"
data-composition-src="./per-word-rise.html"
data-variable-values='{"text":"SHIP IT TODAY","split":"word","cues":"0.5,1.0,1.5"}'
data-start="0"
data-duration="3.5"
data-track-index="0"
></div>
```
Elastic root: no `data-width`/`data-height`; it fills whatever box the host clip gives it. Timeline registers under the literal `per-word-rise` key.
## Notes
- Cues are landing times: each unit's rise ends on its cue (start compresses toward 0 for early cues). Landings clamp ahead of any exit.
- Deterministic and seek-safe; the hold is truly still (no drift), and every landing ends with a short overshoot-free settle tail instead of a dead stop.
-103
View File
@@ -1,103 +0,0 @@
<!doctype html>
<!--
per-word-rise demo. This standalone composition mounts per-word-rise.html
through data-composition-src. Non-default text, split, and accent values
prove the variable path while the primitive remains elastic to its host.
-->
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Per Word Rise Demo</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html,
body {
width: 1920px;
height: 1080px;
overflow: hidden;
background: var(--bg, #0b0c0e);
}
#root {
--bg: #0b0c0e;
--fg: #f8fafc;
--muted: #94a3b8;
--surface: #14171c;
--border: #343a46;
--brand: #71f5a7;
--accent: #61a8ff;
--accent-2: #c5a3ff;
--font-display: Inter, system-ui, sans-serif;
--font-body: Inter, system-ui, sans-serif;
--font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
--radius: 3cqmin;
--space-1: 1.8cqw;
--space-2: 2.8cqh;
--space-3: 4cqh;
--dur-beat: 0.5s;
--ease-standard: cubic-bezier(0.215, 0.61, 0.355, 1);
--ease-emphasis: cubic-bezier(0.19, 1, 0.22, 1);
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: radial-gradient(
ellipse at 50% 42%,
color-mix(in srgb, var(--surface) 88%, var(--accent-2)) 0%,
var(--bg) 72%
);
}
.mount-card {
width: 76%;
height: 50%;
left: 12%;
top: 25%;
container-type: size;
overflow: hidden;
border: 1px solid color-mix(in srgb, var(--border) 72%, transparent);
border-radius: calc(var(--radius) * 1.3);
background: var(--bg);
box-shadow: 0 4cqh 12cqh color-mix(in srgb, var(--bg) 68%, transparent);
}
</style>
</head>
<body>
<div
id="root"
data-composition-id="per-word-rise-demo"
data-start="0"
data-width="1920"
data-height="1080"
data-duration="3.5"
data-fps="30"
>
<div
id="per-word-rise-mount"
class="mount-card clip"
data-composition-id="per-word-rise"
data-composition-src="./per-word-rise.html"
data-variable-values='{"text":"EVERY FRAME MATTERS","split":"char","accent":"violet"}'
data-start="0"
data-duration="3.5"
data-track-index="0"
data-width="1459"
data-height="540"
></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
var tl = gsap.timeline({ paused: true });
tl.seek(0);
window.__timelines = window.__timelines || {};
window.__timelines["per-word-rise-demo"] = tl;
</script>
</body>
</html>
-316
View File
@@ -1,316 +0,0 @@
<!doctype html>
<!--
per-word-rise: HyperFrames video primitive (text effects / reveal)
Words, or characters through the split variable, rise into place with a
controlled cascade and a small blur-to-sharp settle. Each unit lands with
a short overshoot-free settle tail (the final few pixels glide in over the
closing frames instead of stopping dead), then the finished line holds
truly still until the frame cuts (exit none) or departs with the optional
exit variable.
Variables:
- text (string, default "WORDS IN MOTION"): displayed text.
- split (word | char, default "word"): animated unit size.
- cues (string, default ""): comma-separated seconds (from mount start)
for each unit's landing. Empty keeps the authored cascade rhythm.
- accent (green | blue | violet, default "green"): text color.
- exit (none | fade | up, default "none"): optional departure.
Envelope, fixed IN and OUT with elastic HOLD only:
IN_BASE = 1.45s, the full staggered rise and sharp settle (cues can
stretch the rise phase past IN_BASE when they land later)
HOLD = max(0, D - IN - OUT), truly still: no drift
OUT_BASE = 0.50s staggered departure when exit is fade or up, 0s when
exit is none (the default: the line holds until the cut)
If D is shorter than IN_BASE + OUT_BASE, IN and OUT compress together.
The timeline is never time-scaled.
Mount contract: the runtime clones only this template. #root fills the host
box, establishes the container query basis, has no data-width or data-height,
and registers one paused timeline under the literal per-word-rise key.
-->
<html
lang="en"
data-composition-id="per-word-rise"
data-composition-duration="3.5"
data-composition-variables='[
{ "id": "text", "type": "string", "role": "content", "label": "Text", "description": "Text whose words or characters rise into place.", "default": "WORDS IN MOTION" },
{ "id": "split", "type": "enum", "role": "content", "label": "Split", "description": "Animate the text as words or individual characters.", "default": "word", "options": [{ "value": "word", "label": "Word" }, { "value": "char", "label": "Character" }] },
{ "id": "cues", "type": "string", "role": "timing", "label": "Cues", "description": "Comma-separated seconds (from mount start) for each unit landing. Empty keeps the authored cascade rhythm.", "default": "" },
{ "id": "accent", "type": "enum", "role": "style", "label": "Accent", "description": "Contract accent token used for the text color.", "default": "green", "options": [{ "value": "green", "label": "Green" }, { "value": "blue", "label": "Blue" }, { "value": "violet", "label": "Violet" }] },
{ "id": "exit", "type": "enum", "role": "timing", "label": "Exit", "description": "Optional departure. Default none: the line holds until the frame cuts.", "default": "none", "options": [{ "value": "none", "label": "None" }, { "value": "fade", "label": "Fade" }, { "value": "up", "label": "Up" }] }
]'
>
<head>
<meta charset="UTF-8" />
<title>Per Word Rise</title>
</head>
<body>
<template>
<div id="root" data-composition-id="per-word-rise" data-duration="3.5" data-fps="30">
<style>
*,
*::before,
*::after {
box-sizing: border-box;
}
#root {
position: absolute;
inset: 0;
overflow: hidden;
container-type: size;
isolation: isolate;
color: var(--fg, #f8fafc);
font-family: var(--font-display, Inter, system-ui, sans-serif);
pointer-events: none;
}
.pwr-clip {
position: absolute;
inset: 0;
display: grid;
place-items: center;
overflow: hidden;
background: var(--bg, #0b0c0e);
}
.pwr-stage {
display: flex;
width: 88cqw;
max-width: 88cqw;
align-items: baseline;
justify-content: center;
flex-wrap: wrap;
column-gap: var(--space-1, 1.8cqw);
row-gap: var(--space-2, 2.8cqh);
color: var(--pwr-accent);
font-size: var(--pwr-font-size);
font-weight: 780;
line-height: 1.08;
letter-spacing: -0.035em;
text-align: center;
}
.pwr-stage.pwr-char {
column-gap: 0.045em;
letter-spacing: 0;
}
.pwr-unit {
display: inline-block;
flex: 0 0 auto;
filter: blur(var(--pwr-blur, 0cqh));
will-change: transform, filter, opacity;
}
.pwr-space {
width: 0.36em;
}
</style>
<div
id="per-word-rise-clip"
class="pwr-clip clip"
data-start="0"
data-duration="3.5"
data-track-index="0"
>
<div class="pwr-stage" role="img"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<script>
(function () {
"use strict";
var root = document.getElementById("root");
var stage = root.querySelector(".pwr-stage");
var vars =
window.__hyperframes && window.__hyperframes.getVariables
? window.__hyperframes.getVariables()
: {};
var text = vars.text == null ? "WORDS IN MOTION" : String(vars.text);
var split = vars.split === "char" ? "char" : "word";
var accentColors = {
green: "var(--brand, #71f5a7)",
blue: "var(--accent, #61a8ff)",
violet: "var(--accent-2, #c5a3ff)",
};
var accent = Object.prototype.hasOwnProperty.call(accentColors, vars.accent)
? vars.accent
: "green";
var exit = vars.exit === "fade" || vars.exit === "up" ? vars.exit : "none";
var cuesValue = vars.cues == null ? "" : String(vars.cues);
var cues = cuesValue.split(",").reduce(function (result, token) {
var trimmed = token.trim();
if (trimmed === "") return result;
var seconds = Number(trimmed);
if (!Number.isFinite(seconds) || seconds < 0) return result;
result.push(seconds);
return result;
}, []);
root.style.setProperty("--pwr-accent", accentColors[accent]);
stage.classList.toggle("pwr-char", split === "char");
stage.setAttribute("aria-label", text);
var characterCount = Math.max(1, Array.from(text).length);
var fittedCqw = Math.min(9.5, 126 / characterCount);
root.style.setProperty(
"--pwr-font-size",
"min(" + fittedCqw.toFixed(3) + "cqw, 15cqh)",
);
var segments = split === "char" ? Array.from(text) : text.trim().split(/\s+/);
if (segments.length === 0) segments = [""];
var units = segments.map(function (segment, index) {
var unit = document.createElement("span");
unit.id = "per-word-rise-unit-" + index;
unit.className = "pwr-unit";
if (split === "char" && /\s/.test(segment)) {
unit.classList.add("pwr-space");
unit.textContent = "\u00a0";
} else {
unit.textContent = segment;
}
unit.setAttribute("aria-hidden", "true");
stage.appendChild(unit);
return unit;
});
var IN_BASE = 1.45;
var OUT_BASE = exit === "none" ? 0 : 0.5;
var RISE_BASE = 0.74;
var STAGGER_BASE = 0.62;
var EXIT_STAGGER_BASE = 0.14;
var duration = Math.max(0.001, parseFloat(root.dataset.duration || "3.5"));
var totalBase = Math.max(0.001, IN_BASE + OUT_BASE);
var scale = duration < totalBase ? duration / totalBase : 1;
var IN = IN_BASE * scale;
var OUT = OUT_BASE * scale;
var RISE = RISE_BASE * scale;
var STAGGER = units.length > 1 ? STAGGER_BASE * scale : 0;
var EXIT_STAGGER = units.length > 1 ? EXIT_STAGGER_BASE * scale : 0;
var EXIT_MOVE = Math.max(0.001, OUT - EXIT_STAGGER);
// Landing ease: overshoot-free decel with a settle tail. A plain
// power3.out slams from fast to imperceptible in ~2 frames and the
// words stop dead; this piecewise cubic keeps ~7% of the travel for
// the last 22% of the duration and is C1-continuous at the seam,
// so the word glides its final few pixels over the closing frames
// (a settle) with monotonic velocity: no stall, no hop, no
// overshoot. Pure function of progress, so it stays seek-safe.
var LAND_SPLIT = 0.78; // progress where the settle tail begins
var LAND_RESIDUAL = 0.07; // fraction of travel left for the tail
var LAND_SEAM_SLOPE = ((1 - LAND_RESIDUAL) / LAND_SPLIT) * 0.5; // shared velocity at the seam
function landEase(p) {
if (p <= 0) return 0;
if (p >= 1) return 1;
if (p < LAND_SPLIT) {
var x = p / LAND_SPLIT;
var inv = 1 - x;
// Half cubic-out, half linear: keeps end velocity for the seam.
return (1 - LAND_RESIDUAL) * (0.5 * (1 - inv * inv * inv) + 0.5 * x);
}
var u = (p - LAND_SPLIT) / (1 - LAND_SPLIT);
var b = LAND_SEAM_SLOPE * (1 - LAND_SPLIT);
var c = 3 * LAND_RESIDUAL - 2 * b;
var d = b - 2 * LAND_RESIDUAL;
return 1 - LAND_RESIDUAL + b * u + c * u * u + d * u * u * u;
}
// Cues own the word landings when provided; the authored cascade
// otherwise. Landings beyond the cue list extrapolate at the cue
// list's own gap, and every landing clamps ahead of any exit.
var riseEnd = IN;
var landings;
if (cues.length > 0) {
var lastCue = cues[cues.length - 1];
var cueGap =
cues.length >= 2
? Math.max(0.05, (lastCue - cues[0]) / (cues.length - 1))
: RISE * 0.4;
var latestLanding = Math.max(RISE * 0.5, duration - OUT);
landings = units.map(function (unit, index) {
var landing =
index < cues.length
? cues[index]
: lastCue + (index - (cues.length - 1)) * cueGap;
return Math.min(latestLanding, Math.max(0.05, landing));
});
riseEnd = Math.min(duration - OUT, Math.max(IN, Math.max.apply(null, landings)));
} else {
// Authored cascade: same left-to-right wave the stagger drew,
// expressed as explicit landings so every unit gets the settle.
var step = units.length > 1 ? STAGGER / (units.length - 1) : 0;
landings = units.map(function (unit, index) {
return index * step + RISE;
});
}
var HOLD_START = riseEnd;
var HOLD = Math.max(0, duration - (HOLD_START + OUT));
var OUT_START = HOLD_START + HOLD;
gsap.set(units, { opacity: 0, y: "8cqh", "--pwr-blur": "1.4cqh" });
var tl = gsap.timeline({ paused: true });
units.forEach(function (unit, index) {
var landing = landings[index];
var startAt = Math.max(0, landing - RISE);
tl.to(
unit,
{
opacity: 1,
y: "0cqh",
"--pwr-blur": "0cqh",
duration: Math.max(0.08, landing - startAt),
ease: landEase,
},
startAt,
);
});
// TRUE hold: the finished line is dead still until the exit or
// the cut. The old 0.55cqh drift was sub-perceptual at 1080p.
// OUT: optional departure; exit none holds until the frame cuts.
if (exit === "up") {
tl.to(
units,
{
opacity: 0,
y: "-4.5cqh",
"--pwr-blur": "0.7cqh",
duration: EXIT_MOVE,
stagger: { amount: EXIT_STAGGER, from: "start" },
ease: "power2.in",
},
OUT_START,
);
} else if (exit === "fade") {
tl.to(
units,
{
opacity: 0,
duration: EXIT_MOVE,
stagger: { amount: EXIT_STAGGER, from: "start" },
ease: "power2.in",
},
OUT_START,
);
}
tl.seek(0);
window.__timelines = window.__timelines || {};
window.__timelines["per-word-rise"] = tl;
})();
</script>
</div>
</template>
</body>
</html>
@@ -1,76 +0,0 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "per-word-rise",
"type": "hyperframes:component",
"title": "Per Word Rise",
"description": "Words or characters rise into place in a controlled blur-to-sharp cascade, settle softly on landing, and hold still until the cut (optional fade or up exit).",
"tags": ["motion-primitive", "text-effects", "typography", "stagger", "reveal"],
"jobs": ["reveal"],
"family": "text-effects",
"profile": "stagger",
"evidence": "Wave I I1 approved contract build, merging the per-word and SplitTypeReveal candidates",
"syncPoints": [{ "id": "rise-settle", "phase": "in", "offset": 1.45 }],
"files": [
{
"path": "per-word-rise.html",
"target": "compositions/components/per-word-rise.html",
"type": "hyperframes:snippet"
}
],
"variables": [
{
"id": "text",
"type": "string",
"role": "content",
"label": "Text",
"description": "Text whose words or characters rise into place.",
"default": "WORDS IN MOTION"
},
{
"id": "split",
"type": "enum",
"role": "content",
"label": "Split",
"description": "Animate the text as words or individual characters.",
"default": "word",
"options": [
{ "value": "word", "label": "Word" },
{ "value": "char", "label": "Character" }
]
},
{
"id": "cues",
"type": "string",
"role": "timing",
"label": "Cues",
"description": "Comma-separated seconds (from mount start) for each unit landing. Empty keeps the authored cascade rhythm.",
"default": ""
},
{
"id": "accent",
"type": "enum",
"role": "style",
"label": "Accent",
"description": "Contract accent token used for the text color.",
"default": "green",
"options": [
{ "value": "green", "label": "Green" },
{ "value": "blue", "label": "Blue" },
{ "value": "violet", "label": "Violet" }
]
},
{
"id": "exit",
"type": "enum",
"role": "timing",
"label": "Exit",
"description": "Optional departure. Default none: the line holds until the frame cuts.",
"default": "none",
"options": [
{ "value": "none", "label": "None" },
{ "value": "fade", "label": "Fade" },
{ "value": "up", "label": "Up" }
]
}
]
}