diff --git a/docs/packages/cli.mdx b/docs/packages/cli.mdx index 1ba90e01e4..f671e96c1a 100644 --- a/docs/packages/cli.mdx +++ b/docs/packages/cli.mdx @@ -1988,7 +1988,7 @@ npx hyperframes docs [topic] ``` Topics: `data-attributes`, `examples`, `rendering`, `gsap`, `troubleshooting`, -`compositions`. Run it bare to list them. +`compositions`, `motion-blur`. Run it bare to list them. ### `feedback` diff --git a/packages/cli/src/commands/docs.test.ts b/packages/cli/src/commands/docs.test.ts index 707584e0fe..09b873cdf4 100644 --- a/packages/cli/src/commands/docs.test.ts +++ b/packages/cli/src/commands/docs.test.ts @@ -48,3 +48,18 @@ describe("docs examples topic", () => { } }); }); + +describe("docs topics", () => { + const source = readFileSync( + join(REPO_ROOT, "packages", "cli", "src", "commands", "docs.ts"), + "utf8", + ); + + it("has a file for every topic it lists, including motion-blur", () => { + const files = [...source.matchAll(/file: "([^"]+)"/g)].map((match) => match[1] ?? ""); + expect(files).toContain("motion-blur.md"); + for (const file of files) { + expect(readdirSync(join(REPO_ROOT, "packages", "cli", "src", "docs"))).toContain(file); + } + }); +}); diff --git a/packages/cli/src/commands/docs.ts b/packages/cli/src/commands/docs.ts index a3c7566d4c..cd1042f21e 100644 --- a/packages/cli/src/commands/docs.ts +++ b/packages/cli/src/commands/docs.ts @@ -43,6 +43,10 @@ const TOPICS: Record = { file: "compositions.md", description: "Composition structure, nesting, and variables", }, + "motion-blur": { + file: "motion-blur.md", + description: "Shutter smear on fast movers: attributes, tiers, checks", + }, }; function docsDir(): string { diff --git a/packages/cli/src/docs/motion-blur.md b/packages/cli/src/docs/motion-blur.md new file mode 100644 index 0000000000..1eaccd4030 --- /dev/null +++ b/packages/cli/src/docs/motion-blur.md @@ -0,0 +1,28 @@ +# Motion Blur + +Fast-moving elements get a shutter smear along their path. It is opt-in: nothing blurs until the composition root has `data-motion-blur="on"`. + +## Turn it on + +- `data-motion-blur` — `on` enables blur; `off` opts out, on the root or on any subtree +- `data-motion-blur-shutter` — shutter angle in degrees, 0-720 (default: 240) +- `data-motion-blur-samples` — samples per frame, 2-64 (default: at least 32, sized to the smear) +- `data-motion-blur-events` — `on` runs timeline callbacks while blur looks for movers; needed when motion is set from a `render(t)` callback +- `data-motion-blur-tier` — `auto` (default), `capture` or `filter` + +The same attributes drive the browser editor preview and `hyperframes render`. + +## Two ways it is drawn + +- `capture` redraws each mover along its path, so turns and zooms smear truthfully. Needs a browser with HTML in Canvas. +- `filter` puts an SVG filter on each mover. Works in any Chrome and costs little; turns and zooms are only softened. + +`auto` uses `filter` when every mover only translates and `capture` when anything turns or scales. + +## Check and render + +- `npx hyperframes check` reports `motion_blur_filter_fallback`, `motion_blur_no_movers` and `motion_blur_unverified` +- `hyperframes render --page-motion-blur off` renders without the composition's blur +- Blur is off for HDR, distributed, and sub-frame-blur renders + +Full guide: https://hyperframes.heygen.com/guides/motion-blur