flowsvg 0.1.0 → 0.2.0
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/CHANGELOG.md +54 -1
- package/PATTERNS.md +71 -4
- package/README.md +148 -438
- package/README.zh-CN.md +148 -438
- package/dist/adapters/react.d.ts +55 -0
- package/dist/adapters/react.js +138 -0
- package/dist/adapters/vue.d.ts +158 -0
- package/dist/adapters/vue.js +177 -0
- package/dist/adapters/web-component.d.ts +111 -0
- package/dist/adapters/web-component.js +425 -0
- package/dist/{player-controls.d.ts → browser/controls.d.ts} +7 -3
- package/dist/{player-controls.js → browser/controls.js} +23 -31
- package/dist/browser/export-controls.d.ts +32 -0
- package/dist/browser/export-controls.js +151 -0
- package/dist/browser/player.d.ts +77 -0
- package/dist/browser/player.js +412 -0
- package/dist/browser/select-control.d.ts +33 -0
- package/dist/browser/select-control.js +171 -0
- package/dist/browser.d.ts +5 -69
- package/dist/browser.js +3 -353
- package/dist/core/color.d.ts +3 -0
- package/dist/core/color.js +18 -0
- package/dist/core/diagram.d.ts +40 -0
- package/dist/core/diagram.js +481 -0
- package/dist/core/edge-geometry.d.ts +45 -0
- package/dist/core/edge-geometry.js +609 -0
- package/dist/{core.d.ts → core/model.d.ts} +37 -61
- package/dist/core/model.js +1 -0
- package/dist/core/node-content-measurement.d.ts +34 -0
- package/dist/core/node-content-measurement.js +66 -0
- package/dist/{node-content-paint.d.ts → core/node-content-paint.d.ts} +2 -2
- package/dist/{node-content-paint.js → core/node-content-paint.js} +11 -20
- package/dist/{node-content.d.ts → core/node-content.d.ts} +18 -1
- package/dist/{node-content.js → core/node-content.js} +30 -6
- package/dist/core/svg-number.d.ts +5 -0
- package/dist/core/svg-number.js +7 -0
- package/dist/{theme.d.ts → core/theme.d.ts} +6 -0
- package/dist/core/theme.js +17 -0
- package/dist/{timeline.d.ts → core/timeline.d.ts} +11 -5
- package/dist/{timeline.js → core/timeline.js} +30 -22
- package/dist/core/validation.d.ts +11 -0
- package/dist/core/validation.js +399 -0
- package/dist/export/browser.d.ts +9 -0
- package/dist/export/browser.js +172 -0
- package/dist/export/captions.d.ts +18 -0
- package/dist/export/captions.js +44 -0
- package/dist/export/plan.d.ts +25 -0
- package/dist/export/plan.js +94 -0
- package/dist/export/types.d.ts +64 -0
- package/dist/export/types.js +9 -0
- package/dist/export/worker-source.d.ts +1 -0
- package/dist/export/worker-source.js +1 -0
- package/dist/export.d.ts +3 -0
- package/dist/export.js +2 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +3 -1
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +72 -0
- package/dist/node/gif.d.ts +16 -0
- package/dist/node/gif.js +184 -0
- package/dist/node/html.d.ts +19 -0
- package/dist/node/html.js +61 -0
- package/dist/node/output.d.ts +2 -0
- package/dist/node/output.js +22 -0
- package/dist/node.d.ts +6 -0
- package/dist/node.js +3 -0
- package/dist/portable/LICENSE.esbuild.txt +21 -0
- package/dist/portable/LICENSE.flowsvg.txt +21 -0
- package/dist/portable/LICENSE.gifenc.txt +21 -0
- package/dist/portable/export.js +5 -0
- package/dist/portable/manifest.json +36 -0
- package/dist/portable/player.js +2 -0
- package/dist/portable/styles.css +227 -0
- package/dist/react.d.ts +3 -48
- package/dist/react.js +2 -126
- package/dist/styles.css +194 -14
- package/dist/vue.d.ts +3 -132
- package/dist/vue.js +2 -161
- package/dist/web-component.d.ts +3 -103
- package/dist/web-component.js +2 -396
- package/docs/images/entity-graph.png +0 -0
- package/docs/images/facts-timeline.png +0 -0
- package/docs/images/facts-timeline.zh-CN.png +0 -0
- package/docs/images/flowsvg-wordmark.gif +0 -0
- package/docs/images/node-content.png +0 -0
- package/docs/images/numbered-list.png +0 -0
- package/docs/images/numbered-list.zh-CN.png +0 -0
- package/docs/images/quick-start.png +0 -0
- package/docs/images/record-tags.png +0 -0
- package/package.json +38 -6
- package/dist/core.js +0 -1149
- package/dist/theme.js +0 -6
- /package/dist/{player-accessibility.d.ts → browser/accessibility.d.ts} +0 -0
- /package/dist/{player-accessibility.js → browser/accessibility.js} +0 -0
- /package/dist/{player-lifecycle.d.ts → browser/lifecycle.d.ts} +0 -0
- /package/dist/{player-lifecycle.js → browser/lifecycle.js} +0 -0
- /package/dist/{svg-dom.d.ts → browser/svg-dom.d.ts} +0 -0
- /package/dist/{svg-dom.js → browser/svg-dom.js} +0 -0
package/README.zh-CN.md
CHANGED
|
@@ -1,525 +1,235 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://cdn.jsdelivr.net/npm/flowsvg@0.2.0/docs/images/flowsvg-wordmark.gif" alt="FlowSVG" width="280">
|
|
3
|
+
</p>
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
<p align="center">声明式 SVG 图解,支持时间线播放与 SVG/GIF 导出。</p>
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
<p align="center"><a href="https://cdn.jsdelivr.net/npm/flowsvg@0.2.0/README.md">English</a> | 简体中文</p>
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="#定义图解">快速开始</a> ·
|
|
11
|
+
<a href="#控制播放">播放 API</a> ·
|
|
12
|
+
<a href="#核心-api--flowsvg">核心 API</a> ·
|
|
13
|
+
<a href="#文档与动作">文档模型</a> ·
|
|
14
|
+
<a href="#导出-api">导出 API</a>
|
|
15
|
+
</p>
|
|
8
16
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
## 安装
|
|
12
|
-
|
|
13
|
-
从 npm 安装:
|
|
14
|
-
|
|
15
|
-
```sh
|
|
16
|
-
pnpm add flowsvg
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
按所选入口安装框架依赖:
|
|
20
|
-
|
|
21
|
-
```sh
|
|
22
|
-
# Vue 应用
|
|
23
|
-
pnpm add vue
|
|
24
|
-
# React 应用
|
|
25
|
-
pnpm add react react-dom
|
|
26
|
-
# React TypeScript 应用
|
|
27
|
-
pnpm add -D @types/react @types/react-dom
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
核心与原生浏览器入口无需安装框架。发布前可在源码仓库根目录构建本地 tarball:
|
|
17
|
+
同一份图解数据可用于原生浏览器、Vue、React 和 Web Component。
|
|
31
18
|
|
|
32
19
|
```sh
|
|
33
|
-
|
|
34
|
-
pnpm --filter flowsvg exec npm pack --pack-destination /tmp
|
|
20
|
+
npm install flowsvg
|
|
35
21
|
```
|
|
36
22
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
运行核心需要 Node.js 18+ 或支持 ES2022 ESM 的环境;浏览器播放器需要 DOM、SVG 与 `requestAnimationFrame`。框架最低版本为 Vue 3.5、React 18。TypeScript 类型随包提供,React TypeScript 应用还需安装对应的 `@types/react` 和 `@types/react-dom`。
|
|
40
|
-
|
|
41
|
-
| 导入路径 | 用途 |
|
|
42
|
-
| --- | --- |
|
|
43
|
-
| `flowsvg` | 无 DOM 的文档校验、编译、求值、命中与 SVG 导出 |
|
|
44
|
-
| `flowsvg/browser` | 原生浏览器播放器 |
|
|
45
|
-
| `flowsvg/vue` | Vue 组件与实例类型 |
|
|
46
|
-
| `flowsvg/react` | React 组件与 ref 类型 |
|
|
47
|
-
| `flowsvg/web-component` | 显式注册自定义元素;也可使用 `flowsvg/element` |
|
|
48
|
-
| `flowsvg/styles.css` | 控件布局与可见焦点样式 |
|
|
49
|
-
|
|
50
|
-
## 快速开始
|
|
51
|
-
|
|
52
|
-
### 1. 创建图解文档
|
|
53
|
-
|
|
54
|
-
将下面内容保存为 `diagram.ts`,后续集成示例复用此文件:
|
|
23
|
+
## 定义图解
|
|
55
24
|
|
|
56
25
|
```ts
|
|
26
|
+
// diagram.ts
|
|
57
27
|
import type { DiagramDocument } from "flowsvg";
|
|
58
28
|
|
|
59
|
-
export const
|
|
29
|
+
export const diagram: DiagramDocument = {
|
|
60
30
|
version: 1,
|
|
61
|
-
|
|
62
|
-
viewport: { width: 640, height: 280 },
|
|
31
|
+
viewport: { width: 640, height: 240 },
|
|
63
32
|
nodes: [
|
|
64
|
-
{ id: "client", kind: "roundedRect", x: 40, y:
|
|
65
|
-
{ id: "service", kind: "
|
|
33
|
+
{ id: "client", kind: "roundedRect", x: 40, y: 90, width: 140, height: 64, label: "Client" },
|
|
34
|
+
{ id: "service", kind: "roundedRect", x: 400, y: 90, width: 180, height: 64, label: "Service" }
|
|
66
35
|
],
|
|
67
|
-
edges: [{ id: "request", from: "client", to: "service",
|
|
36
|
+
edges: [{ id: "request", from: "client", to: "service", route: "orthogonal" }],
|
|
68
37
|
scenes: [{
|
|
69
|
-
id: "request-
|
|
70
|
-
label: "
|
|
38
|
+
id: "request-response",
|
|
39
|
+
label: "请求与响应",
|
|
71
40
|
beats: [
|
|
72
|
-
{ durationMs: 1000,
|
|
73
|
-
|
|
41
|
+
{ durationMs: 1000, caption: "发送请求", actions: [
|
|
42
|
+
{ type: "flow", target: "request", direction: "forward", text: "Request" }
|
|
43
|
+
] },
|
|
44
|
+
{ durationMs: 1000, caption: "返回响应", actions: [
|
|
45
|
+
{ type: "flow", target: "request", direction: "reverse" }
|
|
46
|
+
] }
|
|
74
47
|
]
|
|
75
48
|
}]
|
|
76
49
|
};
|
|
77
50
|
```
|
|
78
51
|
|
|
79
|
-
|
|
52
|
+
节点显式指定位置和尺寸。连线支持 `straight`、`orthogonal`(圆角折线)和 `curved`。传递设置 `text` 时显示文字胶囊,省略时显示移动实心圆点。
|
|
53
|
+
|
|
54
|
+
## 播放
|
|
55
|
+
|
|
56
|
+
```html
|
|
57
|
+
<div id="diagram"></div>
|
|
58
|
+
```
|
|
80
59
|
|
|
81
60
|
```ts
|
|
82
|
-
import {
|
|
83
|
-
import
|
|
61
|
+
import { createPlayer } from "flowsvg/browser";
|
|
62
|
+
import "flowsvg/styles.css";
|
|
63
|
+
import { diagram } from "./diagram.js";
|
|
84
64
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
65
|
+
const player = createPlayer(document.querySelector<HTMLElement>("#diagram")!, diagram, {
|
|
66
|
+
theme: "light",
|
|
67
|
+
loop: true,
|
|
68
|
+
gifExport: true
|
|
69
|
+
});
|
|
88
70
|
|
|
89
|
-
|
|
90
|
-
|
|
71
|
+
player.play();
|
|
72
|
+
// On disposal: player.destroy();
|
|
91
73
|
```
|
|
92
74
|
|
|
93
|
-
|
|
75
|
+
播放器默认暂停,内置播放控件。循环和 GIF 下载默认关闭。样式只需导入一次。原生播放器卸载时调用 `destroy()`;框架组件自动清理。
|
|
94
76
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
在 Vue 应用中创建组件,`diagram.ts` 与组件放在同一目录:
|
|
77
|
+
### Vue
|
|
98
78
|
|
|
99
79
|
```vue
|
|
100
80
|
<script setup lang="ts">
|
|
101
|
-
import {
|
|
102
|
-
import { FlowPlayer, type FlowPlayerHandle } from "flowsvg/vue";
|
|
103
|
-
import type { PlayerErrorDetail } from "flowsvg/browser";
|
|
81
|
+
import { FlowPlayer } from "flowsvg/vue";
|
|
104
82
|
import "flowsvg/styles.css";
|
|
105
|
-
import {
|
|
106
|
-
|
|
107
|
-
const player = ref<FlowPlayerHandle | null>(null);
|
|
108
|
-
const timeMs = ref(0);
|
|
109
|
-
|
|
110
|
-
function handleError({ error }: PlayerErrorDetail): void {
|
|
111
|
-
console.error(error);
|
|
112
|
-
}
|
|
83
|
+
import { diagram } from "./diagram.js";
|
|
113
84
|
</script>
|
|
114
85
|
|
|
115
86
|
<template>
|
|
116
|
-
<FlowPlayer
|
|
117
|
-
ref="player"
|
|
118
|
-
:document="diagramDocument"
|
|
119
|
-
:speed="1.25"
|
|
120
|
-
theme="dark"
|
|
121
|
-
@timeupdate="timeMs = $event.timeMs"
|
|
122
|
-
@error="handleError"
|
|
123
|
-
/>
|
|
124
|
-
<button type="button" @click="player?.play()">播放</button>
|
|
125
|
-
<button type="button" @click="player?.seek(0)">回到开头</button>
|
|
126
|
-
<output>{{ timeMs }} ms</output>
|
|
87
|
+
<FlowPlayer :document="diagram" loop gif-export />
|
|
127
88
|
</template>
|
|
128
89
|
```
|
|
129
90
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
## React 集成
|
|
91
|
+
### React
|
|
133
92
|
|
|
134
93
|
```tsx
|
|
135
|
-
import {
|
|
136
|
-
import { FlowPlayer, type FlowPlayerHandle } from "flowsvg/react";
|
|
94
|
+
import { FlowPlayer } from "flowsvg/react";
|
|
137
95
|
import "flowsvg/styles.css";
|
|
138
|
-
import {
|
|
139
|
-
|
|
140
|
-
export
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return (
|
|
144
|
-
<section>
|
|
145
|
-
<FlowPlayer
|
|
146
|
-
ref={player}
|
|
147
|
-
document={diagramDocument}
|
|
148
|
-
speed={1.25}
|
|
149
|
-
theme="dark"
|
|
150
|
-
onError={({ error }) => console.error(error)}
|
|
151
|
-
/>
|
|
152
|
-
<button type="button" onClick={() => player.current?.play()}>播放</button>
|
|
153
|
-
<button type="button" onClick={() => player.current?.seek(0)}>回到开头</button>
|
|
154
|
-
</section>
|
|
155
|
-
);
|
|
96
|
+
import { diagram } from "./diagram.js";
|
|
97
|
+
|
|
98
|
+
export function Diagram() {
|
|
99
|
+
return <FlowPlayer document={diagram} loop gifExport />;
|
|
156
100
|
}
|
|
157
101
|
```
|
|
158
102
|
|
|
159
|
-
|
|
103
|
+
## 控制播放
|
|
160
104
|
|
|
161
|
-
|
|
105
|
+
`createPlayer(host: HTMLElement, document: DiagramDocument, options?: PlayerOptions): FlowPlayer` 挂载播放器并替换宿主子元素。初始文档非法时抛出 `DiagramValidationError`。
|
|
162
106
|
|
|
163
|
-
|
|
|
107
|
+
| 方法/属性 | 行为 |
|
|
164
108
|
| --- | --- |
|
|
165
|
-
| `
|
|
166
|
-
| `
|
|
167
|
-
| `
|
|
168
|
-
| `
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
|
|
174
|
-
修改 `theme`、`controls`、`reducedMotion` 或 `hoverFill` 会重建播放器,暂停并回到零时刻;`initialTimeMs` 不会再次应用。修改文档时提供新的对象引用。ref / expose 提供下方播放器 API 中的七个控制和状态方法,清理由组件负责;挂载前或卸载后调用保留的 handle 会抛出未挂载错误。
|
|
175
|
-
|
|
176
|
-
两种框架入口可安全用于服务端导入;SSR 输出宿主容器,播放器在客户端挂载后创建。需要服务端图解预览时,使用核心的 `renderSvg`。
|
|
109
|
+
| `play(): void` / `pause(): void` | 从当前位置继续/暂停。到达终点后,`play()` 仅在开启循环时重新开始;静态图解不能播放。 |
|
|
110
|
+
| `seek(timeMs: number): void` | 时间限制在时间线范围内;除终点外保留播放状态,非有限值按零处理。 |
|
|
111
|
+
| `step(direction: 1 \| -1): void` | 暂停并跳转节拍边界;从节拍内部后退时先回到当前节拍起点。 |
|
|
112
|
+
| `setSpeed(speed: number): void` | 设置有限正数倍率,忽略非法值;默认 `1`。 |
|
|
113
|
+
| `setLoop(loop: boolean): void` | 切换整条时间线循环,保留当前位置和播放状态。 |
|
|
114
|
+
| `load(document: DiagramDocument): void` | 成功后暂停在零时刻;失败时触发 `error` 并保留原文档与状态。 |
|
|
115
|
+
| `getState(): PlayerState` | 返回 `{ timeMs, totalDuration, playing, loop, speed, frame }`,时间单位为毫秒。 |
|
|
116
|
+
| `destroy(): void` | 释放 DOM、监听器和动画资源,可重复调用;Vue/React ref 不提供此方法。 |
|
|
117
|
+
| `element: HTMLElement` | 只读宿主元素,也是事件目标;Vue/React ref 不提供此属性。 |
|
|
177
118
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
在页面中放置容器:
|
|
181
|
-
|
|
182
|
-
```html
|
|
183
|
-
<div id="diagram" style="width: 100%; height: 360px;"></div>
|
|
184
|
-
```
|
|
119
|
+
Vue/React ref 提供上表八个播放与状态方法。替换 `document` 对象会重新加载组件;修改 `theme`、`controls`、`gifExport`、`reducedMotion` 或 `hoverFill` 会重建播放器并暂停在零时刻。
|
|
185
120
|
|
|
186
|
-
|
|
121
|
+
### 选项与组件属性
|
|
187
122
|
|
|
188
|
-
|
|
189
|
-
import { createPlayer } from "flowsvg/browser";
|
|
190
|
-
import "flowsvg/styles.css";
|
|
191
|
-
import { diagramDocument } from "./diagram.js";
|
|
192
|
-
|
|
193
|
-
const host = document.querySelector<HTMLElement>("#diagram");
|
|
194
|
-
if (!host) throw new Error("Diagram host is missing");
|
|
195
|
-
|
|
196
|
-
const player = createPlayer(host, diagramDocument, { theme: "light" });
|
|
197
|
-
player.play();
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
`createPlayer` 接管并替换容器子元素。在页面或组件卸载时调用 `player.destroy()`;在同一容器上创建新播放器前先销毁旧实例。原生 HTML 直接使用裸导入路径时需配置 import map,或通过应用构建工具处理;仓库的原生示例提供了 import map 配置。
|
|
201
|
-
|
|
202
|
-
仓库中的 `apps/native-example/` 在固定镜头和布局下演示订单请求与响应。0–2400 ms,三条曲线分别显示圆点、`Token` 胶囊和反向预检胶囊;2400–4800 ms,另外两条曲线传递创建订单请求和库存确认;4800–7200 ms,响应消息返回网关和客户端。最终节拍在 8200 ms 结束,此时临时高亮已清除。相反方向的关系连线保持分离;悬停参与节点只增加浏览器高亮。
|
|
203
|
-
|
|
204
|
-
可将 `apps/native-example/request-chain.js` 作为原生、Web Component、React 或 Vue 的 `document` 输入。各宿主共用 `seek(timeMs)`、`setSpeed(speed)`、`play()` 和 `pause()` 行为。`src/business-integration.test.ts` 通过四种公开入口验证同一示例,覆盖两种主题、减少动画、节拍边界及核心 SVG 一致性。
|
|
205
|
-
|
|
206
|
-
## Web Component
|
|
207
|
-
|
|
208
|
-
先在 HTML 中放置元素,再从客户端模块显式注册:
|
|
209
|
-
|
|
210
|
-
```html
|
|
211
|
-
<flowsvg-player controls="false"></flowsvg-player>
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
```ts
|
|
215
|
-
import { defineFlowPlayer, type FlowPlayerElement } from "flowsvg/web-component";
|
|
216
|
-
import "flowsvg/styles.css";
|
|
217
|
-
import { diagramDocument } from "./diagram.js";
|
|
218
|
-
|
|
219
|
-
defineFlowPlayer();
|
|
220
|
-
const element = document.querySelector<FlowPlayerElement>("flowsvg-player");
|
|
221
|
-
if (element) element.document = diagramDocument;
|
|
222
|
-
```
|
|
223
|
-
|
|
224
|
-
默认元素名为 `flowsvg-player`,重复注册安全。对象通过 `document` property 传入;HTML `document` attribute 接受 JSON 字符串,`src` 接受相对于页面地址解析的 JSON URL。URL 请求遵循浏览器的同源与 CORS 规则。
|
|
225
|
-
|
|
226
|
-
支持 `autoplay`、`controls`、`speed`、`theme`、`reduced-motion` 和 `hover-fill`。悬停背景默认关闭;设置 `hover-fill` 后同时提供背景和内容反馈。关闭默认显示的控件需使用 `controls="false"`;`autoplay` 默认关闭。选项变化时保留当前时间与播放/暂停状态。元素从 DOM 移除时清理播放器和请求,重新连接时可根据保留的文档重新挂载。
|
|
227
|
-
|
|
228
|
-
元素提供 `play`、`pause`、`seek`、`step`、`setSpeed`、`getState`、`destroy`;更换文档使用 `document` property。挂载前 `getState()` 返回 `undefined`。它将四类播放/错误事件以冒泡、可穿越 Shadow DOM 边界的 CustomEvent 转发。
|
|
229
|
-
|
|
230
|
-
实际注册需在 DOM 环境加载该入口。无 DOM 导入是安全的,但模块求值时会固定 HTMLElement 基类;同一个无 DOM 模块实例不能在稍后补上 DOM 后直接用于注册。
|
|
231
|
-
|
|
232
|
-
## 文档与时间线
|
|
233
|
-
|
|
234
|
-
| 文档字段 | 约定 |
|
|
123
|
+
| `PlayerOptions` | 默认值/含义 |
|
|
235
124
|
| --- | --- |
|
|
236
|
-
| `
|
|
237
|
-
| `
|
|
238
|
-
| `
|
|
239
|
-
| `
|
|
240
|
-
| `
|
|
241
|
-
| `
|
|
125
|
+
| `theme?: "light" \| "dark"` | 使用文档主题,未设置时为 `"light"`。 |
|
|
126
|
+
| `controls?: boolean` | `true`;显示播放控件。 |
|
|
127
|
+
| `loop?: boolean` | `false`;循环整条时间线。 |
|
|
128
|
+
| `gifExport?: boolean` | `false`;在 `controls` 开启时显示 GIF 下载控件。 |
|
|
129
|
+
| `hoverFill?: boolean` | `false`;悬停仅高亮边框,设为 `true` 时同时高亮背景。 |
|
|
130
|
+
| `reducedMotion?: boolean` | 跟随浏览器偏好;`true` 隐藏传递标记并立即完成视觉过渡,保留时间线时长。 |
|
|
242
131
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
连线的 `route` 为 `straight`(默认)、`orthogonal` 或 `curved`。曲线根据端点方向端口生成平滑三次曲线,不会自动避让其他节点。正交路径避开可见普通节点,无法避让时使用固定回退;不保证最短路径或零交叉。需要固定连接侧时可使用 `fromPort` 和 `toPort`(`top`、`right`、`bottom` 或 `left`)。显式正交端口保留向外的端点线段,并绕过两端节点主体;曲线端口决定端点切线。省略端口时根据两节点中心的主方向自动选择,椭圆端口位于椭圆轮廓上。普通标签按确定性宽度估算换行,节点高度不足时保留完整标签并产生 `text-overflow` 警告。
|
|
246
|
-
|
|
247
|
-
连线可以连接普通节点、分组,或分别连接两者。分组端点使用当前求值后的分组矩形,跟随嵌套移动并支持显式端口。隐藏端点分组或其祖先时,连线同时隐藏。传递只高亮端点分组本身,子节点保留自身高亮状态。分组边框仍不作为布线障碍。
|
|
248
|
-
|
|
249
|
-
### 固定节点内的节点内容
|
|
250
|
-
|
|
251
|
-
`rect`、`roundedRect` 和 `cylinder` 节点可用 `content` 替代 `label`。椭圆和分组仅接受普通标签。同时声明两个字段会校验失败,空标签也算声明。`content.blocks` 按顺序排列 `title`、`paragraph`、`list` 和 `graph` 内容块。段落 `role` 为 `body`(默认)或 `auxiliary`(弱化说明)。空内容块数组只显示形状。下例需从 `flowsvg` 导入 `DiagramNode` 类型:
|
|
252
|
-
|
|
253
|
-
```ts
|
|
254
|
-
const service = {
|
|
255
|
-
id: "service", kind: "rect", x: 24, y: 24, width: 260, height: 312,
|
|
256
|
-
content: {
|
|
257
|
-
style: { padding: 16, blockGap: 14 },
|
|
258
|
-
blocks: [
|
|
259
|
-
{ type: "title", text: "Order service" },
|
|
260
|
-
{ type: "paragraph", role: "auxiliary", text: "Core responsibilities" },
|
|
261
|
-
{ type: "paragraph", text: "Validate and persist incoming orders." },
|
|
262
|
-
{ type: "paragraph", text: "Return the order number.", style: { color: "#2563eb", align: "right" } }
|
|
263
|
-
]
|
|
264
|
-
}
|
|
265
|
-
} satisfies DiagramNode;
|
|
266
|
-
```
|
|
132
|
+
Vue/React 的 `FlowPlayerProps` 另提供必填 `document: DiagramDocument`、`speed?: number`(默认 `1`)、`initialTimeMs?: number`(仅首次应用)、`className` 和 `style`。两个入口均导出 `FlowPlayerHandle` 与 `FlowPlayerProps` 类型。原生播放器通过 `play()` 启动,通过 `setSpeed()` 修改倍率。
|
|
267
133
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
每个内容块的 `style` 支持正有限数 `fontSize`、100–900 的整数字重 `fontWeight`、十六进制 `color` 和 `align`(`left`、`center`、`right`)。内容整体样式支持非负有限数 `padding` 和 `blockGap`。字号与间距单位均为图解单位。默认标题字号/字重为 20/700,正文为 14/400,辅助文字为 12/400;内边距为 16,内容块间距为 14,行高至少为 20 或字号的 1.35 倍。辅助文字使用主题弱化色,作者显式指定的内容颜色保持原值。
|
|
271
|
-
|
|
272
|
-
列表格式为 `{ type: "list", marker?: "none" | "bullet" | "number", items: ContentListItem[], style?: ContentListStyle }`,默认无标记(`none`)。每个条目可省略 `tags`、`status`、`body`、`auxiliary`、正文 `style`、`auxiliaryStyle` 和 `statusStyle`。列表 `style` 提供正文样式及非负有限 `itemGap`(默认 12)。条目正文样式覆盖列表正文样式;辅助文字保留弱化的 12 单位默认样式,可由 `auxiliaryStyle` 覆盖。圆点和编号使用统一标记列,容纳多位编号后留出 8 单位间距。正文续行和辅助文字均从正文列开始。标记与第一个存在的文字部分共用基线。缺省部分及完全空的条目不占高度或条目间距;显式空字符串和空行仍保留一行。例如:
|
|
273
|
-
|
|
274
|
-
```ts
|
|
275
|
-
{ type: "list", marker: "number", style: { itemGap: 16 }, items: [
|
|
276
|
-
{ body: "校验请求。", auxiliary: "输入检查" },
|
|
277
|
-
{ body: [{ text: "保存", style: { fontWeight: 700 } }, { text: "订单。" }] }
|
|
278
|
-
] }
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
文字标签格式为 `{ text: ContentText, style?: { fontSize, fontWeight, color, fill } }`,默认字号 11。底色在文字左右各留 6 单位、上下各留 2 单位,圆角半径 3。列表 `style.tagGap` 为非负有限数,默认 6,用于标签之间、标签行之间及同排状态之前的间距。默认底色和文字颜色随主题变化,显式颜色保持原值。状态默认使用 12 单位的主题强调色;`statusStyle` 仅支持 `fontSize`、`fontWeight` 和 `color`,状态始终右对齐。单排标签与单行状态的自然宽度能容纳时共用基线;否则标签先换行,状态从全部标签之后开始。长标签和长状态沿用混合样式的字素换行规则。正文位于元信息之后,辅助文字位于正文之后。缺省部分不占空间。例如:
|
|
282
|
-
|
|
283
|
-
```ts
|
|
284
|
-
{ type: "list", items: [{
|
|
285
|
-
tags: [{ text: "world" }, { text: "experience", style: { fill: "#eadfff", color: "#57319b" } }],
|
|
286
|
-
status: "new", body: "Alice joined Google", auxiliary: "Mar 2026"
|
|
287
|
-
}] }
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
节点尺寸保持固定。矩形应用作者内边距。圆角矩形先在四边额外内缩 4 个图解单位,再应用内边距。圆柱上下各预留当前弧面半径的两倍,再应用内边距;弧面半径为 `min(14, height / 4)`。安全区宽高不小于零,形状留白与内边距占满节点时仍可求值。内容从安全区顶部开始,按确定性字宽估算换行,保留显式换行和空行。SVG 在内容安全区裁剪文字。宽度或高度不足时分别产生 `text-overflow` 警告,包含节点 ID、`nodes[index].content` 路径和不足方向。空安全区仍可求值和导出。被裁剪的内容不扩大节点命中范围。普通标签保留现有溢出行为。
|
|
291
|
-
|
|
292
|
-
`EvaluatedNode.contentLayout` 提供 `safeArea`、有序 `lines`、标签底色 `tags`、`widthOverflow` 和 `heightOverflow`。每行提供文本、基线坐标、估算宽度、字号、字重、颜色、对齐方式、语义角色(`title`、`body`、`auxiliary`、`marker`、`tag` 或 `status`)、源内容块索引 `blockIndex`,以及列表行的可选条目索引 `itemIndex`。标签文字行另有 `tagIndex`。每个标签底色提供绝对 `x`、`y`、`width`、`height`、最终 `fill`、作者颜色来源 `explicitFill` 及源索引 `blockIndex`/`itemIndex`/`tagIndex`。每行 `spans` 提供各片段的文本、左侧绝对坐标、共用基线、估算宽度及最终字号/字重/颜色;`explicitColor` 表示作者是否在内容块或源片段指定了颜色。浏览器 SVG 与静态导出使用相同结果。内容跟随当前绝对几何(包括尺寸过渡和父分组移动)、祖先显隐和有效透明度。内容不改变端口、连线路由、传递几何或节点父级规则。可运行[标题与段落示例](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/node-content.html)和[列表示例](https://github.com/yokingma/FlowSVG/blob/HEAD/apps/native-example/list-content.html)。
|
|
134
|
+
### 事件
|
|
293
135
|
|
|
294
|
-
|
|
136
|
+
原生播放器在 `player.element` 派发 `CustomEvent`,数据位于 `event.detail`。Vue 处理函数与 React 回调直接接收事件数据。
|
|
295
137
|
|
|
138
|
+
| 原生/Vue 事件 | React 回调 | 数据 |
|
|
139
|
+
| --- | --- | --- |
|
|
140
|
+
| `stepchange` | `onStepChange` | `PlayerState`;场景/节拍选择变化。 |
|
|
141
|
+
| `timeupdate` | `onTimeUpdate` | `PlayerState`;画面更新。 |
|
|
142
|
+
| `playstatechange` | `onPlayStateChange` | `PlayerState`;播放状态变化。 |
|
|
143
|
+
| `loopchange` | `onLoopChange` | `PlayerState`;循环设置变化。 |
|
|
144
|
+
| `error` | `onError` | `PlayerErrorDetail`:播放器状态加 `error: Error`。 |
|
|
145
|
+
| `hover`、`click` | — | 原生/Web Component:`{ id, type, point, timeMs, sceneId, beatId }`;未命中时为 `null`。别名为 `objecthover`、`objectclick`。 |
|
|
296
146
|
|
|
297
|
-
|
|
147
|
+
交互数据中的 `point` 使用图解坐标,`type` 为 `"node"` 或 `"edge"`。画面更新时,如需派发 `stepchange`,该事件先于 `timeupdate`。Web Component 挂载前发生的错误可能不包含播放器状态字段。
|
|
298
148
|
|
|
299
|
-
|
|
149
|
+
## 导出
|
|
300
150
|
|
|
301
151
|
```ts
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
{ id: "alice", label: "Alice", x: 120, y: 30, highlighted: true },
|
|
306
|
-
{ id: "google", label: "Google", x: 50, y: 110, highlighted: true },
|
|
307
|
-
{ id: "ml", label: "ML project", x: 190, y: 110 }
|
|
308
|
-
],
|
|
309
|
-
edges: [{ from: "alice", to: "google" }, { from: "alice", to: "ml" }]
|
|
310
|
-
};
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
`height` 为正数,预留固定高度,之后应用普通内容块间距。实体中心 `x`/`y` 是相对该区域左上角的非负有限坐标,单位为图解单位。ID 在单个图形块内唯一,关系的两个端点引用该块中的实体 ID。标签使用转义的 12 单位纯文本,居中放在半径为 4 的圆点上方。选中实体使用强调色圆点和粗体标签;关系的两个端点均选中时,关系使用强调色。文字与图元共用父节点内容裁剪,并跟随主题与高亮配色。坐标或标签超出图形块区域时,SDK 报告对应方向的溢出警告,后续内容块的位置保持不变。作者需在圆点上方为标签预留空间。实体没有独立交互身份,点击或悬停均命中父节点。
|
|
152
|
+
import { createDiagram } from "flowsvg";
|
|
153
|
+
import { exportGif } from "flowsvg/export";
|
|
154
|
+
import { diagram } from "./diagram.js";
|
|
314
155
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
先指定固定形状尺寸,再按顺序添加内容块。记录使用 `marker: "none"`,能力清单使用 `"bullet"`,步骤使用 `"number"`。条目只需提供实际存在的部分。日期是普通文字;小字号片段可让日期与正文共用基线。下面的完整文档可以直接传给任一宿主:
|
|
320
|
-
|
|
321
|
-
```ts
|
|
322
|
-
import { createDiagram, type DiagramDocument, type NodeContent } from "flowsvg";
|
|
323
|
-
|
|
324
|
-
const facts: NodeContent = { blocks: [
|
|
325
|
-
{ type: "title", text: "Facts" },
|
|
326
|
-
{ type: "paragraph", role: "auxiliary", text: "已确认的信息记录" },
|
|
327
|
-
{ type: "list", marker: "none", items: [{
|
|
328
|
-
tags: [{ text: "world" }, { text: "订单", style: { fill: "#eadfff", color: "#57319b" } }],
|
|
329
|
-
status: "new",
|
|
330
|
-
body: [{ text: "订单已保存", style: { fontWeight: 700 } }, { text: " 2026-10-17", style: { fontSize: 11 } }],
|
|
331
|
-
auxiliary: "等待支付。"
|
|
332
|
-
}] }
|
|
333
|
-
] };
|
|
334
|
-
const updated: NodeContent = { blocks: [
|
|
335
|
-
{ type: "title", text: "Facts" },
|
|
336
|
-
{ type: "list", items: [{ tags: [{ text: "verified" }], status: "done", body: "支付已确认", auxiliary: "2026-10-18" }] }
|
|
337
|
-
] };
|
|
338
|
-
const document: DiagramDocument = {
|
|
339
|
-
version: 1, viewport: { width: 300, height: 480 },
|
|
340
|
-
nodes: [{ id: "facts", kind: "cylinder", x: 24, y: 20, width: 240, height: 440, content: facts }], edges: [],
|
|
341
|
-
scenes: [{ id: "records", label: "记录", beats: [
|
|
342
|
-
{ durationMs: 1000, actions: [] },
|
|
343
|
-
{ durationMs: 1000, actions: [{ type: "updateNode", target: "facts", value: { content: updated } }] },
|
|
344
|
-
{ durationMs: 1000, actions: [{ type: "updateNode", target: "facts", value: { label: "已归档" } }] },
|
|
345
|
-
{ durationMs: 1000, actions: [{ type: "updateNode", target: "facts", value: { content: facts } }] },
|
|
346
|
-
{ durationMs: 1000, actions: [{ type: "updateNode", target: "facts", value: { content: null } }] }
|
|
347
|
-
] }]
|
|
348
|
-
};
|
|
349
|
-
const diagram = createDiagram(document);
|
|
350
|
-
console.log(diagram.evaluateAt(1000).warnings);
|
|
351
|
-
const svg = diagram.renderSvg(1000, { theme: "dark", reducedMotion: true });
|
|
156
|
+
const compiled = createDiagram(diagram);
|
|
157
|
+
const frame = compiled.evaluateAt(1200);
|
|
158
|
+
const svg = compiled.renderSvg(1200, { theme: "dark" });
|
|
159
|
+
const gif = await exportGif(diagram, { width: 640, fps: 15 });
|
|
352
160
|
```
|
|
353
161
|
|
|
354
|
-
|
|
162
|
+
SVG 返回字符串,GIF 返回 `Uint8Array`。浏览器 GIF 支持 `signal` 与 `onProgress`;需要内置下载按钮时启用 `gifExport: true`。Node 20+ 可从 `flowsvg/node` 使用 `renderHtml` 和 `exportGif`,其中 Node GIF 须提前准备 Chromium。
|
|
355
163
|
|
|
356
|
-
|
|
164
|
+
## 核心 API — `flowsvg`
|
|
357
165
|
|
|
358
|
-
|
|
166
|
+
时间单位均为毫秒,位置与尺寸使用图解单位。`options?: EvaluateOptions` 包含 `theme?: "light" | "dark"` 与 `reducedMotion?: boolean`(默认 `false`)。
|
|
359
167
|
|
|
360
|
-
|
|
|
168
|
+
| 函数 | 返回值/行为 |
|
|
361
169
|
| --- | --- |
|
|
362
|
-
| `
|
|
363
|
-
| `
|
|
364
|
-
| `
|
|
365
|
-
| `
|
|
366
|
-
| `
|
|
367
|
-
| `focus` | `target` 为节点/分组 ID;`null` 恢复完整视口 |
|
|
170
|
+
| `createDiagram(document: DiagramDocument): Diagram` | 校验、复制、冻结并编译文档,供重复查询。 |
|
|
171
|
+
| `renderSvg(document: DiagramDocument, timeMs = 0, options?): string` | 编译并渲染一帧静态 SVG。 |
|
|
172
|
+
| `validateDocument(input: unknown): ValidationIssue[]` | 检查字段与引用,返回 `{ path, message }` 问题列表。空数组表示这些检查通过;编译阶段仍可能拒绝非有限的派生传递时长。 |
|
|
173
|
+
| `measureNodeContent({ kind, width, content }): NodeContentMeasurement` | 返回 `{ height, diagnostics }`。`kind` 为 `rect`、`roundedRect` 或 `cylinder`;`content` 为单个 `NodeContent` 或非空版本数组,多个版本取所需高度最大值。 |
|
|
174
|
+
| `wrapText(label: string, maxWidth: number, typography?): string[]` | 无 DOM 的估算换行;可选排版对象须包含 `fontSize`、`lineHeight`、`charWidth` 三个数值字段,省略时使用节点排版参数。 |
|
|
368
175
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
```ts
|
|
372
|
-
// 每个 value 分别用于不同节拍的 updateNode 动作。
|
|
373
|
-
import type { NodeVisualState } from "flowsvg";
|
|
374
|
-
|
|
375
|
-
const replacement: NodeVisualState = { content: { blocks: [{ type: "title", text: "Updated" }] } };
|
|
376
|
-
const simpleLabel: NodeVisualState = { label: "Summary" };
|
|
377
|
-
const clearText: NodeVisualState = { content: null };
|
|
378
|
-
```
|
|
379
|
-
|
|
380
|
-
所有场景共享以毫秒计的全局时间线,后一场景继承前一场景最终状态。标签与说明文字在节拍起点切换,可插值属性在过渡阶段变化。节拍区间左闭右开,最终时间返回完成后的最后画面。隐藏节点或分组会同步遮蔽关联连线;重新显示时按对象各自保存的状态恢复。
|
|
381
|
-
|
|
382
|
-
一个节拍可以同时在多条不同连线上传递。所有传递在节拍起点开始,以 `durationMs` 上相同的线性完成比例推进,在节拍结束时消失。因此,不同长度路径上的实际速度可以不同。只要当前传递仍使用某个端点,该节点就保持高亮。下一节拍会替换整组传递,跨场景边界也相同。传递高亮不会继承;显式高亮和普通样式继续按原有规则继承。
|
|
383
|
-
|
|
384
|
-
## API
|
|
385
|
-
|
|
386
|
-
传递文字属于单次动作,不替换连线的静态 `label`。胶囊宽度按 Unicode 码点和固定宽度估算,并保留内边距,不依赖浏览器测量。文字在所有路由上保持水平,并作为纯文本转义。胶囊不提供独立指针操作或键盘焦点;显式提供空字符串仍显示胶囊。
|
|
387
|
-
|
|
388
|
-
### 核心 API
|
|
389
|
-
|
|
390
|
-
从 `flowsvg` 导入:
|
|
391
|
-
|
|
392
|
-
| API | 返回与行为 |
|
|
176
|
+
| 编译后的 `Diagram` | 返回值/行为 |
|
|
393
177
|
| --- | --- |
|
|
394
|
-
| `
|
|
395
|
-
| `
|
|
396
|
-
| `
|
|
397
|
-
| `
|
|
398
|
-
| `
|
|
399
|
-
| `diagram.renderSvg(timeMs = 0, options?)` | 导出相同求值结果的 SVG |
|
|
400
|
-
| `diagram.hitTest(timeMs, point, options?)` | 在文档坐标中返回 `{ id, type: "node" \| "edge" }` 或 `null` |
|
|
401
|
-
| `wrapText(label, maxWidth, typography?)` | 按固定字宽估算换行,返回文本行数组 |
|
|
402
|
-
|
|
403
|
-
`EvaluateOptions` 为 `{ theme?: "light" | "dark"; reducedMotion?: boolean }`。有限时间限制在 `[0, totalDuration]`,非有限时间按零处理。命中忽略隐藏或零透明度对象,优先级依次为普通节点、连线、分组背景。悬停所有节点默认只高亮边框,保留当前求值后的背景色,包括播放中的时间线高亮;设置 `hoverFill: true` 后才绘制背景反馈。
|
|
178
|
+
| `totalDuration: number` | 只读有效总时长;静态文档为 `0`。 |
|
|
179
|
+
| `beats: readonly DiagramBeat[]` | 编译后的 `{ sceneId, beatId, beatIndex, startMs, endMs, caption? }` 区间。 |
|
|
180
|
+
| `evaluateAt(timeMs: number, options?): DiagramFrameState` | 返回 `{ timeMs, sceneId, beatId, beatProgress, nodes, edges, camera, caption?, warnings }`。 |
|
|
181
|
+
| `renderSvg(timeMs = 0, options?): string` | 渲染 SVG,复用编译结果。 |
|
|
182
|
+
| `hitTest(timeMs: number, point: DiagramPoint, options?): HitObject \| null` | 用图解坐标 `{ x, y }` 命中检测,返回 `{ id, type }` 或 `null`。 |
|
|
404
183
|
|
|
405
|
-
|
|
184
|
+
查询时间限制在 `[0, totalDuration]` 内,非有限值按零处理。`DiagramValidationError.issues` 提供字段路径与原因。测量诊断包含 `{ versionIndex, path, dimension, message }`;增加节点高度不能修复宽度溢出或固定高度关系图块内部的溢出。
|
|
406
185
|
|
|
407
|
-
|
|
186
|
+
### 文档与动作
|
|
408
187
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
从 `flowsvg/browser` 导入 `createPlayer(host, document, options?)`。`PlayerOptions` 包含核心视觉选项以及 `controls?: boolean` 和 `hoverFill?: boolean`。
|
|
412
|
-
|
|
413
|
-
| 方法或属性 | 行为 |
|
|
188
|
+
| 类型 | 核心字段 |
|
|
414
189
|
| --- | --- |
|
|
415
|
-
| `
|
|
416
|
-
| `
|
|
417
|
-
| `
|
|
418
|
-
| `
|
|
419
|
-
| `
|
|
420
|
-
| `
|
|
421
|
-
| `getState()` | 返回 `PlayerState`:`timeMs`、`totalDuration`、`playing`、`speed`、`frame` |
|
|
422
|
-
| `destroy()` | 幂等清理 DOM、监听器与调度;之后停止事件,状态仍可读取 |
|
|
423
|
-
|
|
424
|
-
创建时默认暂停在零时刻,内置控件默认开启。页面隐藏时暂停,重新可见后保持暂停;播放结束停在最终画面。静态图禁用时间控件。框架 handle 提供上述控制和状态方法,省去 `element` 与 `destroy`,由框架管理宿主与清理。
|
|
190
|
+
| `Viewport` | 必填有限正数 `width`、`height`;可选 `fitHeight: { padding }`,底部留白 `padding` 为有限非负数。 |
|
|
191
|
+
| `DiagramDocument` | `version: 1`、`viewport`、`nodes`、`edges`、`scenes`;可选 `title`、`theme`。`scenes` 为空时是静态图解。 |
|
|
192
|
+
| `DiagramNode` | `id`、`kind`、`x`、`y`、`width`、`height`;可选互斥的 `label`/`content`,以及 `parentId`、`visible`、`style`。类型有 `rect`、`roundedRect`、`ellipse`、`cylinder`、`group`。 |
|
|
193
|
+
| `DiagramEdge` | `id`、`from`、`to`;可选 `route`(默认 `straight`)、`fromPort`、`toPort`、`label`、`visible`、`style`。连接方向为 `top`、`right`、`bottom`、`left`。 |
|
|
194
|
+
| `Scene` / `Beat` | 场景为 `{ id, label, beats }`;节拍为 `{ durationMs, actions, transitionMs?, caption? }`。`durationMs` 须为正数;`transitionMs` 须位于 `[0, durationMs]`,默认 `min(250, durationMs)`,并受有效节拍时长限制。 |
|
|
195
|
+
| `Action` | `{ type, target }`;`updateNode`/`updateEdge` 另需 `value`。`flow` 必填 `direction`(`forward`/`reverse`),可选 `text` 和 `highlightParticipants`(默认 `true`)。`focus` 的目标为节点/分组 ID,或用 `null` 恢复完整视图。 |
|
|
425
196
|
|
|
426
|
-
|
|
197
|
+
`updateNode.value` 支持 `x`、`y`、`width`、`height`、`label`、`content`(`null` 清空)、`fill`、`stroke`、`opacity`;`updateEdge.value` 支持 `label`、`stroke`、`strokeWidth`、`opacity`。节点与连线 ID 全局唯一,子节点位置相对父分组。`NodeContent` 及其内容块类型均从 `flowsvg` 导出。
|
|
427
198
|
|
|
428
|
-
|
|
199
|
+
## 导出 API
|
|
429
200
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
### 事件
|
|
433
|
-
|
|
434
|
-
原生播放器在宿主元素上派发 `CustomEvent`:
|
|
435
|
-
|
|
436
|
-
| 事件 | `detail` |
|
|
201
|
+
| 入口/函数 | 返回值/要求 |
|
|
437
202
|
| --- | --- |
|
|
438
|
-
| `
|
|
439
|
-
| `
|
|
440
|
-
| `
|
|
441
|
-
| `error` | `PlayerErrorDetail`:保留的播放器状态与 `error` |
|
|
442
|
-
| `hover` / `click` | `PlayerInteractionDetail` 或 `null`;也提供 `objecthover` / `objectclick` 同义事件 |
|
|
443
|
-
|
|
444
|
-
交互 detail 包含 `id`、`type`、文档坐标 `point`、`timeMs`、`sceneId`、`beatId`;未命中为 `null`。Vue、React 和 Web Component 的包装层转发前四类事件。Web Component 挂载前的错误可能没有播放器状态。
|
|
445
|
-
|
|
446
|
-
指针移动时发送 `hover`;画面更新导致未移动的指针命中其他对象时,也发送该事件。指针移出或取消时发送 `null`。
|
|
447
|
-
|
|
448
|
-
```ts
|
|
449
|
-
import type { PlayerEventMap } from "flowsvg/browser";
|
|
450
|
-
|
|
451
|
-
function handleTimeUpdate(event: Event): void {
|
|
452
|
-
const { detail } = event as PlayerEventMap["timeupdate"];
|
|
453
|
-
console.log(detail.timeMs, detail.frame.caption);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
player.element.addEventListener("timeupdate", handleTimeUpdate);
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
上例沿用原生集成创建的 `player`。宿主自行添加的监听器由宿主移除。
|
|
460
|
-
|
|
461
|
-
## 主题与无障碍
|
|
203
|
+
| `flowsvg/export` → `exportGif(document, options?: GifExportOptions)` | `Promise<Uint8Array>`;需要浏览器 canvas 与 worker 支持。 |
|
|
204
|
+
| `flowsvg/node` → `renderHtml(document, options?: HtmlRenderOptions)` | `Promise<string>`;生成包含播放器的独立离线 HTML,要求 Node 20+,无需浏览器。 |
|
|
205
|
+
| `flowsvg/node` → `exportGif(document, options?: NodeGifExportOptions)` | `Promise<Uint8Array>`;要求 Node 20+ 和预先准备的 Chromium。设置 `executablePath` 或传入 Playwright `browser`,二者互斥;传入的浏览器仍由调用方管理。 |
|
|
462
206
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
导入 `flowsvg/styles.css` 可获得控件布局与焦点样式。CSS 变量只定制控件,图解本身颜色由核心主题和文档样式决定:
|
|
466
|
-
|
|
467
|
-
```css
|
|
468
|
-
.flowsvg {
|
|
469
|
-
--flowsvg-control-background: #101828;
|
|
470
|
-
--flowsvg-control-color: #f4f7fb;
|
|
471
|
-
--flowsvg-control-border: #64748b;
|
|
472
|
-
--flowsvg-control-accent: #60a5fa;
|
|
473
|
-
--flowsvg-control-focus: #fbbf24;
|
|
474
|
-
}
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
原生宿主可添加 `flowsvg` class 使用上例,框架组件会自动添加。聚焦画布后,Space 播放/暂停,左右方向键逐步移动;表单控件保留原生按键行为。说明文字使用礼貌的 live region,SVG 具有独立的可访问名称。
|
|
478
|
-
|
|
479
|
-
## 校验与错误
|
|
480
|
-
|
|
481
|
-
未知字段、无效引用、循环分组、重复节拍赋值、非法颜色以及无效尺寸/时长会在加载时拒绝。文本按纯文本转义。需要预先检查外部 JSON 时,先调用 `validateDocument`,根据每条 issue 的 `path` 和 `message` 展示错误。
|
|
482
|
-
|
|
483
|
-
`createDiagram` 和无效初始文档的 `createPlayer` 会抛出 `DiagramValidationError`,其 `issues` 包含详细路径。已有播放器的 `load` 通过 `error` 事件报告校验失败并保留旧状态。文字高度溢出属于非阻塞布局警告,读取 `frame.warnings` 后决定是否调整节点尺寸。
|
|
484
|
-
|
|
485
|
-
一个节拍内每条连线最多包含一个 `flow`,方向或文字不同也不能重复。重复连线会在后一动作的 `target` 报告 `duplicate beat assignment`,其他字段无效也不影响重复检查。传递可以与同一连线的显隐、显式高亮和普通样式动作共存;各类动作继续遵循各自的校验规则。
|
|
486
|
-
|
|
487
|
-
## 开发与验证
|
|
488
|
-
|
|
489
|
-
以下命令在源码仓库根目录执行。完整开发环境使用 Node.js 22.12+ 与 pnpm 10.12.1;Vue/React 示例基于 Vite 7。
|
|
490
|
-
|
|
491
|
-
| 命令 | 用途 |
|
|
207
|
+
| `GifExportOptions` | 默认值/含义 |
|
|
492
208
|
| --- | --- |
|
|
493
|
-
| `
|
|
494
|
-
| `
|
|
495
|
-
| `
|
|
496
|
-
| `
|
|
497
|
-
| `
|
|
498
|
-
| `
|
|
499
|
-
| `pnpm release:verify` | 从缺失的 `dist` 打包,检查文件白名单与全部导出,再验证无框架导入、严格消费者类型及四种宿主生命周期 |
|
|
500
|
-
| `pnpm release` | 运行全部发布检查与 npm 预演,将已验证的 tarball 保存在 `dist/releases/`,不上传 |
|
|
501
|
-
| `pnpm release:publish` | 检查 npm 登录状态,重新运行全部检查与预演,然后上传已验证的 tarball |
|
|
502
|
-
|
|
503
|
-
维护者请参照 [npm 发布指南](https://github.com/yokingma/FlowSVG/blob/HEAD/docs/RELEASING.md)。SDK 打包会删除并重建 `dist`,必须在示例构建之前完成。
|
|
504
|
-
|
|
505
|
-
构建后,从仓库根目录启动 Node.js 静态服务器:
|
|
506
|
-
|
|
507
|
-
```sh
|
|
508
|
-
pnpm dlx http-server@14.1.1 . -p 4175 -c-1
|
|
509
|
-
```
|
|
209
|
+
| `theme`、`reducedMotion` | 文档主题,未设置时为 `light`;减少动态效果默认 `false`。 |
|
|
210
|
+
| `startMs`、`endMs`、`speed` | 默认导出完整时间线,倍率 `1`;区间含起点、不含终点,完整时间线终点包含完成状态。 |
|
|
211
|
+
| `width`、`fps` | 默认 `min(viewport.width, 1280)` 像素;每秒 `15` 次采样(整数 `1`–`30`)。 |
|
|
212
|
+
| `includeCaptions`、`captionHeight` | 默认 `true`,固定字幕区域高度为 `80` 像素。 |
|
|
213
|
+
| `loopCount`、`finalHoldMs` | `0` 为无限循环,`1`–`65536` 为总播放次数;默认追加 `1000` 毫秒结尾停留。与播放器循环独立。 |
|
|
214
|
+
| `signal`、`onProgress` | `AbortSignal`;回调接收 `{ phase, completed, total }`,阶段为 `preparing`、`sampling`、`encoding`、`complete`。 |
|
|
510
215
|
|
|
511
|
-
|
|
216
|
+
GIF 限制:有效时长 120 秒,1800 个采样帧,1801 个编码帧,含字幕的最终长边不超过 1920 像素。`GifExportError.code` 为 `options`、`limit`、`document`、`caption-overflow`、`environment`、`rasterization`、`encoding` 或 `cancelled`。两个导出入口均提供 `GifExportError` 和各自选项/进度类型。
|
|
512
217
|
|
|
513
|
-
|
|
218
|
+
`HtmlRenderOptions` 包含 `theme`、`autoplay`(默认 `false`)、`loop`(`false`)、`controls`(`true`)、`gifExport`(`false`)。内置 GIF 下载按当前主题,以 `1×` 导出完整时间线。
|
|
514
219
|
|
|
515
|
-
|
|
220
|
+
## 其他浏览器 API
|
|
516
221
|
|
|
517
|
-
|
|
222
|
+
- `flowsvg/web-component`(别名 `flowsvg/element`):在浏览器调用 `defineFlowPlayer(tagName = "flowsvg-player")`,返回已注册的构造器;无自定义元素注册表时返回 `undefined`。可设置 `.document` 对象、`document` JSON 属性,或通过 `src` 加载 URL。播放选项使用 `loop`、`gif-export`、`reduced-motion` 等属性,`autoplay` 默认 `false`,`speed` 默认 `1`。播放方法不含 `load()`,重新赋值 `.document` 即可加载;挂载前或销毁后 `getState()` 可返回 `undefined`。事件支持冒泡并可穿过 Shadow DOM 边界。
|
|
223
|
+
- `flowsvg/browser`:`new SelectControl(owner: Document, label: string, onChange: (value: string) => void)` 创建选择控件。挂载 `.element`,调用 `render(choices: readonly SelectChoice[], value: string, disabled = false)` 同步状态,卸载时调用 `destroy()`。每个选项为 `{ value, label }`,调用方管理已提交的值。
|
|
518
224
|
|
|
519
|
-
|
|
225
|
+
## 编写规则
|
|
520
226
|
|
|
521
|
-
|
|
227
|
+
- `show`、`hide`、`highlight`、`unhighlight`、`updateNode`、`updateEdge`、`focus` 跨节拍继承;`flow` 仅在当前节拍生效。
|
|
228
|
+
- 单条传递按参考速度 180 图解单位/秒移动,最短 400ms,再执行含高亮渐变的 500ms 到达区间,替代作者的 `durationMs`。多条传递与无传递节拍保留作者时长。实际边界从 `compiled.beats` 读取;阅读停留使用独立无传递节拍。
|
|
229
|
+
- 矩形、圆角矩形和圆柱支持结构化 `content`,可包含标题、段落、列表、标签和实体关系小图。通过 `measureNodeContent({ kind, width, content })` 测量后写入固定尺寸,并检查 `diagnostics`;播放时不会自动调整节点尺寸。
|
|
230
|
+
- `viewport.fitHeight: { padding: 24 }` 让完整视图按当前内容调整高度;GIF 保留作者视口尺寸与比例。
|
|
231
|
+
- 非法文档抛出带字段路径的 `DiagramValidationError`;文字溢出记录在 `frame.warnings`。外部 JSON 可先调用 `validateDocument(input)`。
|
|
522
232
|
|
|
523
|
-
|
|
233
|
+
ESM,附 TypeScript 声明。无 DOM 核心支持 Node 18+,Node 工具要求 Node 20+;Vue 3.5+/React 18+。
|
|
524
234
|
|
|
525
|
-
|
|
235
|
+
[完整 SDK 手册](https://github.com/yokingma/FlowSVG/blob/main/docs/SDK-GUIDE.zh-CN.md) · [更新记录](https://cdn.jsdelivr.net/npm/flowsvg@0.2.0/CHANGELOG.md) · [MIT](https://cdn.jsdelivr.net/npm/flowsvg@0.2.0/LICENSE)
|