mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-14 13:27:13 +00:00
Add an anime.js v4 anime_template (and anime_template_horizontal/vertical for
push-slide) alongside each gsap_template in the transition registry, vendored
identically into product-launch-video, pr-to-video, and faceless-explainer.
transitions.mjs now detects the target composition's runtime and stamps GSAP
onto window.__timelines["main"] or anime onto
hyperframesAnime.get("main").instance accordingly, defaulting to anime per the
anime-first contract. Anime templates use explicit [from, to] arrays,
decomposed transform keys, ms-based tokens, and percentage transformOrigin
pairs (a real Chrome anime.js 4.5.0 bug: keyword syntax silently resolves both
endpoints to (0,0)).
100 lines
6.7 KiB
JSON
100 lines
6.7 KiB
JSON
{
|
|
"_comment": "Vendored transition registry for the product-launch workflow — the curated Tier-B subset (transform/opacity/filter on the two frame clip wrappers #el-<id>, no overlay DOM, no per-frame cooperation). Each type carries a GSAP template and an equivalent anime.js v4 template; the transitions.mjs injector picks one per target composition's detected runtime and stamps it onto the master timeline (window.__timelines[\"main\"] for GSAP, hyperframesAnime.get(\"main\").instance for anime). Recipes originate from the shared catalog skills/hyperframes-animation/transitions/ (css-*.md) — keep in step if those change. Token placeholders the injector substitutes: __OLD__ (#el-<from>), __NEW__ (#el-<to>), __T__/__T_MS__ (overlap-start, seconds for GSAP / milliseconds for anime), __DUR__/__DUR_MS__ (this boundary's duration, seconds/ms), __DX__/__DXIN__ (horizontal travel + incoming offset, px — same for both engines), __DY__/__DYIN__ (vertical, px). anime templates use explicit [from, to] arrays (never an implicit from) because the boundary's start position __T_MS__ can be > 0, decompose transforms into anime's translateX/translateY/scale/scaleX keys (never a CSS `transform` string), and use tl.set(...) for immediate (non-tweened) property writes instead of a zero-duration tl.add(...). GSAP eases map to anime eases per packages/core/src/animation/easeMap.ts (power2 -> Cubic, power3 -> Quart family). transformOrigin values use percentage pairs (\"0% 50%\" / \"100% 50%\"), never CSS keyword syntax (\"left center\") — verified in real Chrome (anime.js 4.5.0) that keyword syntax silently resolves to (0,0) for both endpoints instead of parsing the keywords.",
|
|
"transitions": [
|
|
{
|
|
"name": "crossfade",
|
|
"energy": "any",
|
|
"default_duration_s": 0.5,
|
|
"directions": [],
|
|
"source": "css-dissolve.md",
|
|
"gsap_template": [
|
|
"tl.to(__OLD__, { opacity: 0, duration: __DUR__, ease: \"power2.inOut\" }, __T__);",
|
|
"tl.fromTo(__NEW__, { opacity: 0 }, { opacity: 1, duration: __DUR__, ease: \"power2.inOut\" }, __T__);"
|
|
],
|
|
"anime_template": [
|
|
"tl.add(__OLD__, { opacity: [1, 0], duration: __DUR_MS__, ease: \"inOutCubic\" }, __T_MS__);",
|
|
"tl.add(__NEW__, { opacity: [0, 1], duration: __DUR_MS__, ease: \"inOutCubic\" }, __T_MS__);"
|
|
]
|
|
},
|
|
{
|
|
"name": "blur-crossfade",
|
|
"energy": "calm",
|
|
"default_duration_s": 0.6,
|
|
"directions": [],
|
|
"source": "css-dissolve.md",
|
|
"note": "Default when the two frames' #root backgrounds differ a lot — the blur masks the background-color clash a plain crossfade would expose.",
|
|
"gsap_template": [
|
|
"tl.to(__OLD__, { filter: \"blur(10px)\", scale: 1.03, opacity: 0, duration: __DUR__, ease: \"power2.inOut\" }, __T__);",
|
|
"tl.fromTo(__NEW__, { filter: \"blur(10px)\", scale: 0.97, opacity: 0 }, { filter: \"blur(0px)\", scale: 1, opacity: 1, duration: __DUR__, ease: \"power2.inOut\" }, __T__);"
|
|
],
|
|
"anime_template": [
|
|
"tl.add(__OLD__, { filter: [\"blur(0px)\", \"blur(10px)\"], scale: [1, 1.03], opacity: [1, 0], duration: __DUR_MS__, ease: \"inOutCubic\" }, __T_MS__);",
|
|
"tl.add(__NEW__, { filter: [\"blur(10px)\", \"blur(0px)\"], scale: [0.97, 1], opacity: [0, 1], duration: __DUR_MS__, ease: \"inOutCubic\" }, __T_MS__);"
|
|
]
|
|
},
|
|
{
|
|
"name": "push-slide",
|
|
"energy": "medium",
|
|
"default_duration_s": 0.5,
|
|
"directions": ["LEFT", "RIGHT", "UP", "DOWN"],
|
|
"default_direction": "LEFT",
|
|
"source": "css-push.md",
|
|
"note": "Directional. The injector picks __DX__/__DY__ from the direction and emits the horizontal OR vertical pair (not both).",
|
|
"gsap_template_horizontal": [
|
|
"tl.to(__OLD__, { x: __DX__, duration: __DUR__, ease: \"power3.inOut\" }, __T__);",
|
|
"tl.fromTo(__NEW__, { x: __DXIN__, opacity: 1 }, { x: 0, duration: __DUR__, ease: \"power3.inOut\" }, __T__);"
|
|
],
|
|
"gsap_template_vertical": [
|
|
"tl.to(__OLD__, { y: __DY__, duration: __DUR__, ease: \"power3.inOut\" }, __T__);",
|
|
"tl.fromTo(__NEW__, { y: __DYIN__, opacity: 1 }, { y: 0, duration: __DUR__, ease: \"power3.inOut\" }, __T__);"
|
|
],
|
|
"anime_template_horizontal": [
|
|
"tl.add(__OLD__, { translateX: [0, __DX__], duration: __DUR_MS__, ease: \"inOutQuart\" }, __T_MS__);",
|
|
"tl.set(__NEW__, { opacity: 1 }, __T_MS__);",
|
|
"tl.add(__NEW__, { translateX: [__DXIN__, 0], duration: __DUR_MS__, ease: \"inOutQuart\" }, __T_MS__);"
|
|
],
|
|
"anime_template_vertical": [
|
|
"tl.add(__OLD__, { translateY: [0, __DY__], duration: __DUR_MS__, ease: \"inOutQuart\" }, __T_MS__);",
|
|
"tl.set(__NEW__, { opacity: 1 }, __T_MS__);",
|
|
"tl.add(__NEW__, { translateY: [__DYIN__, 0], duration: __DUR_MS__, ease: \"inOutQuart\" }, __T_MS__);"
|
|
]
|
|
},
|
|
{
|
|
"name": "zoom-through",
|
|
"energy": "high",
|
|
"default_duration_s": 0.4,
|
|
"directions": [],
|
|
"source": "css-scale.md",
|
|
"gsap_template": [
|
|
"tl.to(__OLD__, { scale: 2.5, opacity: 0, filter: \"blur(8px)\", duration: __DUR__, ease: \"power3.in\" }, __T__);",
|
|
"tl.fromTo(__NEW__, { scale: 0.5, opacity: 0, filter: \"blur(8px)\" }, { scale: 1, opacity: 1, filter: \"blur(0px)\", duration: __DUR__, ease: \"power3.out\" }, __T__);"
|
|
],
|
|
"anime_template": [
|
|
"tl.add(__OLD__, { scale: [1, 2.5], opacity: [1, 0], filter: [\"blur(0px)\", \"blur(8px)\"], duration: __DUR_MS__, ease: \"inQuart\" }, __T_MS__);",
|
|
"tl.add(__NEW__, { scale: [0.5, 1], opacity: [0, 1], filter: [\"blur(8px)\", \"blur(0px)\"], duration: __DUR_MS__, ease: \"outQuart\" }, __T_MS__);"
|
|
]
|
|
},
|
|
{
|
|
"name": "squeeze",
|
|
"energy": "medium",
|
|
"default_duration_s": 0.4,
|
|
"directions": [],
|
|
"source": "css-push.md",
|
|
"note": "Old compresses to a vertical line on the left edge; new expands from the right edge. Incoming starts off (scaleX 0) so its higher-track stacking is harmless.",
|
|
"gsap_template": [
|
|
"tl.to(__OLD__, { scaleX: 0, transformOrigin: \"left center\", duration: __DUR__, ease: \"power3.inOut\" }, __T__);",
|
|
"tl.fromTo(__NEW__, { scaleX: 0, transformOrigin: \"right center\", opacity: 1 }, { scaleX: 1, transformOrigin: \"right center\", duration: __DUR__, ease: \"power3.inOut\" }, __T__);"
|
|
],
|
|
"anime_template": [
|
|
"tl.set(__OLD__, { transformOrigin: \"0% 50%\" }, __T_MS__);",
|
|
"tl.add(__OLD__, { scaleX: [1, 0], duration: __DUR_MS__, ease: \"inOutQuart\" }, __T_MS__);",
|
|
"tl.set(__NEW__, { transformOrigin: \"100% 50%\", opacity: 1 }, __T_MS__);",
|
|
"tl.add(__NEW__, { scaleX: [0, 1], duration: __DUR_MS__, ease: \"inOutQuart\" }, __T_MS__);"
|
|
]
|
|
}
|
|
],
|
|
"default_high_energy": "zoom-through",
|
|
"default_calm": "blur-crossfade",
|
|
"max_duration_s": 2.0
|
|
}
|