mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-04 16:42:27 +00:00
feat(registry): bring back the video-primitive moves (#3169)
Restores the 208 catalog items reverted after their previews 404'd in production, this time on the payload mechanism rather than the .html files that caused the outage. The generator no longer writes a preview document to docs/public. That writer, and the machinery under it, existed only to produce files the docs host discards, so it is gone rather than bypassed. Items now embed the composition itself via a payload, which is what the previous change already does for the items that were already in the catalog. The variables explorer is parked, not restored: it drove its preview through the same unpublished .html path, so it would have shown an empty frame. Items that declare variables get the live player plus the static variables table, and reconnecting the explorer to payloads is a follow-up.
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# svg-stroke-trace
|
||||
|
||||
An authored SVG path draws from start to finish using its measured `getTotalLength()`. Paths ending in a close command (`Z`) receive a restrained fill after the stroke completes; open paths (signatures, waves) stay stroke-only. The finished mark then holds until the frame cuts.
|
||||
|
||||
3.5s authored, elastic HOLD, exit `none` by default.
|
||||
|
||||
## Variables
|
||||
|
||||
| id | type | default | notes |
|
||||
| -------------- | ------ | ----------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| `path` | string | a wave path | SVG path data in the 1024x520 viewBox. A trailing `Z` is the single owner of fill behavior. |
|
||||
| `stroke_width` | number | `12` | Stroke width in viewBox units (2 to 32). |
|
||||
| `accent` | enum | `green` | Trace and fill 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 upward fade (the pre-Wave-J default ending). |
|
||||
|
||||
## Mount
|
||||
|
||||
```html
|
||||
<div
|
||||
class="clip"
|
||||
data-composition-id="svg-stroke-trace"
|
||||
data-composition-src="./svg-stroke-trace.html"
|
||||
data-variable-values='{"path":"M 120 400 L 512 120 L 904 400 Z","stroke_width":10,"accent":"blue"}'
|
||||
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 `svg-stroke-trace` key.
|
||||
|
||||
## Notes
|
||||
|
||||
- Dash values come from the real measured path length (never the `pathLength` attribute, never `non-scaling-stroke`), so any authored geometry draws exactly once.
|
||||
- The hold is one finite vertical drift cycle, never a repeating tween.
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
svg-stroke-trace demo. This standalone composition mounts the primitive
|
||||
through data-composition-src. A non-default closed logo path, wider stroke,
|
||||
and violet accent prove all three variables and the optional fill behavior.
|
||||
-->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>SVG Stroke Trace 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: 2.4cqmin;
|
||||
--space-1: 1.2cqmin;
|
||||
--space-2: 2.4cqmin;
|
||||
--space-3: 4cqmin;
|
||||
--dur-beat: 0.4s;
|
||||
--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% 46%,
|
||||
color-mix(in srgb, var(--surface) 88%, var(--accent-2)) 0%,
|
||||
var(--bg) 72%
|
||||
);
|
||||
}
|
||||
|
||||
.mount-card {
|
||||
width: 76%;
|
||||
height: 62%;
|
||||
left: 12%;
|
||||
top: 19%;
|
||||
container-type: size;
|
||||
overflow: hidden;
|
||||
border: 1px solid color-mix(in srgb, var(--border) 70%, transparent);
|
||||
border-radius: calc(var(--radius) * 1.35);
|
||||
background: color-mix(in srgb, var(--surface) 78%, transparent);
|
||||
box-shadow: 0 4cqh 12cqh color-mix(in srgb, var(--bg) 58%, transparent);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="root"
|
||||
data-composition-id="svg-stroke-trace-demo"
|
||||
data-start="0"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
data-duration="3.5"
|
||||
data-fps="30"
|
||||
>
|
||||
<div
|
||||
id="svg-stroke-trace-mount"
|
||||
class="mount-card clip"
|
||||
data-composition-id="svg-stroke-trace"
|
||||
data-composition-src="./svg-stroke-trace.html"
|
||||
data-variable-values='{"path":"M 170 378 L 330 132 L 458 330 L 584 132 L 830 378 L 680 378 L 584 248 L 508 378 L 408 378 L 330 248 L 252 378 Z","stroke_width":16,"accent":"violet"}'
|
||||
data-start="0"
|
||||
data-duration="3.5"
|
||||
data-track-index="0"
|
||||
data-width="1459"
|
||||
data-height="670"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["svg-stroke-trace-demo"] = gsap.timeline({ paused: true });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
|
||||
"name": "svg-stroke-trace",
|
||||
"type": "hyperframes:component",
|
||||
"title": "SVG Stroke Trace",
|
||||
"description": "An authored SVG path draws from its measured length, holds with subtle drift, and optionally fills when the path is closed.",
|
||||
"tags": ["motion-primitive", "intro", "reveal", "svg", "stroke", "line-draw"],
|
||||
"jobs": ["reveal"],
|
||||
"family": "intros-and-reveals",
|
||||
"profile": "trace",
|
||||
"evidence": "Video Primitives Wave I I5 svg-stroke-trace contract",
|
||||
"syncPoints": [{ "id": "trace-complete", "phase": "in", "offset": 1.55 }],
|
||||
"files": [
|
||||
{
|
||||
"path": "svg-stroke-trace.html",
|
||||
"target": "compositions/components/svg-stroke-trace.html",
|
||||
"type": "hyperframes:snippet"
|
||||
}
|
||||
],
|
||||
"variables": [
|
||||
{
|
||||
"id": "path",
|
||||
"type": "string",
|
||||
"role": "content",
|
||||
"label": "Path",
|
||||
"description": "SVG path data for the authored mark.",
|
||||
"default": "M 92 328 C 178 142 292 138 366 276 C 430 396 500 414 558 262 C 622 94 724 112 786 274 C 836 406 894 376 930 194"
|
||||
},
|
||||
{
|
||||
"id": "stroke_width",
|
||||
"type": "number",
|
||||
"role": "style",
|
||||
"label": "Stroke width",
|
||||
"description": "Width of the traced stroke in SVG viewBox units.",
|
||||
"default": 12,
|
||||
"min": 2,
|
||||
"max": 32,
|
||||
"step": 1
|
||||
},
|
||||
{
|
||||
"id": "accent",
|
||||
"type": "enum",
|
||||
"role": "style",
|
||||
"label": "Accent",
|
||||
"description": "Color used by the trace and optional fill.",
|
||||
"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 finished mark holds until the frame cuts.",
|
||||
"default": "none",
|
||||
"options": [
|
||||
{ "value": "none", "label": "None" },
|
||||
{ "value": "fade", "label": "Fade" },
|
||||
{ "value": "up", "label": "Up" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
svg-stroke-trace: HyperFrames video primitive (intros and reveals)
|
||||
|
||||
An authored SVG path draws from start to finish using its measured length.
|
||||
Closed paths receive a restrained fill after the stroke completes. Open
|
||||
paths remain stroke-only, which keeps signatures and waves clean.
|
||||
|
||||
Variables:
|
||||
- path (string): SVG path data for the traced mark.
|
||||
- stroke_width (number, default 12): stroke width in viewBox units.
|
||||
- accent (green | blue | violet, default green): trace and fill color.
|
||||
- exit (none | fade | up, default "none"): optional departure.
|
||||
|
||||
Envelope, fixed IN and OUT with elastic HOLD only:
|
||||
IN_BASE = 1.85s, stroke draw followed by optional closed-path fill
|
||||
HOLD = max(0, D - IN - OUT), one subtle vertical drift cycle
|
||||
OUT_BASE = 0.45s departure when exit is fade or up, 0s when exit is
|
||||
none (the default: the finished mark 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 svg-stroke-trace key.
|
||||
-->
|
||||
<html
|
||||
lang="en"
|
||||
data-composition-id="svg-stroke-trace"
|
||||
data-composition-duration="3.5"
|
||||
data-composition-variables='[
|
||||
{ "id": "path", "type": "string", "role": "content", "label": "Path", "description": "SVG path data for the authored mark.", "default": "M 92 328 C 178 142 292 138 366 276 C 430 396 500 414 558 262 C 622 94 724 112 786 274 C 836 406 894 376 930 194" },
|
||||
{ "id": "stroke_width", "type": "number", "role": "style", "label": "Stroke width", "description": "Width of the traced stroke in SVG viewBox units.", "default": 12, "min": 2, "max": 32, "step": 1 },
|
||||
{ "id": "accent", "type": "enum", "role": "style", "label": "Accent", "description": "Color used by the trace and optional fill.", "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 finished mark 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>SVG Stroke Trace</title>
|
||||
</head>
|
||||
<body>
|
||||
<template>
|
||||
<div id="root" data-composition-id="svg-stroke-trace" 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;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.sst-clip {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sst-stage {
|
||||
width: 88cqw;
|
||||
height: 70cqh;
|
||||
opacity: 0;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
.sst-svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.sst-fill,
|
||||
.sst-stroke {
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.sst-fill {
|
||||
fill: var(--sst-accent);
|
||||
stroke: none;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.sst-stroke {
|
||||
fill: none;
|
||||
stroke: var(--sst-accent);
|
||||
filter: drop-shadow(
|
||||
0 0 1.8cqmin color-mix(in srgb, var(--sst-accent) 34%, transparent)
|
||||
);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div
|
||||
id="svg-stroke-trace-clip"
|
||||
class="sst-clip clip"
|
||||
data-start="0"
|
||||
data-duration="3.5"
|
||||
data-track-index="0"
|
||||
>
|
||||
<div class="sst-stage" role="img">
|
||||
<svg
|
||||
class="sst-svg"
|
||||
viewBox="0 0 1024 520"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path class="sst-fill"></path>
|
||||
<path class="sst-stroke"></path>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script>
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var DEFAULT_PATH =
|
||||
"M 92 328 C 178 142 292 138 366 276 C 430 396 500 414 558 262 C 622 94 724 112 786 274 C 836 406 894 376 930 194";
|
||||
var root = document.getElementById("root");
|
||||
var stage = root.querySelector(".sst-stage");
|
||||
var fillPath = root.querySelector(".sst-fill");
|
||||
var strokePath = root.querySelector(".sst-stroke");
|
||||
var vars =
|
||||
window.__hyperframes && window.__hyperframes.getVariables
|
||||
? window.__hyperframes.getVariables()
|
||||
: {};
|
||||
|
||||
var suppliedPath = vars.path == null ? DEFAULT_PATH : String(vars.path).trim();
|
||||
var pathData = suppliedPath || DEFAULT_PATH;
|
||||
var widthValue = vars.stroke_width;
|
||||
var strokeWidth = Number.isFinite(widthValue)
|
||||
? Math.max(2, Math.min(32, widthValue))
|
||||
: 12;
|
||||
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";
|
||||
|
||||
root.style.setProperty("--sst-accent", accentColors[accent]);
|
||||
fillPath.setAttribute("d", pathData);
|
||||
strokePath.setAttribute("d", pathData);
|
||||
strokePath.setAttribute("stroke-width", String(strokeWidth));
|
||||
stage.setAttribute("aria-label", "Authored SVG path tracing from start to finish");
|
||||
|
||||
var pathLength = strokePath.getTotalLength();
|
||||
strokePath.style.strokeDasharray = pathLength + " " + pathLength;
|
||||
strokePath.style.strokeDashoffset = String(pathLength);
|
||||
|
||||
// A trailing close command is the single owner of fill behavior.
|
||||
// Open signatures and waves never receive an implied closing fill.
|
||||
var fillEnabled = /[zZ]\s*$/.test(pathData);
|
||||
|
||||
var IN_BASE = 1.85;
|
||||
var OUT_BASE = exit === "none" ? 0 : 0.45;
|
||||
var DRAW_SHARE = 0.84;
|
||||
var duration = Math.max(0.001, parseFloat(root.dataset.duration || "3.5"));
|
||||
var fixedTotal = Math.max(0.001, IN_BASE + OUT_BASE);
|
||||
var phaseScale = duration < fixedTotal ? duration / fixedTotal : 1;
|
||||
var IN = IN_BASE * phaseScale;
|
||||
var OUT = OUT_BASE * phaseScale;
|
||||
var DRAW = IN * DRAW_SHARE;
|
||||
var FILL = IN - DRAW;
|
||||
var HOLD = Math.max(0, duration - IN - OUT);
|
||||
var HOLD_START = IN;
|
||||
var OUT_START = IN + HOLD;
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
// IN: the path measurement owns both dash values, so every authored
|
||||
// path draws exactly once regardless of its geometry.
|
||||
tl.to(stage, { opacity: 1, duration: Math.min(0.28, DRAW), ease: "power2.out" }, 0);
|
||||
tl.to(strokePath, { strokeDashoffset: 0, duration: DRAW, ease: "sine.inOut" }, 0);
|
||||
|
||||
if (fillEnabled) {
|
||||
tl.to(fillPath, { opacity: 0.16, duration: FILL, ease: "power2.out" }, DRAW);
|
||||
}
|
||||
|
||||
// HOLD: one finite drift cycle keeps longer mounts alive.
|
||||
if (HOLD > 0) {
|
||||
tl.to(
|
||||
stage,
|
||||
{
|
||||
y: "-0.7cqh",
|
||||
duration: HOLD / 2,
|
||||
ease: "sine.inOut",
|
||||
yoyo: true,
|
||||
repeat: 1,
|
||||
},
|
||||
HOLD_START,
|
||||
);
|
||||
}
|
||||
|
||||
// OUT: optional departure; exit none holds until the frame cuts.
|
||||
if (exit === "up") {
|
||||
tl.to(
|
||||
stage,
|
||||
{ opacity: 0, y: "-2.4cqh", duration: OUT, ease: "power2.in" },
|
||||
OUT_START,
|
||||
);
|
||||
} else if (exit === "fade") {
|
||||
tl.to(stage, { opacity: 0, duration: OUT, ease: "power2.in" }, OUT_START);
|
||||
}
|
||||
|
||||
tl.seek(0);
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["svg-stroke-trace"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</div>
|
||||
</template>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user