From 3e98e2b07036675346f2df1d19d2df33824cbf4d Mon Sep 17 00:00:00 2001 From: youngeuibae <127659562+youngeuibae@users.noreply.github.com> Date: Thu, 6 Aug 2026 17:23:42 +0900 Subject: [PATCH] docs(skills): fix anime.js v3 syntax in v4 adapter guidance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The animejs adapter docs teach v3 syntax against a v4 build, so the examples cannot run as written: every v4 bundle assigns a *namespace object* to the global `anime`, making v3's `anime({ targets })` call form a TypeError. `easing:` is now `ease:`, ease names lost their `ease` prefix, and `timeline.add()` takes `(targets, parameters, position)`. - Rewrite `skills/hyperframes-animation/adapters/animejs.md` for v4: `anime.animate()` / `anime.createTimeline()`, `ease:` names, targets-first `add()`, position shorthands, and a note that the producer fixtures pin 4.0.2 (`lib/`) while 4.1+ moved bundles to `dist/bundles/`. - Fix the same v3 `anime.timeline({ targets })` snippet in `skills/hyperframes-keyframes/references/keyframe-patterns.md`. - Stop advertising `anime.running` auto-discovery as a safety net. No v4 build exports `running` (checked 4.0.2 and 4.5.0), so `discover()` returns immediately and any instance a composition forgets to push onto `window.__hfAnime` is silently never seeked. Marked v3-only/inert in the skill page and the adapter docstring; explicit registration is now stated as mandatory. - Add render-safety notes the page lacked: `createSeededRandom()` as the deterministic replacement for `Math.random()`, and why `autoplay: onScroll(...)`, `createDraggable`, and pointer-driven `createAnimatable` cannot work under headless seek rendering. - Regenerate skills-manifest.json. Runtime behaviour is unchanged: the `packages/core` edit is comment-only, and the seek path already works on v4 (registered instances expose seek/pause/play). The now-dead `anime.running` branch in `discover()` is left in place — it is guarded and try/caught, and removing it is a behaviour change that belongs in its own PR. --- packages/core/src/runtime/adapters/animejs.ts | 27 +++--- skills-manifest.json | 4 +- .../hyperframes-animation/adapters/animejs.md | 84 +++++++++++-------- .../references/keyframe-patterns.md | 6 +- 4 files changed, 72 insertions(+), 49 deletions(-) diff --git a/packages/core/src/runtime/adapters/animejs.ts b/packages/core/src/runtime/adapters/animejs.ts index 5e4986ad25..2d8f6d9e96 100644 --- a/packages/core/src/runtime/adapters/animejs.ts +++ b/packages/core/src/runtime/adapters/animejs.ts @@ -8,14 +8,18 @@ import { swallow } from "../diagnostics"; * * ## Usage in a composition * + * The v4 global `anime` is a namespace object, not a callable — `anime.animate()`, + * `anime.createTimeline()`. v3's `anime({ targets })` form does not exist in v4. + * (4.1+ also moved the bundles from `lib/` to `dist/bundles/`.) + * * ```html - * + * * * ``` * - * Timelines work the same way: + * Timelines work the same way — note `add(targets, params, position)`: * * ```html * @@ -37,10 +41,11 @@ import { swallow } from "../diagnostics"; * * Multiple instances are supported — all are seeked in sync. * - * ## Auto-discovery + * ## Auto-discovery (v3 only — inert on v4) * - * The adapter also checks `anime.running` for active instances - * (useful for compositions that forget to register manually). + * `discover()` checks `anime.running`, which v4 no longer exports, so it always + * returns empty against a v4 build. Compositions MUST push every instance onto + * `window.__hfAnime` themselves; an unregistered instance is never seeked. */ export function createAnimeJsAdapter(): RuntimeDeterministicAdapter { return { diff --git a/skills-manifest.json b/skills-manifest.json index cb7dbe3e27..71cb77d671 100644 --- a/skills-manifest.json +++ b/skills-manifest.json @@ -22,7 +22,7 @@ "files": 17 }, "hyperframes-animation": { - "hash": "5bc2ce098387a547", + "hash": "2ce5ca7dbf361e27", "files": 121 }, "hyperframes-cli": { @@ -38,7 +38,7 @@ "files": 78 }, "hyperframes-keyframes": { - "hash": "a568c05c01b27461", + "hash": "d00744ff0e669624", "files": 3 }, "hyperframes-registry": { diff --git a/skills/hyperframes-animation/adapters/animejs.md b/skills/hyperframes-animation/adapters/animejs.md index 5d108f94ab..1f1b8f9793 100644 --- a/skills/hyperframes-animation/adapters/animejs.md +++ b/skills/hyperframes-animation/adapters/animejs.md @@ -7,28 +7,39 @@ description: Anime.js adapter patterns for HyperFrames. Use when writing Anime.j HyperFrames can seek Anime.js instances through its `animejs` runtime adapter. The composition owns the animation objects; HyperFrames owns the clock. +**This page targets v4 (examples pinned to 4.5.0, MIT).** v4 is a hard break from v3 — there is no callable `anime()`, `easing:` is now `ease:`, and ease names lost their `ease` prefix. Writing v3 from memory produces a composition that throws or silently animates nothing. + +The repo's own producer fixtures pin `animejs@4.0.2/lib/anime.iife.min.js`, which still resolves — but that build predates `splitText` / `scrambleText` / `createSeededRandom` / `createLayout` used below, and 4.1+ moved the bundles to `dist/bundles/`, so a version bump needs the path changed too. + ## Contract - Create animations or timelines synchronously during composition initialization. - Set `autoplay: false` so Anime.js does not advance on its own clock. -- Register every returned animation or timeline on `window.__hfAnime`. +- Register every returned animation or timeline on `window.__hfAnime` — **explicitly. There is no working auto-discovery on v4** (see Avoid). - Use finite durations and loop counts. - Avoid callbacks that mutate DOM based on wall-clock time, network state, or unseeded randomness. -The adapter seeks every registered instance with `instance.seek(timeMs)`, where `timeMs` is HyperFrames time in milliseconds. +The adapter seeks every registered instance with `instance.seek(timeMs)`, where `timeMs` is HyperFrames time **in milliseconds** (`ctx.time` seconds × 1000). It also calls `pause()` and `play()` on each instance; anything exposing those three methods works, whatever created it. + +## Loading v4 + +```html + + +``` + +`anime.animate(...)`, `anime.createTimeline(...)`, `anime.utils.*`, `anime.svg.*`, `anime.stagger(...)`. **Calling `anime(...)` is a TypeError** — every v4 build (UMD and IIFE alike) assigns a namespace object to the global, so v3's `anime({ targets })` form cannot work no matter which v4 file you load. ## Basic Pattern ```html - ``` +Position accepts a number, a label, `"+=250"` / `"-=100"`, `"<"` (previous **end**) and `"<<"` (previous **start**). + ## Module Builds -If you use an ES module build, the adapter does not care how the instance was created. It only needs the returned object to expose `seek()`, `pause()`, and preferably `play()`: +The adapter does not care how the instance was created — only that it exposes `seek()`, `pause()`, and `play()`: ```html ``` +## Determinism + +v4 ships `createSeededRandom(seed)` — use it instead of `Math.random()` when a composition needs scatter/jitter, so the same frame renders the same on every pass: + +```js +const rnd = anime.createSeededRandom(1337); +anime.animate(".dot", { y: () => -40 * rnd(), duration: 800, autoplay: false }); +``` + +`anime.utils.random()` / `randomPick()` / `shuffle()` are **not** seeded — they break frame-to-frame reproducibility. + ## Good Uses - Small SVG and DOM flourishes where Anime.js syntax is compact. -- Imported Anime.js examples that can be made seek-driven. +- Free `splitText` / `scrambleText` (Motion puts these behind Motion+; GSAP SplitText is the other free option). +- `svg.createDrawable` / `svg.morphTo` / `svg.createMotionPath` line-draw and path work. - Multiple independent micro-animations pushed into the same registry. Use GSAP for complex scene sequencing unless the user specifically asks for Anime.js. GSAP is still the primary HyperFrames authoring path. @@ -95,8 +107,11 @@ Use GSAP for complex scene sequencing unless the user specifically asks for Anim ## Avoid - Leaving `autoplay` at the Anime.js default. -- Depending on `anime.running` auto-discovery instead of explicit `window.__hfAnime.push(...)`. -- Infinite loops. Compute a finite repeat count from the composition duration. +- **Relying on the adapter's `anime.running` auto-discovery — it cannot work on v4.** `running` is not among v4.5.0's exports (verified against the published bundle), so `discover()` returns immediately and any instance you did not `push()` is never seeked. Explicit registration is mandatory, not a nicety. +- `autoplay: onScroll(...)` — there is no scroll in a headless seek render, so the animation would never advance. Drive it off composition time instead. +- `waapi.animate()` for anything the adapter must seek — the adapter seeks via `.seek()`, and whether WAAPI-backed instances honor it is **unverified**. Use the JS engine (`animate`) for rendered compositions; `waapi` is an off-main-thread optimization for live pages. +- `createDraggable`, and any pointer-driven `createAnimatable` loop — input does not exist at render time. +- Infinite loops. Compute a finite repeat count from the composition duration (v4 `loop` counts **repeats**: `loop: 1` plays twice). - Building animations in timers, promises, event handlers, or after async asset loads. ## Validation @@ -105,10 +120,11 @@ After editing a composition that uses Anime.js: ```bash npx hyperframes lint -npx hyperframes check +npx hyperframes validate ``` ## Credits And References - HyperFrames adapter source: `packages/core/src/runtime/adapters/animejs.ts`. -- Anime.js documentation for `autoplay`, `pause()`, and `seek()`: https://animejs.com/documentation/ +- Anime.js v4 docs: https://animejs.com/documentation/ +- v3 → v4 migration (not on animejs.com): https://github.com/juliangarnier/anime/wiki/Migrating-from-v3-to-v4 diff --git a/skills/hyperframes-keyframes/references/keyframe-patterns.md b/skills/hyperframes-keyframes/references/keyframe-patterns.md index 3c3211d1c6..5770721d01 100644 --- a/skills/hyperframes-keyframes/references/keyframe-patterns.md +++ b/skills/hyperframes-keyframes/references/keyframe-patterns.md @@ -42,8 +42,10 @@ CSS: Anime.js: ```js -const animation = anime.timeline({ autoplay: false }); -animation.add({ targets: "" /* derived channels */ }); +const animation = anime.createTimeline({ autoplay: false }); +animation.add("", { + /* derived channels */ +}); window.__hfAnime = window.__hfAnime || []; window.__hfAnime.push(animation); ```