@retikz/vanilla 0.3.0-alpha.3 → 0.3.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -50
- package/dist/es/hydrate.d.ts +10 -4
- package/dist/es/hydrate.d.ts.map +1 -1
- package/dist/es/hydrate.js +22 -6
- package/dist/es/index.d.ts +4 -1
- package/dist/es/index.d.ts.map +1 -1
- package/dist/es/index.js +2 -1
- package/dist/es/mountCanvas.d.ts.map +1 -1
- package/dist/es/mountCanvas.js +65 -4
- package/dist/es/mountSvg.d.ts.map +1 -1
- package/dist/es/mountSvg.js +40 -1
- package/dist/es/renderToSvgString.d.ts.map +1 -1
- package/dist/es/renderToSvgString.js +6 -1
- package/dist/es/types.d.ts +36 -1
- package/dist/es/types.d.ts.map +1 -1
- package/dist/lib/hydrate.cjs +21 -5
- package/dist/lib/hydrate.d.ts +10 -4
- package/dist/lib/hydrate.d.ts.map +1 -1
- package/dist/lib/index.cjs +91 -0
- package/dist/lib/index.d.ts +4 -1
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/mountCanvas.cjs +64 -3
- package/dist/lib/mountCanvas.d.ts.map +1 -1
- package/dist/lib/mountSvg.cjs +40 -1
- package/dist/lib/mountSvg.d.ts.map +1 -1
- package/dist/lib/renderToSvgString.cjs +6 -1
- package/dist/lib/renderToSvgString.d.ts.map +1 -1
- package/dist/lib/types.d.ts +36 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
# @retikz/vanilla
|
|
2
|
-
|
|
3
|
-
Framework-free runtime + SSR entry for [retikz](https://pionpill.github.io/retikz/). No JSX, no UI framework — mount a diagram to the DOM, render it to an SVG string on the server, or compose one with an imperative named builder.
|
|
4
|
-
|
|
5
|
-
retikz 的无框架 runtime / SSR 入口:不依赖任何 UI 框架。`renderToSvgString` 走服务端 / 构建期(零 DOM)产 SVG 字符串;`mountSvg` 在浏览器把图形挂到 DOM;命令式 builder(`figure`/`node`/`draw`/`coordinate`/`scope`)让你像 React 一样具名构图、产同一份 IR。组合 `@retikz/render` 内核,不自维护第二套渲染逻辑。
|
|
6
|
-
|
|
7
|
-
## Install
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
pnpm add @retikz/vanilla @retikz/core @retikz/render
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Render IR / Scene
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
import { renderToSvgString, mountSvg } from '@retikz/vanilla';
|
|
17
|
-
|
|
18
|
-
// server / build time — no DOM
|
|
19
|
-
const svg = renderToSvgString(ir);
|
|
20
|
-
|
|
21
|
-
// browser
|
|
22
|
-
mountSvg(document.querySelector('#diagram'), ir);
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Imperative builder
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import { figure, node, draw } from '@retikz/vanilla';
|
|
29
|
-
|
|
30
|
-
const fig = figure([
|
|
31
|
-
node('a', { position: [0, 0], text: 'A' }),
|
|
32
|
-
node('b', { position: [120, 0], text: 'B' }),
|
|
33
|
-
draw(['a', 'b'], { arrow: '->' }),
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
const svg = fig.toSvgString(); // also: fig.mount(el) / fig.toCanvas(canvas)
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Exports
|
|
40
|
-
|
|
41
|
-
- Runtime: `renderToSvgString`, `mountSvg`
|
|
42
|
-
- Builder: `figure` / `node` / `draw` / `coordinate` / `scope` + the `Figure` view (`.toSvgString` / `.mount` / `.toCanvas`)
|
|
43
|
-
|
|
44
|
-
## Docs
|
|
45
|
-
|
|
46
|
-
<https://pionpill.github.io/retikz/>
|
|
47
|
-
|
|
48
|
-
## License
|
|
49
|
-
|
|
50
|
-
MIT
|
|
1
|
+
# @retikz/vanilla
|
|
2
|
+
|
|
3
|
+
Framework-free runtime + SSR entry for [retikz](https://pionpill.github.io/retikz/). No JSX, no UI framework — mount a diagram to the DOM, render it to an SVG string on the server, or compose one with an imperative named builder.
|
|
4
|
+
|
|
5
|
+
retikz 的无框架 runtime / SSR 入口:不依赖任何 UI 框架。`renderToSvgString` 走服务端 / 构建期(零 DOM)产 SVG 字符串;`mountSvg` 在浏览器把图形挂到 DOM;命令式 builder(`figure`/`node`/`draw`/`coordinate`/`scope`)让你像 React 一样具名构图、产同一份 IR。组合 `@retikz/render` 内核,不自维护第二套渲染逻辑。
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @retikz/vanilla @retikz/core @retikz/render
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Render IR / Scene
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { renderToSvgString, mountSvg } from '@retikz/vanilla';
|
|
17
|
+
|
|
18
|
+
// server / build time — no DOM
|
|
19
|
+
const svg = renderToSvgString(ir);
|
|
20
|
+
|
|
21
|
+
// browser
|
|
22
|
+
mountSvg(document.querySelector('#diagram'), ir);
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Imperative builder
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { figure, node, draw } from '@retikz/vanilla';
|
|
29
|
+
|
|
30
|
+
const fig = figure([
|
|
31
|
+
node('a', { position: [0, 0], text: 'A' }),
|
|
32
|
+
node('b', { position: [120, 0], text: 'B' }),
|
|
33
|
+
draw(['a', 'b'], { arrow: '->' }),
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const svg = fig.toSvgString(); // also: fig.mount(el) / fig.toCanvas(canvas)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Exports
|
|
40
|
+
|
|
41
|
+
- Runtime: `renderToSvgString`, `mountSvg`
|
|
42
|
+
- Builder: `figure` / `node` / `draw` / `coordinate` / `scope` + the `Figure` view (`.toSvgString` / `.mount` / `.toCanvas`)
|
|
43
|
+
|
|
44
|
+
## Docs
|
|
45
|
+
|
|
46
|
+
<https://pionpill.github.io/retikz/>
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT
|
package/dist/es/hydrate.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { HydrateOptions, HydrationHandle } from './types';
|
|
2
2
|
/**
|
|
3
|
-
* 把用户 handler 水合到容器内已挂的 `<svg
|
|
3
|
+
* 把用户 handler 水合到容器内已挂的 `<svg>`(SSR 后的独立 SVG 水合入口)
|
|
4
4
|
* @description SSR / `mountSvg` 先渲染出含 `data-retikz-id` 的图,本入口再把 handler 绑回图元——不重建 DOM、
|
|
5
|
-
* 不接管状态,只做「图元 id ↔ 用户函数」绑定。用
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* 不接管状态,只做「图元 id ↔ 用户函数」绑定。用 `createHydrationController(root, handlers, locateSvg, buildContext)`
|
|
6
|
+
* 在 `root` 上挂根级委托:事件经 `closest('[data-retikz-id]')` 反查图元 id,命中即以 `(event, context)` 触发对应
|
|
7
|
+
* handler;`pointerEnter` / `pointerLeave` 由控制器经 `pointermove` + 命中 id 状态机合成。返回 `{ dispose }` 解绑。
|
|
8
|
+
*
|
|
9
|
+
* **context 能力分两档**:传 `options.scene`(可经 `toScene(ir)` 得到)→ 富 context——`meta` / `geometry` 经 Scene 按 id
|
|
10
|
+
* 聚合查询、`point` 逆 meet-fit、`animation` 经 `data-retikz-id` / `data-retikz-animation-owner` 双查
|
|
11
|
+
* `getAnimations()` per-id 控制;不传 → 最小 context——`id` + `element` + `root` + `renderer`,`point` 退 `getScreenCTM`
|
|
12
|
+
* (浏览器有效、jsdom 为 null),`meta` / `geometry` / `scene` undefined、`animation` no-op。SSR 后要富 context 须把
|
|
13
|
+
* `scene` 一并传入。
|
|
8
14
|
*/
|
|
9
15
|
export declare const hydrate: (root: Element, options: HydrateOptions) => HydrationHandle;
|
|
10
16
|
//# sourceMappingURL=hydrate.d.ts.map
|
package/dist/es/hydrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../../src/hydrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../../src/hydrate.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,SAAS,cAAc,KAAG,eAahE,CAAC"}
|
package/dist/es/hydrate.js
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
|
-
import { createHydrationController, locateSvg } from "@retikz/render/hydration";
|
|
1
|
+
import { createContextBuilder, createHydrationController, createSvgAnimationControls, locateSvg, noopAnimationControls, resolvePointViaLayout, resolveSvgElement, resolveSvgPointViaCtm } from "@retikz/render/hydration";
|
|
2
2
|
//#region src/hydrate.ts
|
|
3
3
|
/**
|
|
4
|
-
* 把用户 handler 水合到容器内已挂的 `<svg
|
|
4
|
+
* 把用户 handler 水合到容器内已挂的 `<svg>`(SSR 后的独立 SVG 水合入口)
|
|
5
5
|
* @description SSR / `mountSvg` 先渲染出含 `data-retikz-id` 的图,本入口再把 handler 绑回图元——不重建 DOM、
|
|
6
|
-
* 不接管状态,只做「图元 id ↔ 用户函数」绑定。用
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* 不接管状态,只做「图元 id ↔ 用户函数」绑定。用 `createHydrationController(root, handlers, locateSvg, buildContext)`
|
|
7
|
+
* 在 `root` 上挂根级委托:事件经 `closest('[data-retikz-id]')` 反查图元 id,命中即以 `(event, context)` 触发对应
|
|
8
|
+
* handler;`pointerEnter` / `pointerLeave` 由控制器经 `pointermove` + 命中 id 状态机合成。返回 `{ dispose }` 解绑。
|
|
9
|
+
*
|
|
10
|
+
* **context 能力分两档**:传 `options.scene`(可经 `toScene(ir)` 得到)→ 富 context——`meta` / `geometry` 经 Scene 按 id
|
|
11
|
+
* 聚合查询、`point` 逆 meet-fit、`animation` 经 `data-retikz-id` / `data-retikz-animation-owner` 双查
|
|
12
|
+
* `getAnimations()` per-id 控制;不传 → 最小 context——`id` + `element` + `root` + `renderer`,`point` 退 `getScreenCTM`
|
|
13
|
+
* (浏览器有效、jsdom 为 null),`meta` / `geometry` / `scene` undefined、`animation` no-op。SSR 后要富 context 须把
|
|
14
|
+
* `scene` 一并传入。
|
|
9
15
|
*/
|
|
10
16
|
var hydrate = (root, options) => {
|
|
11
|
-
|
|
17
|
+
const renderer = options.renderer ?? "svg";
|
|
18
|
+
const scene = options.scene;
|
|
19
|
+
const buildContext = createContextBuilder({
|
|
20
|
+
renderer,
|
|
21
|
+
root,
|
|
22
|
+
scene,
|
|
23
|
+
resolveElement: resolveSvgElement,
|
|
24
|
+
resolvePoint: scene ? resolvePointViaLayout(root, scene.layout) : resolveSvgPointViaCtm(root),
|
|
25
|
+
makeAnimation: scene ? (id) => createSvgAnimationControls(root, id) : () => noopAnimationControls
|
|
26
|
+
});
|
|
27
|
+
return { dispose: createHydrationController(root, options.handlers, locateSvg, buildContext).dispose };
|
|
12
28
|
};
|
|
13
29
|
//#endregion
|
|
14
30
|
export { hydrate };
|
package/dist/es/index.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export { renderToSvgString } from './renderToSvgString';
|
|
|
10
10
|
export { mountSvg } from './mountSvg';
|
|
11
11
|
export { hydrate } from './hydrate';
|
|
12
12
|
export { mountCanvas } from './mountCanvas';
|
|
13
|
+
export type { RetikzEventName, HydrationHandlers, HydrationHandler, HydrationContext, HydrationAnimationControls, HydrationGeometry, } from '@retikz/render/hydration';
|
|
14
|
+
export { fadeIn, drawOn, scaleIn, grow, growUp, slideIn, colorShift, cameraTo, pulse, spin, loop, flash, blink, wiggle, stagger, } from '@retikz/core';
|
|
15
|
+
export type { AnimationPresetOptions, ScaleInOptions, GrowUpOptions, SlideInOptions, ColorShiftOptions, CameraToOptions, PulseOptions, SpinOptions, LoopOptions, FlashOptions, BlinkOptions, WiggleOptions, } from '@retikz/core';
|
|
13
16
|
export { figure } from './builder/figure';
|
|
14
17
|
export { node } from './builder/node';
|
|
15
18
|
export { draw } from './builder/draw';
|
|
@@ -18,5 +21,5 @@ export { scope } from './builder/scope';
|
|
|
18
21
|
export type { Figure } from './figure';
|
|
19
22
|
export type { ScopeBuilder } from './builder/scope';
|
|
20
23
|
export type { Child, NodeConfig, DrawConfig, CoordinateConfig, ScopeConfig, FigureConfig, Way, } from './builder/types';
|
|
21
|
-
export type { RenderInput, CommonOptions, MountOptions, RenderToStringOptions, VanillaView, CanvasView, MountCanvasOptions, HydrateOptions, HydrationHandle, ScenePoint, } from './types';
|
|
24
|
+
export type { RenderInput, CommonOptions, MountOptions, RenderToStringOptions, VanillaView, CanvasView, MountCanvasOptions, HydrateOptions, HydrationHandle, ScenePoint, AnimationControls, } from './types';
|
|
22
25
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/es/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,0BAA0B,EAC1B,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,MAAM,EACN,OAAO,EACP,UAAU,EACV,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,KAAK,EACL,MAAM,EACN,OAAO,GACR,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,sBAAsB,EACtB,cAAc,EACd,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EACV,KAAK,EACL,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,GAAG,GACJ,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,UAAU,EACV,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
|
package/dist/es/index.js
CHANGED
|
@@ -7,4 +7,5 @@ import { draw } from "./builder/draw.js";
|
|
|
7
7
|
import { node } from "./builder/node.js";
|
|
8
8
|
import { scope } from "./builder/scope.js";
|
|
9
9
|
import { figure } from "./builder/figure.js";
|
|
10
|
-
|
|
10
|
+
import { blink, cameraTo, colorShift, drawOn, fadeIn, flash, grow, growUp, loop, pulse, scaleIn, slideIn, spin, stagger, wiggle } from "@retikz/core";
|
|
11
|
+
export { blink, cameraTo, colorShift, coordinate, draw, drawOn, fadeIn, figure, flash, grow, growUp, hydrate, loop, mountCanvas, mountSvg, node, pulse, renderToSvgString, scaleIn, scope, slideIn, spin, stagger, wiggle };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mountCanvas.d.ts","sourceRoot":"","sources":["../../src/mountCanvas.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mountCanvas.d.ts","sourceRoot":"","sources":["../../src/mountCanvas.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAkB,kBAAkB,EAAE,WAAW,EAAc,MAAM,SAAS,CAAC;AASvG;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GACtB,WAAW,OAAO,EAClB,OAAO,WAAW,EAClB,UAAS,kBAAuB,KAC/B,UA6JF,CAAC"}
|
package/dist/es/mountCanvas.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isFigure } from "./builder/isFigure.js";
|
|
2
2
|
import { toScene } from "./toScene.js";
|
|
3
|
-
import {
|
|
3
|
+
import { createClock, createIdClockRegistry, prefersReducedMotion, sceneAnimationDurationMs, sceneHasAnimations, sceneHasAutoplayTrigger } from "@retikz/render/animation";
|
|
4
|
+
import { createCanvasIdAnimationControls, createHydrationController, geometryOf, metaOf } from "@retikz/render/hydration";
|
|
4
5
|
import { hitTest, renderToCanvas } from "@retikz/render/canvas";
|
|
5
6
|
//#region src/mountCanvas.ts
|
|
6
7
|
/** 设备像素比:取有限正数、否则回退 1(镜像 react CanvasHost) */
|
|
@@ -20,7 +21,27 @@ var mountCanvas = (container, input, options = {}) => {
|
|
|
20
21
|
if (typeof Element === "undefined" || !(container instanceof Element)) throw new Error("mountCanvas: container must be a DOM Element.");
|
|
21
22
|
const canvas = document.createElement("canvas");
|
|
22
23
|
const ratio = resolveDevicePixelRatio(options.devicePixelRatio);
|
|
24
|
+
const animate = options.animate !== false && !prefersReducedMotion();
|
|
25
|
+
let clock;
|
|
26
|
+
const registry = createIdClockRegistry();
|
|
23
27
|
let currentScene;
|
|
28
|
+
/** 把全局帧时刻折算成单个 prim 的动画解析(per-id):stop→渲染 base;否则按有效时刻 + 是否含非自动播 track */
|
|
29
|
+
const resolvePrim = (id, globalTime) => id !== void 0 && registry.isStopped(id) ? { mode: "skip" } : {
|
|
30
|
+
mode: "at",
|
|
31
|
+
time: registry.timeFor(id, globalTime),
|
|
32
|
+
includeNonAutoplay: registry.isActive(id)
|
|
33
|
+
};
|
|
34
|
+
/** 按当前时钟时刻 + per-id 登记表立即重绘一帧(per-id pause / stop 即时反映) */
|
|
35
|
+
const renderFrame = () => {
|
|
36
|
+
const time = clock?.time ?? 0;
|
|
37
|
+
renderToCanvas(canvas, currentScene, {
|
|
38
|
+
devicePixelRatio: ratio,
|
|
39
|
+
time,
|
|
40
|
+
easings: options.easings,
|
|
41
|
+
animationProperties: options.animationProperties,
|
|
42
|
+
resolvePrimAnimation: (id) => resolvePrim(id, time)
|
|
43
|
+
});
|
|
44
|
+
};
|
|
24
45
|
const renderInto = (next) => {
|
|
25
46
|
if (isFigure(next)) throw new Error("mountCanvas: view.update does not accept a Figure; pass figure.ir instead.");
|
|
26
47
|
const scene = toScene(next, options);
|
|
@@ -34,6 +55,21 @@ var mountCanvas = (container, input, options = {}) => {
|
|
|
34
55
|
if (options.height !== void 0) canvas.style.height = `${options.height}px`;
|
|
35
56
|
canvas.style.objectFit = "contain";
|
|
36
57
|
renderToCanvas(canvas, scene, { devicePixelRatio: ratio });
|
|
58
|
+
clock?.dispose();
|
|
59
|
+
clock = void 0;
|
|
60
|
+
if (animate && sceneHasAnimations(scene)) {
|
|
61
|
+
clock = createClock({
|
|
62
|
+
durationMs: sceneAnimationDurationMs(scene),
|
|
63
|
+
onFrame: (time) => renderToCanvas(canvas, currentScene, {
|
|
64
|
+
devicePixelRatio: ratio,
|
|
65
|
+
time,
|
|
66
|
+
easings: options.easings,
|
|
67
|
+
animationProperties: options.animationProperties,
|
|
68
|
+
resolvePrimAnimation: (id) => resolvePrim(id, time)
|
|
69
|
+
})
|
|
70
|
+
});
|
|
71
|
+
if (sceneHasAutoplayTrigger(scene)) clock.play();
|
|
72
|
+
}
|
|
37
73
|
};
|
|
38
74
|
renderInto(isFigure(input) ? input.ir : input);
|
|
39
75
|
container.appendChild(canvas);
|
|
@@ -68,11 +104,32 @@ var mountCanvas = (container, input, options = {}) => {
|
|
|
68
104
|
*/
|
|
69
105
|
const hydrate = (hydrateOptions) => {
|
|
70
106
|
const context2d = canvas.getContext("2d") ?? void 0;
|
|
71
|
-
|
|
107
|
+
const locate = (event) => {
|
|
72
108
|
const scenePoint = clientToScene(event.clientX, event.clientY);
|
|
73
109
|
context2d?.setTransform(1, 0, 0, 1, 0, 0);
|
|
74
110
|
return hitTest(currentScene, scenePoint, { context2d });
|
|
75
|
-
}
|
|
111
|
+
};
|
|
112
|
+
const buildContext = (event, id) => {
|
|
113
|
+
const mouse = event;
|
|
114
|
+
return {
|
|
115
|
+
id,
|
|
116
|
+
meta: metaOf(currentScene, id),
|
|
117
|
+
renderer: "canvas",
|
|
118
|
+
element: null,
|
|
119
|
+
root: canvas,
|
|
120
|
+
point: typeof mouse.clientX === "number" ? clientToScene(mouse.clientX, mouse.clientY) : null,
|
|
121
|
+
geometry: geometryOf(currentScene, id),
|
|
122
|
+
animation: createCanvasIdAnimationControls({
|
|
123
|
+
registry,
|
|
124
|
+
clockTime: () => clock?.time ?? 0,
|
|
125
|
+
ensurePlaying: () => clock?.play(),
|
|
126
|
+
renderFrame,
|
|
127
|
+
defaultId: id
|
|
128
|
+
}),
|
|
129
|
+
scene: currentScene
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
return { dispose: createHydrationController(canvas, hydrateOptions.handlers, locate, buildContext).dispose };
|
|
76
133
|
};
|
|
77
134
|
return {
|
|
78
135
|
root: canvas,
|
|
@@ -83,10 +140,14 @@ var mountCanvas = (container, input, options = {}) => {
|
|
|
83
140
|
dispose() {
|
|
84
141
|
if (disposed) return;
|
|
85
142
|
disposed = true;
|
|
143
|
+
clock?.dispose();
|
|
86
144
|
canvas.remove();
|
|
87
145
|
},
|
|
88
146
|
hydrate,
|
|
89
|
-
clientToScene
|
|
147
|
+
clientToScene,
|
|
148
|
+
get animation() {
|
|
149
|
+
return clock;
|
|
150
|
+
}
|
|
90
151
|
};
|
|
91
152
|
};
|
|
92
153
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mountSvg.d.ts","sourceRoot":"","sources":["../../src/mountSvg.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mountSvg.d.ts","sourceRoot":"","sources":["../../src/mountSvg.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAmC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAMvG;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,WAAW,OAAO,EAAE,OAAO,WAAW,EAAE,UAAS,YAAiB,KAAG,WA4E7F,CAAC"}
|
package/dist/es/mountSvg.js
CHANGED
|
@@ -2,6 +2,8 @@ import { isFigure } from "./builder/isFigure.js";
|
|
|
2
2
|
import { toScene } from "./toScene.js";
|
|
3
3
|
import { applyAttrs, svgNodeToDom } from "./svgNodeToDom.js";
|
|
4
4
|
import { buildSvgDocument } from "@retikz/render/svg";
|
|
5
|
+
import { bindWaapiDescriptors, prefersReducedMotion, sceneHasAnimations } from "@retikz/render/animation";
|
|
6
|
+
import { createHydrationController, createSvgAnimationControls, geometryOf, locateSvg, metaOf, resolvePointViaLayout, resolveSvgElement } from "@retikz/render/hydration";
|
|
5
7
|
//#region src/mountSvg.ts
|
|
6
8
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
7
9
|
/** 默认资源 id 前缀(确定性);多实例同页须经 `options.idPrefix` 显式区分 */
|
|
@@ -18,18 +20,50 @@ var mountSvg = (container, input, options = {}) => {
|
|
|
18
20
|
if (typeof Element === "undefined" || !(container instanceof Element)) throw new Error("mountSvg: container must be a DOM Element.");
|
|
19
21
|
const idPrefix = options.idPrefix ?? DEFAULT_ID_PREFIX;
|
|
20
22
|
const root = document.createElementNS(SVG_NS, "svg");
|
|
23
|
+
const animate = options.animate !== false && !prefersReducedMotion();
|
|
24
|
+
let animationControls;
|
|
25
|
+
let currentScene;
|
|
21
26
|
const renderInto = (next) => {
|
|
22
27
|
if (isFigure(next)) throw new Error("mountSvg: view.update does not accept a Figure; pass figure.ir instead.");
|
|
23
|
-
const
|
|
28
|
+
const scene = toScene(next, options);
|
|
29
|
+
currentScene = scene;
|
|
30
|
+
const doc = buildSvgDocument(scene, {
|
|
31
|
+
idPrefix,
|
|
32
|
+
animate,
|
|
33
|
+
snapshotAt: options.snapshotAt,
|
|
34
|
+
easings: options.easings
|
|
35
|
+
});
|
|
24
36
|
while (root.firstChild) root.removeChild(root.firstChild);
|
|
25
37
|
for (const attr of [...root.attributes]) root.removeAttribute(attr.name);
|
|
26
38
|
applyAttrs(root, doc);
|
|
27
39
|
if (options.width !== void 0) root.setAttribute("width", String(options.width));
|
|
28
40
|
if (options.height !== void 0) root.setAttribute("height", String(options.height));
|
|
29
41
|
for (const child of doc.children ?? []) root.appendChild(typeof child === "string" ? document.createTextNode(child) : svgNodeToDom(child));
|
|
42
|
+
animationControls?.dispose();
|
|
43
|
+
animationControls = animate && sceneHasAnimations(scene) ? bindWaapiDescriptors(root) : void 0;
|
|
30
44
|
};
|
|
31
45
|
renderInto(input);
|
|
32
46
|
container.appendChild(root);
|
|
47
|
+
/**
|
|
48
|
+
* 把 handler 绑到本 view 的 `<svg>`,handler 收 `(event, context)` 富上下文
|
|
49
|
+
* @description `buildContext` 读 live `currentScene`(`update` 后自动反映新图):meta / geometry 经 Scene 按 id
|
|
50
|
+
* 聚合查询,element 经 `closest('[data-retikz-id]')`,point 逆 meet-fit,动画控制经 `data-retikz-id` /
|
|
51
|
+
* `data-retikz-animation-owner` 双查 `getAnimations()` per-id 控制。
|
|
52
|
+
*/
|
|
53
|
+
const hydrate = (hydrateOptions) => {
|
|
54
|
+
const buildContext = (event, id) => ({
|
|
55
|
+
id,
|
|
56
|
+
meta: metaOf(currentScene, id),
|
|
57
|
+
renderer: "svg",
|
|
58
|
+
element: resolveSvgElement(event),
|
|
59
|
+
root,
|
|
60
|
+
point: resolvePointViaLayout(root, currentScene.layout)(event),
|
|
61
|
+
geometry: geometryOf(currentScene, id),
|
|
62
|
+
animation: createSvgAnimationControls(root, id),
|
|
63
|
+
scene: currentScene
|
|
64
|
+
});
|
|
65
|
+
return { dispose: createHydrationController(root, hydrateOptions.handlers, locateSvg, buildContext).dispose };
|
|
66
|
+
};
|
|
33
67
|
let disposed = false;
|
|
34
68
|
return {
|
|
35
69
|
root,
|
|
@@ -37,10 +71,15 @@ var mountSvg = (container, input, options = {}) => {
|
|
|
37
71
|
if (disposed) throw new Error("mountSvg: view already disposed.");
|
|
38
72
|
renderInto(next);
|
|
39
73
|
},
|
|
74
|
+
hydrate,
|
|
40
75
|
dispose() {
|
|
41
76
|
if (disposed) return;
|
|
42
77
|
disposed = true;
|
|
78
|
+
animationControls?.dispose();
|
|
43
79
|
root.remove();
|
|
80
|
+
},
|
|
81
|
+
get animation() {
|
|
82
|
+
return animationControls;
|
|
44
83
|
}
|
|
45
84
|
};
|
|
46
85
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderToSvgString.d.ts","sourceRoot":"","sources":["../../src/renderToSvgString.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAYlE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,WAAW,EAAE,UAAS,qBAA0B,KAAG,
|
|
1
|
+
{"version":3,"file":"renderToSvgString.d.ts","sourceRoot":"","sources":["../../src/renderToSvgString.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAYlE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,WAAW,EAAE,UAAS,qBAA0B,KAAG,MAS3F,CAAC"}
|
|
@@ -17,7 +17,12 @@ var injectSize = (svg, width, height) => {
|
|
|
17
17
|
*/
|
|
18
18
|
var renderToSvgString$1 = (input, options = {}) => {
|
|
19
19
|
if (isFigure(input)) return input.toSvgString(options);
|
|
20
|
-
return injectSize(renderToSvgString(toScene(input, options), {
|
|
20
|
+
return injectSize(renderToSvgString(toScene(input, options), {
|
|
21
|
+
idPrefix: options.idPrefix ?? DEFAULT_ID_PREFIX,
|
|
22
|
+
animate: options.animate,
|
|
23
|
+
snapshotAt: options.snapshotAt,
|
|
24
|
+
easings: options.easings
|
|
25
|
+
}), options.width, options.height);
|
|
21
26
|
};
|
|
22
27
|
//#endregion
|
|
23
28
|
export { renderToSvgString$1 as renderToSvgString };
|
package/dist/es/types.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CompileOptions, IR, Scene } from '@retikz/core';
|
|
2
2
|
import { HydrationHandlers } from '@retikz/render/hydration';
|
|
3
|
+
import { AnimationControls, AnimationPropertyRegistry, EasingRegistry } from '@retikz/render/animation';
|
|
3
4
|
import { Figure } from './figure';
|
|
5
|
+
export type { AnimationControls } from '@retikz/render/animation';
|
|
4
6
|
/** mountSvg / renderToSvgString 的入参:已编译 `Scene`、待编译 `IR`,或命令式 builder 的 `Figure` */
|
|
5
7
|
export type RenderInput = Scene | IR | Figure;
|
|
6
8
|
/**
|
|
@@ -14,6 +16,18 @@ export type CommonOptions = {
|
|
|
14
16
|
idPrefix?: string;
|
|
15
17
|
width?: number;
|
|
16
18
|
height?: number;
|
|
19
|
+
/**
|
|
20
|
+
* 是否播放动画(缺省 true);`false` → 渲染 base 静态图(不 emit CSS/WAAPI、Canvas 不起 rAF)
|
|
21
|
+
* @description runtime 据 `{animate:false}` 或 `prefers-reduced-motion` 走静态路径(ADR-01「三事一路」)。
|
|
22
|
+
*/
|
|
23
|
+
animate?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* 静态截帧时刻(毫秒);给定时渲染「定格在该时刻」的静态图(不播放、不 emit 动画),SSR 海报帧 / 缩略图用
|
|
26
|
+
* @description SVG 后端把各 track 在该时刻的值烘焙成静态属性 / transform(复用 `evaluateTrack`);覆盖 `animate`。
|
|
27
|
+
*/
|
|
28
|
+
snapshotAt?: number;
|
|
29
|
+
/** 自定义 easing 注册表(透传 renderer / runtime) */
|
|
30
|
+
easings?: EasingRegistry;
|
|
17
31
|
} & CompileOptions;
|
|
18
32
|
export type RenderToStringOptions = CommonOptions;
|
|
19
33
|
export type MountOptions = CommonOptions;
|
|
@@ -25,16 +39,33 @@ export type VanillaView = {
|
|
|
25
39
|
update: (next: RenderInput) => void;
|
|
26
40
|
/** 卸载:移除 `root`、置 view 失效(再调 `update` 抛、`dispose` noop) */
|
|
27
41
|
dispose: () => void;
|
|
42
|
+
/**
|
|
43
|
+
* 绑定 handler 到本 view 的 `<svg>`(locateSvg 定位);handler 收 `(event, context)` 富上下文
|
|
44
|
+
* @description context 由本 view 的 Scene 构造(meta / geometry / per-id 动画控制),读 live `currentScene`——
|
|
45
|
+
* `update` 后 context 自动反映新图(无需重 hydrate)。`HydrateOptions.scene` / `renderer` 在 view.hydrate 下忽略。
|
|
46
|
+
*/
|
|
47
|
+
hydrate: (options: HydrateOptions) => HydrationHandle;
|
|
48
|
+
/** 动画播放控制句柄(scene 含动画且未降级时存在):play / pause / seek;manual trigger 经此驱动 */
|
|
49
|
+
animation?: AnimationControls;
|
|
28
50
|
};
|
|
29
51
|
/** `hydrate` / `view.hydrate` 返回的解绑句柄 */
|
|
30
52
|
export type HydrationHandle = {
|
|
31
53
|
/** 解绑本次水合的全部 listener,之后事件不再触发 */
|
|
32
54
|
dispose: () => void;
|
|
33
55
|
};
|
|
34
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* 水合入参:按 id 提供的 handler 注册表(事件名 → handler)+ 可选 Scene(富 context 来源)
|
|
58
|
+
* @description `view.hydrate`(mountSvg / mountCanvas)忽略 `scene` / `renderer`、用自身 Scene 构造富 context;
|
|
59
|
+
* standalone `hydrate(root, options)`(SSR 后独立入口)传 `scene` → 富 context(meta / geometry / 动画),
|
|
60
|
+
* 不传 → 最小 context(id + element + root + point,`meta` / `geometry` / `scene` undefined、`animation` no-op)。
|
|
61
|
+
*/
|
|
35
62
|
export type HydrateOptions = {
|
|
36
63
|
/** id → 事件名 → handler 的注册表(透传给 `@retikz/render/hydration` 控制器) */
|
|
37
64
|
handlers: HydrationHandlers;
|
|
65
|
+
/** 富 context 来源 Scene(仅 standalone `hydrate` 用;不传则最小 context);可经 `toScene(ir)` 得到 */
|
|
66
|
+
scene?: Scene;
|
|
67
|
+
/** standalone `hydrate` 的渲染后端(缺省 `'svg'`);决定 context.renderer 与 element 定位口径 */
|
|
68
|
+
renderer?: 'svg' | 'canvas';
|
|
38
69
|
};
|
|
39
70
|
/** Scene user units 坐标点(hitTest 入参 / 坐标映射出参) */
|
|
40
71
|
export type ScenePoint = {
|
|
@@ -63,10 +94,14 @@ export type CanvasView = {
|
|
|
63
94
|
* 交给 `hitTest` 自然判为无命中(无需在此截断),故无 `null` 返回。
|
|
64
95
|
*/
|
|
65
96
|
clientToScene: (clientX: number, clientY: number) => ScenePoint;
|
|
97
|
+
/** 动画播放控制句柄(scene 含动画且未降级时存在):rAF 时钟的 play / pause / seek */
|
|
98
|
+
animation?: AnimationControls;
|
|
66
99
|
};
|
|
67
100
|
/** `mountCanvas` 选项:继承 SSR / compile 公共项,外加 canvas 显示 / dpr 透传 */
|
|
68
101
|
export type MountCanvasOptions = CommonOptions & {
|
|
69
102
|
/** 设备像素比;缺省读 `globalThis.devicePixelRatio`、再回退 1(镜像 react CanvasHost) */
|
|
70
103
|
devicePixelRatio?: number;
|
|
104
|
+
/** 自定义 property 插值器注册表(透传 drawScene;自定义动画通道用) */
|
|
105
|
+
animationProperties?: AnimationPropertyRegistry;
|
|
71
106
|
};
|
|
72
107
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/es/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7G,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,GAAG,cAAc,CAAC;AAEnB,MAAM,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC;AAEzC,6DAA6D;AAC7D,MAAM,MAAM,WAAW,GAAG;IACxB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,gEAAgE;IAChE,MAAM,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACpC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,eAAe,CAAC;IACtD,yEAAyE;IACzE,SAAS,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF,yCAAyC;AACzC,MAAM,MAAM,eAAe,GAAG;IAC5B,kCAAkC;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,kEAAkE;IAClE,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,qFAAqF;IACrF,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC7B,CAAC;AAEF,gDAAgD;AAChD,MAAM,MAAM,UAAU,GAAG;IACvB,2BAA2B;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,2BAA2B;IAC3B,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,iDAAiD;IACjD,MAAM,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACpC,gEAAgE;IAChE,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,4FAA4F;IAC5F,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,eAAe,CAAC;IACtD;;;;OAIG;IACH,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,UAAU,CAAC;IAChE,6DAA6D;IAC7D,SAAS,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF,kEAAkE;AAClE,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG;IAC/C,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;CACjD,CAAC"}
|
package/dist/lib/hydrate.cjs
CHANGED
|
@@ -1,14 +1,30 @@
|
|
|
1
1
|
let _retikz_render_hydration = require("@retikz/render/hydration");
|
|
2
2
|
//#region src/hydrate.ts
|
|
3
3
|
/**
|
|
4
|
-
* 把用户 handler 水合到容器内已挂的 `<svg
|
|
4
|
+
* 把用户 handler 水合到容器内已挂的 `<svg>`(SSR 后的独立 SVG 水合入口)
|
|
5
5
|
* @description SSR / `mountSvg` 先渲染出含 `data-retikz-id` 的图,本入口再把 handler 绑回图元——不重建 DOM、
|
|
6
|
-
* 不接管状态,只做「图元 id ↔ 用户函数」绑定。用
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* 不接管状态,只做「图元 id ↔ 用户函数」绑定。用 `createHydrationController(root, handlers, locateSvg, buildContext)`
|
|
7
|
+
* 在 `root` 上挂根级委托:事件经 `closest('[data-retikz-id]')` 反查图元 id,命中即以 `(event, context)` 触发对应
|
|
8
|
+
* handler;`pointerEnter` / `pointerLeave` 由控制器经 `pointermove` + 命中 id 状态机合成。返回 `{ dispose }` 解绑。
|
|
9
|
+
*
|
|
10
|
+
* **context 能力分两档**:传 `options.scene`(可经 `toScene(ir)` 得到)→ 富 context——`meta` / `geometry` 经 Scene 按 id
|
|
11
|
+
* 聚合查询、`point` 逆 meet-fit、`animation` 经 `data-retikz-id` / `data-retikz-animation-owner` 双查
|
|
12
|
+
* `getAnimations()` per-id 控制;不传 → 最小 context——`id` + `element` + `root` + `renderer`,`point` 退 `getScreenCTM`
|
|
13
|
+
* (浏览器有效、jsdom 为 null),`meta` / `geometry` / `scene` undefined、`animation` no-op。SSR 后要富 context 须把
|
|
14
|
+
* `scene` 一并传入。
|
|
9
15
|
*/
|
|
10
16
|
var hydrate = (root, options) => {
|
|
11
|
-
|
|
17
|
+
const renderer = options.renderer ?? "svg";
|
|
18
|
+
const scene = options.scene;
|
|
19
|
+
const buildContext = (0, _retikz_render_hydration.createContextBuilder)({
|
|
20
|
+
renderer,
|
|
21
|
+
root,
|
|
22
|
+
scene,
|
|
23
|
+
resolveElement: _retikz_render_hydration.resolveSvgElement,
|
|
24
|
+
resolvePoint: scene ? (0, _retikz_render_hydration.resolvePointViaLayout)(root, scene.layout) : (0, _retikz_render_hydration.resolveSvgPointViaCtm)(root),
|
|
25
|
+
makeAnimation: scene ? (id) => (0, _retikz_render_hydration.createSvgAnimationControls)(root, id) : () => _retikz_render_hydration.noopAnimationControls
|
|
26
|
+
});
|
|
27
|
+
return { dispose: (0, _retikz_render_hydration.createHydrationController)(root, options.handlers, _retikz_render_hydration.locateSvg, buildContext).dispose };
|
|
12
28
|
};
|
|
13
29
|
//#endregion
|
|
14
30
|
exports.hydrate = hydrate;
|
package/dist/lib/hydrate.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { HydrateOptions, HydrationHandle } from './types';
|
|
2
2
|
/**
|
|
3
|
-
* 把用户 handler 水合到容器内已挂的 `<svg
|
|
3
|
+
* 把用户 handler 水合到容器内已挂的 `<svg>`(SSR 后的独立 SVG 水合入口)
|
|
4
4
|
* @description SSR / `mountSvg` 先渲染出含 `data-retikz-id` 的图,本入口再把 handler 绑回图元——不重建 DOM、
|
|
5
|
-
* 不接管状态,只做「图元 id ↔ 用户函数」绑定。用
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* 不接管状态,只做「图元 id ↔ 用户函数」绑定。用 `createHydrationController(root, handlers, locateSvg, buildContext)`
|
|
6
|
+
* 在 `root` 上挂根级委托:事件经 `closest('[data-retikz-id]')` 反查图元 id,命中即以 `(event, context)` 触发对应
|
|
7
|
+
* handler;`pointerEnter` / `pointerLeave` 由控制器经 `pointermove` + 命中 id 状态机合成。返回 `{ dispose }` 解绑。
|
|
8
|
+
*
|
|
9
|
+
* **context 能力分两档**:传 `options.scene`(可经 `toScene(ir)` 得到)→ 富 context——`meta` / `geometry` 经 Scene 按 id
|
|
10
|
+
* 聚合查询、`point` 逆 meet-fit、`animation` 经 `data-retikz-id` / `data-retikz-animation-owner` 双查
|
|
11
|
+
* `getAnimations()` per-id 控制;不传 → 最小 context——`id` + `element` + `root` + `renderer`,`point` 退 `getScreenCTM`
|
|
12
|
+
* (浏览器有效、jsdom 为 null),`meta` / `geometry` / `scene` undefined、`animation` no-op。SSR 后要富 context 须把
|
|
13
|
+
* `scene` 一并传入。
|
|
8
14
|
*/
|
|
9
15
|
export declare const hydrate: (root: Element, options: HydrateOptions) => HydrationHandle;
|
|
10
16
|
//# sourceMappingURL=hydrate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../../src/hydrate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"hydrate.d.ts","sourceRoot":"","sources":["../../src/hydrate.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,SAAS,cAAc,KAAG,eAahE,CAAC"}
|
package/dist/lib/index.cjs
CHANGED
|
@@ -8,12 +8,103 @@ const require_draw = require("./builder/draw.cjs");
|
|
|
8
8
|
const require_node = require("./builder/node.cjs");
|
|
9
9
|
const require_scope = require("./builder/scope.cjs");
|
|
10
10
|
const require_figure = require("./builder/figure.cjs");
|
|
11
|
+
let _retikz_core = require("@retikz/core");
|
|
12
|
+
Object.defineProperty(exports, "blink", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function() {
|
|
15
|
+
return _retikz_core.blink;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "cameraTo", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function() {
|
|
21
|
+
return _retikz_core.cameraTo;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "colorShift", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function() {
|
|
27
|
+
return _retikz_core.colorShift;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
11
30
|
exports.coordinate = require_coordinate.coordinate;
|
|
12
31
|
exports.draw = require_draw.draw;
|
|
32
|
+
Object.defineProperty(exports, "drawOn", {
|
|
33
|
+
enumerable: true,
|
|
34
|
+
get: function() {
|
|
35
|
+
return _retikz_core.drawOn;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
Object.defineProperty(exports, "fadeIn", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
get: function() {
|
|
41
|
+
return _retikz_core.fadeIn;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
13
44
|
exports.figure = require_figure.figure;
|
|
45
|
+
Object.defineProperty(exports, "flash", {
|
|
46
|
+
enumerable: true,
|
|
47
|
+
get: function() {
|
|
48
|
+
return _retikz_core.flash;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
Object.defineProperty(exports, "grow", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
get: function() {
|
|
54
|
+
return _retikz_core.grow;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
Object.defineProperty(exports, "growUp", {
|
|
58
|
+
enumerable: true,
|
|
59
|
+
get: function() {
|
|
60
|
+
return _retikz_core.growUp;
|
|
61
|
+
}
|
|
62
|
+
});
|
|
14
63
|
exports.hydrate = require_hydrate.hydrate;
|
|
64
|
+
Object.defineProperty(exports, "loop", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
get: function() {
|
|
67
|
+
return _retikz_core.loop;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
15
70
|
exports.mountCanvas = require_mountCanvas.mountCanvas;
|
|
16
71
|
exports.mountSvg = require_mountSvg.mountSvg;
|
|
17
72
|
exports.node = require_node.node;
|
|
73
|
+
Object.defineProperty(exports, "pulse", {
|
|
74
|
+
enumerable: true,
|
|
75
|
+
get: function() {
|
|
76
|
+
return _retikz_core.pulse;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
18
79
|
exports.renderToSvgString = require_renderToSvgString.renderToSvgString;
|
|
80
|
+
Object.defineProperty(exports, "scaleIn", {
|
|
81
|
+
enumerable: true,
|
|
82
|
+
get: function() {
|
|
83
|
+
return _retikz_core.scaleIn;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
19
86
|
exports.scope = require_scope.scope;
|
|
87
|
+
Object.defineProperty(exports, "slideIn", {
|
|
88
|
+
enumerable: true,
|
|
89
|
+
get: function() {
|
|
90
|
+
return _retikz_core.slideIn;
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
Object.defineProperty(exports, "spin", {
|
|
94
|
+
enumerable: true,
|
|
95
|
+
get: function() {
|
|
96
|
+
return _retikz_core.spin;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
Object.defineProperty(exports, "stagger", {
|
|
100
|
+
enumerable: true,
|
|
101
|
+
get: function() {
|
|
102
|
+
return _retikz_core.stagger;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
Object.defineProperty(exports, "wiggle", {
|
|
106
|
+
enumerable: true,
|
|
107
|
+
get: function() {
|
|
108
|
+
return _retikz_core.wiggle;
|
|
109
|
+
}
|
|
110
|
+
});
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export { renderToSvgString } from './renderToSvgString';
|
|
|
10
10
|
export { mountSvg } from './mountSvg';
|
|
11
11
|
export { hydrate } from './hydrate';
|
|
12
12
|
export { mountCanvas } from './mountCanvas';
|
|
13
|
+
export type { RetikzEventName, HydrationHandlers, HydrationHandler, HydrationContext, HydrationAnimationControls, HydrationGeometry, } from '@retikz/render/hydration';
|
|
14
|
+
export { fadeIn, drawOn, scaleIn, grow, growUp, slideIn, colorShift, cameraTo, pulse, spin, loop, flash, blink, wiggle, stagger, } from '@retikz/core';
|
|
15
|
+
export type { AnimationPresetOptions, ScaleInOptions, GrowUpOptions, SlideInOptions, ColorShiftOptions, CameraToOptions, PulseOptions, SpinOptions, LoopOptions, FlashOptions, BlinkOptions, WiggleOptions, } from '@retikz/core';
|
|
13
16
|
export { figure } from './builder/figure';
|
|
14
17
|
export { node } from './builder/node';
|
|
15
18
|
export { draw } from './builder/draw';
|
|
@@ -18,5 +21,5 @@ export { scope } from './builder/scope';
|
|
|
18
21
|
export type { Figure } from './figure';
|
|
19
22
|
export type { ScopeBuilder } from './builder/scope';
|
|
20
23
|
export type { Child, NodeConfig, DrawConfig, CoordinateConfig, ScopeConfig, FigureConfig, Way, } from './builder/types';
|
|
21
|
-
export type { RenderInput, CommonOptions, MountOptions, RenderToStringOptions, VanillaView, CanvasView, MountCanvasOptions, HydrateOptions, HydrationHandle, ScenePoint, } from './types';
|
|
24
|
+
export type { RenderInput, CommonOptions, MountOptions, RenderToStringOptions, VanillaView, CanvasView, MountCanvasOptions, HydrateOptions, HydrationHandle, ScenePoint, AnimationControls, } from './types';
|
|
22
25
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,0BAA0B,EAC1B,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,MAAM,EACN,OAAO,EACP,UAAU,EACV,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,KAAK,EACL,MAAM,EACN,OAAO,GACR,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,sBAAsB,EACtB,cAAc,EACd,aAAa,EACb,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,WAAW,EACX,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,YAAY,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EACV,KAAK,EACL,UAAU,EACV,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,GAAG,GACJ,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,WAAW,EACX,UAAU,EACV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,UAAU,EACV,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
|
package/dist/lib/mountCanvas.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_isFigure = require("./builder/isFigure.cjs");
|
|
2
2
|
const require_toScene = require("./toScene.cjs");
|
|
3
|
+
let _retikz_render_animation = require("@retikz/render/animation");
|
|
3
4
|
let _retikz_render_hydration = require("@retikz/render/hydration");
|
|
4
5
|
let _retikz_render_canvas = require("@retikz/render/canvas");
|
|
5
6
|
//#region src/mountCanvas.ts
|
|
@@ -20,7 +21,27 @@ var mountCanvas = (container, input, options = {}) => {
|
|
|
20
21
|
if (typeof Element === "undefined" || !(container instanceof Element)) throw new Error("mountCanvas: container must be a DOM Element.");
|
|
21
22
|
const canvas = document.createElement("canvas");
|
|
22
23
|
const ratio = resolveDevicePixelRatio(options.devicePixelRatio);
|
|
24
|
+
const animate = options.animate !== false && !(0, _retikz_render_animation.prefersReducedMotion)();
|
|
25
|
+
let clock;
|
|
26
|
+
const registry = (0, _retikz_render_animation.createIdClockRegistry)();
|
|
23
27
|
let currentScene;
|
|
28
|
+
/** 把全局帧时刻折算成单个 prim 的动画解析(per-id):stop→渲染 base;否则按有效时刻 + 是否含非自动播 track */
|
|
29
|
+
const resolvePrim = (id, globalTime) => id !== void 0 && registry.isStopped(id) ? { mode: "skip" } : {
|
|
30
|
+
mode: "at",
|
|
31
|
+
time: registry.timeFor(id, globalTime),
|
|
32
|
+
includeNonAutoplay: registry.isActive(id)
|
|
33
|
+
};
|
|
34
|
+
/** 按当前时钟时刻 + per-id 登记表立即重绘一帧(per-id pause / stop 即时反映) */
|
|
35
|
+
const renderFrame = () => {
|
|
36
|
+
const time = clock?.time ?? 0;
|
|
37
|
+
(0, _retikz_render_canvas.renderToCanvas)(canvas, currentScene, {
|
|
38
|
+
devicePixelRatio: ratio,
|
|
39
|
+
time,
|
|
40
|
+
easings: options.easings,
|
|
41
|
+
animationProperties: options.animationProperties,
|
|
42
|
+
resolvePrimAnimation: (id) => resolvePrim(id, time)
|
|
43
|
+
});
|
|
44
|
+
};
|
|
24
45
|
const renderInto = (next) => {
|
|
25
46
|
if (require_isFigure.isFigure(next)) throw new Error("mountCanvas: view.update does not accept a Figure; pass figure.ir instead.");
|
|
26
47
|
const scene = require_toScene.toScene(next, options);
|
|
@@ -34,6 +55,21 @@ var mountCanvas = (container, input, options = {}) => {
|
|
|
34
55
|
if (options.height !== void 0) canvas.style.height = `${options.height}px`;
|
|
35
56
|
canvas.style.objectFit = "contain";
|
|
36
57
|
(0, _retikz_render_canvas.renderToCanvas)(canvas, scene, { devicePixelRatio: ratio });
|
|
58
|
+
clock?.dispose();
|
|
59
|
+
clock = void 0;
|
|
60
|
+
if (animate && (0, _retikz_render_animation.sceneHasAnimations)(scene)) {
|
|
61
|
+
clock = (0, _retikz_render_animation.createClock)({
|
|
62
|
+
durationMs: (0, _retikz_render_animation.sceneAnimationDurationMs)(scene),
|
|
63
|
+
onFrame: (time) => (0, _retikz_render_canvas.renderToCanvas)(canvas, currentScene, {
|
|
64
|
+
devicePixelRatio: ratio,
|
|
65
|
+
time,
|
|
66
|
+
easings: options.easings,
|
|
67
|
+
animationProperties: options.animationProperties,
|
|
68
|
+
resolvePrimAnimation: (id) => resolvePrim(id, time)
|
|
69
|
+
})
|
|
70
|
+
});
|
|
71
|
+
if ((0, _retikz_render_animation.sceneHasAutoplayTrigger)(scene)) clock.play();
|
|
72
|
+
}
|
|
37
73
|
};
|
|
38
74
|
renderInto(require_isFigure.isFigure(input) ? input.ir : input);
|
|
39
75
|
container.appendChild(canvas);
|
|
@@ -68,11 +104,32 @@ var mountCanvas = (container, input, options = {}) => {
|
|
|
68
104
|
*/
|
|
69
105
|
const hydrate = (hydrateOptions) => {
|
|
70
106
|
const context2d = canvas.getContext("2d") ?? void 0;
|
|
71
|
-
|
|
107
|
+
const locate = (event) => {
|
|
72
108
|
const scenePoint = clientToScene(event.clientX, event.clientY);
|
|
73
109
|
context2d?.setTransform(1, 0, 0, 1, 0, 0);
|
|
74
110
|
return (0, _retikz_render_canvas.hitTest)(currentScene, scenePoint, { context2d });
|
|
75
|
-
}
|
|
111
|
+
};
|
|
112
|
+
const buildContext = (event, id) => {
|
|
113
|
+
const mouse = event;
|
|
114
|
+
return {
|
|
115
|
+
id,
|
|
116
|
+
meta: (0, _retikz_render_hydration.metaOf)(currentScene, id),
|
|
117
|
+
renderer: "canvas",
|
|
118
|
+
element: null,
|
|
119
|
+
root: canvas,
|
|
120
|
+
point: typeof mouse.clientX === "number" ? clientToScene(mouse.clientX, mouse.clientY) : null,
|
|
121
|
+
geometry: (0, _retikz_render_hydration.geometryOf)(currentScene, id),
|
|
122
|
+
animation: (0, _retikz_render_hydration.createCanvasIdAnimationControls)({
|
|
123
|
+
registry,
|
|
124
|
+
clockTime: () => clock?.time ?? 0,
|
|
125
|
+
ensurePlaying: () => clock?.play(),
|
|
126
|
+
renderFrame,
|
|
127
|
+
defaultId: id
|
|
128
|
+
}),
|
|
129
|
+
scene: currentScene
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
return { dispose: (0, _retikz_render_hydration.createHydrationController)(canvas, hydrateOptions.handlers, locate, buildContext).dispose };
|
|
76
133
|
};
|
|
77
134
|
return {
|
|
78
135
|
root: canvas,
|
|
@@ -83,10 +140,14 @@ var mountCanvas = (container, input, options = {}) => {
|
|
|
83
140
|
dispose() {
|
|
84
141
|
if (disposed) return;
|
|
85
142
|
disposed = true;
|
|
143
|
+
clock?.dispose();
|
|
86
144
|
canvas.remove();
|
|
87
145
|
},
|
|
88
146
|
hydrate,
|
|
89
|
-
clientToScene
|
|
147
|
+
clientToScene,
|
|
148
|
+
get animation() {
|
|
149
|
+
return clock;
|
|
150
|
+
}
|
|
90
151
|
};
|
|
91
152
|
};
|
|
92
153
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mountCanvas.d.ts","sourceRoot":"","sources":["../../src/mountCanvas.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mountCanvas.d.ts","sourceRoot":"","sources":["../../src/mountCanvas.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,UAAU,EAAkB,kBAAkB,EAAE,WAAW,EAAc,MAAM,SAAS,CAAC;AASvG;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GACtB,WAAW,OAAO,EAClB,OAAO,WAAW,EAClB,UAAS,kBAAuB,KAC/B,UA6JF,CAAC"}
|
package/dist/lib/mountSvg.cjs
CHANGED
|
@@ -2,6 +2,8 @@ const require_isFigure = require("./builder/isFigure.cjs");
|
|
|
2
2
|
const require_toScene = require("./toScene.cjs");
|
|
3
3
|
const require_svgNodeToDom = require("./svgNodeToDom.cjs");
|
|
4
4
|
let _retikz_render_svg = require("@retikz/render/svg");
|
|
5
|
+
let _retikz_render_animation = require("@retikz/render/animation");
|
|
6
|
+
let _retikz_render_hydration = require("@retikz/render/hydration");
|
|
5
7
|
//#region src/mountSvg.ts
|
|
6
8
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
7
9
|
/** 默认资源 id 前缀(确定性);多实例同页须经 `options.idPrefix` 显式区分 */
|
|
@@ -18,18 +20,50 @@ var mountSvg = (container, input, options = {}) => {
|
|
|
18
20
|
if (typeof Element === "undefined" || !(container instanceof Element)) throw new Error("mountSvg: container must be a DOM Element.");
|
|
19
21
|
const idPrefix = options.idPrefix ?? DEFAULT_ID_PREFIX;
|
|
20
22
|
const root = document.createElementNS(SVG_NS, "svg");
|
|
23
|
+
const animate = options.animate !== false && !(0, _retikz_render_animation.prefersReducedMotion)();
|
|
24
|
+
let animationControls;
|
|
25
|
+
let currentScene;
|
|
21
26
|
const renderInto = (next) => {
|
|
22
27
|
if (require_isFigure.isFigure(next)) throw new Error("mountSvg: view.update does not accept a Figure; pass figure.ir instead.");
|
|
23
|
-
const
|
|
28
|
+
const scene = require_toScene.toScene(next, options);
|
|
29
|
+
currentScene = scene;
|
|
30
|
+
const doc = (0, _retikz_render_svg.buildSvgDocument)(scene, {
|
|
31
|
+
idPrefix,
|
|
32
|
+
animate,
|
|
33
|
+
snapshotAt: options.snapshotAt,
|
|
34
|
+
easings: options.easings
|
|
35
|
+
});
|
|
24
36
|
while (root.firstChild) root.removeChild(root.firstChild);
|
|
25
37
|
for (const attr of [...root.attributes]) root.removeAttribute(attr.name);
|
|
26
38
|
require_svgNodeToDom.applyAttrs(root, doc);
|
|
27
39
|
if (options.width !== void 0) root.setAttribute("width", String(options.width));
|
|
28
40
|
if (options.height !== void 0) root.setAttribute("height", String(options.height));
|
|
29
41
|
for (const child of doc.children ?? []) root.appendChild(typeof child === "string" ? document.createTextNode(child) : require_svgNodeToDom.svgNodeToDom(child));
|
|
42
|
+
animationControls?.dispose();
|
|
43
|
+
animationControls = animate && (0, _retikz_render_animation.sceneHasAnimations)(scene) ? (0, _retikz_render_animation.bindWaapiDescriptors)(root) : void 0;
|
|
30
44
|
};
|
|
31
45
|
renderInto(input);
|
|
32
46
|
container.appendChild(root);
|
|
47
|
+
/**
|
|
48
|
+
* 把 handler 绑到本 view 的 `<svg>`,handler 收 `(event, context)` 富上下文
|
|
49
|
+
* @description `buildContext` 读 live `currentScene`(`update` 后自动反映新图):meta / geometry 经 Scene 按 id
|
|
50
|
+
* 聚合查询,element 经 `closest('[data-retikz-id]')`,point 逆 meet-fit,动画控制经 `data-retikz-id` /
|
|
51
|
+
* `data-retikz-animation-owner` 双查 `getAnimations()` per-id 控制。
|
|
52
|
+
*/
|
|
53
|
+
const hydrate = (hydrateOptions) => {
|
|
54
|
+
const buildContext = (event, id) => ({
|
|
55
|
+
id,
|
|
56
|
+
meta: (0, _retikz_render_hydration.metaOf)(currentScene, id),
|
|
57
|
+
renderer: "svg",
|
|
58
|
+
element: (0, _retikz_render_hydration.resolveSvgElement)(event),
|
|
59
|
+
root,
|
|
60
|
+
point: (0, _retikz_render_hydration.resolvePointViaLayout)(root, currentScene.layout)(event),
|
|
61
|
+
geometry: (0, _retikz_render_hydration.geometryOf)(currentScene, id),
|
|
62
|
+
animation: (0, _retikz_render_hydration.createSvgAnimationControls)(root, id),
|
|
63
|
+
scene: currentScene
|
|
64
|
+
});
|
|
65
|
+
return { dispose: (0, _retikz_render_hydration.createHydrationController)(root, hydrateOptions.handlers, _retikz_render_hydration.locateSvg, buildContext).dispose };
|
|
66
|
+
};
|
|
33
67
|
let disposed = false;
|
|
34
68
|
return {
|
|
35
69
|
root,
|
|
@@ -37,10 +71,15 @@ var mountSvg = (container, input, options = {}) => {
|
|
|
37
71
|
if (disposed) throw new Error("mountSvg: view already disposed.");
|
|
38
72
|
renderInto(next);
|
|
39
73
|
},
|
|
74
|
+
hydrate,
|
|
40
75
|
dispose() {
|
|
41
76
|
if (disposed) return;
|
|
42
77
|
disposed = true;
|
|
78
|
+
animationControls?.dispose();
|
|
43
79
|
root.remove();
|
|
80
|
+
},
|
|
81
|
+
get animation() {
|
|
82
|
+
return animationControls;
|
|
44
83
|
}
|
|
45
84
|
};
|
|
46
85
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mountSvg.d.ts","sourceRoot":"","sources":["../../src/mountSvg.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"mountSvg.d.ts","sourceRoot":"","sources":["../../src/mountSvg.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAmC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAMvG;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,GAAI,WAAW,OAAO,EAAE,OAAO,WAAW,EAAE,UAAS,YAAiB,KAAG,WA4E7F,CAAC"}
|
|
@@ -17,7 +17,12 @@ var injectSize = (svg, width, height) => {
|
|
|
17
17
|
*/
|
|
18
18
|
var renderToSvgString = (input, options = {}) => {
|
|
19
19
|
if (require_isFigure.isFigure(input)) return input.toSvgString(options);
|
|
20
|
-
return injectSize((0, _retikz_render_svg.renderToSvgString)(require_toScene.toScene(input, options), {
|
|
20
|
+
return injectSize((0, _retikz_render_svg.renderToSvgString)(require_toScene.toScene(input, options), {
|
|
21
|
+
idPrefix: options.idPrefix ?? DEFAULT_ID_PREFIX,
|
|
22
|
+
animate: options.animate,
|
|
23
|
+
snapshotAt: options.snapshotAt,
|
|
24
|
+
easings: options.easings
|
|
25
|
+
}), options.width, options.height);
|
|
21
26
|
};
|
|
22
27
|
//#endregion
|
|
23
28
|
exports.renderToSvgString = renderToSvgString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderToSvgString.d.ts","sourceRoot":"","sources":["../../src/renderToSvgString.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAYlE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,WAAW,EAAE,UAAS,qBAA0B,KAAG,
|
|
1
|
+
{"version":3,"file":"renderToSvgString.d.ts","sourceRoot":"","sources":["../../src/renderToSvgString.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAYlE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAAI,OAAO,WAAW,EAAE,UAAS,qBAA0B,KAAG,MAS3F,CAAC"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { CompileOptions, IR, Scene } from '@retikz/core';
|
|
2
2
|
import { HydrationHandlers } from '@retikz/render/hydration';
|
|
3
|
+
import { AnimationControls, AnimationPropertyRegistry, EasingRegistry } from '@retikz/render/animation';
|
|
3
4
|
import { Figure } from './figure';
|
|
5
|
+
export type { AnimationControls } from '@retikz/render/animation';
|
|
4
6
|
/** mountSvg / renderToSvgString 的入参:已编译 `Scene`、待编译 `IR`,或命令式 builder 的 `Figure` */
|
|
5
7
|
export type RenderInput = Scene | IR | Figure;
|
|
6
8
|
/**
|
|
@@ -14,6 +16,18 @@ export type CommonOptions = {
|
|
|
14
16
|
idPrefix?: string;
|
|
15
17
|
width?: number;
|
|
16
18
|
height?: number;
|
|
19
|
+
/**
|
|
20
|
+
* 是否播放动画(缺省 true);`false` → 渲染 base 静态图(不 emit CSS/WAAPI、Canvas 不起 rAF)
|
|
21
|
+
* @description runtime 据 `{animate:false}` 或 `prefers-reduced-motion` 走静态路径(ADR-01「三事一路」)。
|
|
22
|
+
*/
|
|
23
|
+
animate?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* 静态截帧时刻(毫秒);给定时渲染「定格在该时刻」的静态图(不播放、不 emit 动画),SSR 海报帧 / 缩略图用
|
|
26
|
+
* @description SVG 后端把各 track 在该时刻的值烘焙成静态属性 / transform(复用 `evaluateTrack`);覆盖 `animate`。
|
|
27
|
+
*/
|
|
28
|
+
snapshotAt?: number;
|
|
29
|
+
/** 自定义 easing 注册表(透传 renderer / runtime) */
|
|
30
|
+
easings?: EasingRegistry;
|
|
17
31
|
} & CompileOptions;
|
|
18
32
|
export type RenderToStringOptions = CommonOptions;
|
|
19
33
|
export type MountOptions = CommonOptions;
|
|
@@ -25,16 +39,33 @@ export type VanillaView = {
|
|
|
25
39
|
update: (next: RenderInput) => void;
|
|
26
40
|
/** 卸载:移除 `root`、置 view 失效(再调 `update` 抛、`dispose` noop) */
|
|
27
41
|
dispose: () => void;
|
|
42
|
+
/**
|
|
43
|
+
* 绑定 handler 到本 view 的 `<svg>`(locateSvg 定位);handler 收 `(event, context)` 富上下文
|
|
44
|
+
* @description context 由本 view 的 Scene 构造(meta / geometry / per-id 动画控制),读 live `currentScene`——
|
|
45
|
+
* `update` 后 context 自动反映新图(无需重 hydrate)。`HydrateOptions.scene` / `renderer` 在 view.hydrate 下忽略。
|
|
46
|
+
*/
|
|
47
|
+
hydrate: (options: HydrateOptions) => HydrationHandle;
|
|
48
|
+
/** 动画播放控制句柄(scene 含动画且未降级时存在):play / pause / seek;manual trigger 经此驱动 */
|
|
49
|
+
animation?: AnimationControls;
|
|
28
50
|
};
|
|
29
51
|
/** `hydrate` / `view.hydrate` 返回的解绑句柄 */
|
|
30
52
|
export type HydrationHandle = {
|
|
31
53
|
/** 解绑本次水合的全部 listener,之后事件不再触发 */
|
|
32
54
|
dispose: () => void;
|
|
33
55
|
};
|
|
34
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* 水合入参:按 id 提供的 handler 注册表(事件名 → handler)+ 可选 Scene(富 context 来源)
|
|
58
|
+
* @description `view.hydrate`(mountSvg / mountCanvas)忽略 `scene` / `renderer`、用自身 Scene 构造富 context;
|
|
59
|
+
* standalone `hydrate(root, options)`(SSR 后独立入口)传 `scene` → 富 context(meta / geometry / 动画),
|
|
60
|
+
* 不传 → 最小 context(id + element + root + point,`meta` / `geometry` / `scene` undefined、`animation` no-op)。
|
|
61
|
+
*/
|
|
35
62
|
export type HydrateOptions = {
|
|
36
63
|
/** id → 事件名 → handler 的注册表(透传给 `@retikz/render/hydration` 控制器) */
|
|
37
64
|
handlers: HydrationHandlers;
|
|
65
|
+
/** 富 context 来源 Scene(仅 standalone `hydrate` 用;不传则最小 context);可经 `toScene(ir)` 得到 */
|
|
66
|
+
scene?: Scene;
|
|
67
|
+
/** standalone `hydrate` 的渲染后端(缺省 `'svg'`);决定 context.renderer 与 element 定位口径 */
|
|
68
|
+
renderer?: 'svg' | 'canvas';
|
|
38
69
|
};
|
|
39
70
|
/** Scene user units 坐标点(hitTest 入参 / 坐标映射出参) */
|
|
40
71
|
export type ScenePoint = {
|
|
@@ -63,10 +94,14 @@ export type CanvasView = {
|
|
|
63
94
|
* 交给 `hitTest` 自然判为无命中(无需在此截断),故无 `null` 返回。
|
|
64
95
|
*/
|
|
65
96
|
clientToScene: (clientX: number, clientY: number) => ScenePoint;
|
|
97
|
+
/** 动画播放控制句柄(scene 含动画且未降级时存在):rAF 时钟的 play / pause / seek */
|
|
98
|
+
animation?: AnimationControls;
|
|
66
99
|
};
|
|
67
100
|
/** `mountCanvas` 选项:继承 SSR / compile 公共项,外加 canvas 显示 / dpr 透传 */
|
|
68
101
|
export type MountCanvasOptions = CommonOptions & {
|
|
69
102
|
/** 设备像素比;缺省读 `globalThis.devicePixelRatio`、再回退 1(镜像 react CanvasHost) */
|
|
70
103
|
devicePixelRatio?: number;
|
|
104
|
+
/** 自定义 property 插值器注册表(透传 drawScene;自定义动画通道用) */
|
|
105
|
+
animationProperties?: AnimationPropertyRegistry;
|
|
71
106
|
};
|
|
72
107
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/lib/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC7G,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,oFAAoF;AACpF,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,EAAE,GAAG,MAAM,CAAC;AAE9C;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,GAAG,cAAc,CAAC;AAEnB,MAAM,MAAM,qBAAqB,GAAG,aAAa,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC;AAEzC,6DAA6D;AAC7D,MAAM,MAAM,WAAW,GAAG;IACxB,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,gEAAgE;IAChE,MAAM,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACpC,2DAA2D;IAC3D,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB;;;;OAIG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,eAAe,CAAC;IACtD,yEAAyE;IACzE,SAAS,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF,yCAAyC;AACzC,MAAM,MAAM,eAAe,GAAG;IAC5B,kCAAkC;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,kEAAkE;IAClE,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,qFAAqF;IACrF,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,gFAAgF;IAChF,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC7B,CAAC;AAEF,gDAAgD;AAChD,MAAM,MAAM,UAAU,GAAG;IACvB,2BAA2B;IAC3B,CAAC,EAAE,MAAM,CAAC;IACV,2BAA2B;IAC3B,CAAC,EAAE,MAAM,CAAC;CACX,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,iDAAiD;IACjD,MAAM,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IACpC,gEAAgE;IAChE,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,4FAA4F;IAC5F,OAAO,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,eAAe,CAAC;IACtD;;;;OAIG;IACH,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,UAAU,CAAC;IAChE,6DAA6D;IAC7D,SAAS,CAAC,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEF,kEAAkE;AAClE,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG;IAC/C,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,mBAAmB,CAAC,EAAE,yBAAyB,CAAC;CACjD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retikz/vanilla",
|
|
3
|
-
"version": "0.3.0-alpha.
|
|
3
|
+
"version": "0.3.0-alpha.5",
|
|
4
4
|
"description": "Framework-free runtime for retikz: mount Scene/IR to SVG DOM, or render to SVG string for SSR.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Pionpill",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
18
|
"url": "git+https://github.com/Pionpill/retikz.git",
|
|
19
|
-
"directory": "packages/vanilla"
|
|
19
|
+
"directory": "packages/core/vanilla"
|
|
20
20
|
},
|
|
21
21
|
"bugs": {
|
|
22
22
|
"url": "https://github.com/Pionpill/retikz/issues"
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"dist/**/*"
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@retikz/core": "0.3.0-alpha.
|
|
43
|
-
"@retikz/render": "0.3.0-alpha.
|
|
42
|
+
"@retikz/core": "0.3.0-alpha.5",
|
|
43
|
+
"@retikz/render": "0.3.0-alpha.5"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^25.6.0",
|