ice-render 2.3.0 → 2.3.2
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/dist/index.cjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/index.umd.js +2 -2
- package/dist/types/event/DOMEventDispatcher.d.mts +2 -0
- package/dist/types/event/DOMEventDispatcher.d.ts +2 -0
- package/dist/types/graphic/ICEComponent.d.mts +15 -3
- package/dist/types/graphic/ICEComponent.d.ts +15 -3
- package/dist/types/graphic/ICEDotPath.d.mts +9 -0
- package/dist/types/graphic/ICEDotPath.d.ts +9 -0
- package/dist/types/graphic/ICEPath.d.mts +44 -0
- package/dist/types/graphic/ICEPath.d.ts +44 -0
- package/dist/types/graphic/link/ICEPolyLine.d.mts +6 -0
- package/dist/types/graphic/link/ICEPolyLine.d.ts +6 -0
- package/dist/types/graphic/shape/ICEEllipse.d.mts +6 -0
- package/dist/types/graphic/shape/ICEEllipse.d.ts +6 -0
- package/dist/types/graphic/shape/ICERect.d.mts +6 -0
- package/dist/types/graphic/shape/ICERect.d.ts +6 -0
- package/dist/types/renderer/CanvasRenderer.d.mts +63 -0
- package/dist/types/renderer/CanvasRenderer.d.ts +63 -0
- package/dist/types/renderer/ObjectCache.d.mts +34 -0
- package/dist/types/renderer/ObjectCache.d.ts +34 -0
- package/package.json +3 -3
|
@@ -46,8 +46,9 @@ declare abstract class ICEComponent extends ICEEventTarget {
|
|
|
46
46
|
* 本组件是否**已经真正绘制过一次**(`__renderCore` 走完了 doRender 才会置真)。
|
|
47
47
|
*
|
|
48
48
|
* 存在的意义:`dirty` 同时承担两个语义 —— 「本帧要重绘」和「几何缓存(`ICEPath.createPathObject`)
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
* 是否该建立」。后者只在 doRender 里以 `if (this.dirty)` 的形式被消费(会不会真的重建另由几何
|
|
50
|
+
* 签名决定,见 `ICEPath.__pathStale`),因此**从未渲染过的组件一旦被置干净,它的路径缓存就永远
|
|
51
|
+
* 不会被建立**,首次上屏是空的(见 `__applyDirty`)。
|
|
51
52
|
*/
|
|
52
53
|
protected __everRendered: boolean;
|
|
53
54
|
private __localBoxScratch;
|
|
@@ -77,6 +78,15 @@ declare abstract class ICEComponent extends ICEEventTarget {
|
|
|
77
78
|
* 统一由 `refreshParams()` 读取与清理,不要在别处手工维护。
|
|
78
79
|
*/
|
|
79
80
|
protected __paramsDirty: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* 派生参数的**重算代次**:`refreshParams()` 每真正重算一次就 +1。
|
|
83
|
+
*
|
|
84
|
+
* 为什么需要它:`paramsDirty` 是个「脏了就清」的布尔量,消费掉之后就看不出
|
|
85
|
+
* 「这一帧到底重算过没有」。而几何缓存(`ICEPath` 的命令流)恰恰要问这个 ——
|
|
86
|
+
* 重算过就必须重建命令流。用自增计数就能在**时序无关**的前提下回答它:
|
|
87
|
+
* 无论 `refreshParams()` 在何时被调用,比较两个代次即可。
|
|
88
|
+
*/
|
|
89
|
+
private __paramsRev;
|
|
80
90
|
private __absScratchA;
|
|
81
91
|
private __absScratchB;
|
|
82
92
|
private __transScratch;
|
|
@@ -258,6 +268,8 @@ declare abstract class ICEComponent extends ICEEventTarget {
|
|
|
258
268
|
* - 需要强制重算时请先置 `paramsDirty = true`(`setState` 已自动做这件事)。
|
|
259
269
|
*/
|
|
260
270
|
refreshParams(): void;
|
|
271
|
+
/** 派生参数的重算代次(只读)。几何缓存用它判断「重算过没有」,见 `__paramsRev`。 */
|
|
272
|
+
get paramsRev(): number;
|
|
261
273
|
/**
|
|
262
274
|
* 计算本地原点坐标,相对于组件本地坐标系。
|
|
263
275
|
* 此方法依赖于 width/height ,需要先计算组件的尺寸,然后才能调用此方法。
|
|
@@ -464,7 +476,7 @@ declare abstract class ICEComponent extends ICEEventTarget {
|
|
|
464
476
|
*
|
|
465
477
|
* `markDirty = false` 的含义是「这次操作**不要**主动把组件标记为要重绘」(批量挂载时的性能优化),
|
|
466
478
|
* 而**不是**「把它强制置干净」:对从未绘制过的组件置干净会让几何缓存永不建立,首次上屏画不出
|
|
467
|
-
* 自身的路径(`ICEPath.doRender` 只在 dirty
|
|
479
|
+
* 自身的路径(`ICEPath.doRender` 只在 dirty 时才可能调用 `createPathObject`)。
|
|
468
480
|
*
|
|
469
481
|
* 例:`UIButton` 构造函数里 `addChild(this.label, false)` 会把自己置干净,导致按钮的圆角矩形
|
|
470
482
|
* 背景/边框在首帧是空路径 —— 页面上表现为「白底白字、完全看不见的按钮」。
|
|
@@ -46,8 +46,9 @@ declare abstract class ICEComponent extends ICEEventTarget {
|
|
|
46
46
|
* 本组件是否**已经真正绘制过一次**(`__renderCore` 走完了 doRender 才会置真)。
|
|
47
47
|
*
|
|
48
48
|
* 存在的意义:`dirty` 同时承担两个语义 —— 「本帧要重绘」和「几何缓存(`ICEPath.createPathObject`)
|
|
49
|
-
*
|
|
50
|
-
*
|
|
49
|
+
* 是否该建立」。后者只在 doRender 里以 `if (this.dirty)` 的形式被消费(会不会真的重建另由几何
|
|
50
|
+
* 签名决定,见 `ICEPath.__pathStale`),因此**从未渲染过的组件一旦被置干净,它的路径缓存就永远
|
|
51
|
+
* 不会被建立**,首次上屏是空的(见 `__applyDirty`)。
|
|
51
52
|
*/
|
|
52
53
|
protected __everRendered: boolean;
|
|
53
54
|
private __localBoxScratch;
|
|
@@ -77,6 +78,15 @@ declare abstract class ICEComponent extends ICEEventTarget {
|
|
|
77
78
|
* 统一由 `refreshParams()` 读取与清理,不要在别处手工维护。
|
|
78
79
|
*/
|
|
79
80
|
protected __paramsDirty: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* 派生参数的**重算代次**:`refreshParams()` 每真正重算一次就 +1。
|
|
83
|
+
*
|
|
84
|
+
* 为什么需要它:`paramsDirty` 是个「脏了就清」的布尔量,消费掉之后就看不出
|
|
85
|
+
* 「这一帧到底重算过没有」。而几何缓存(`ICEPath` 的命令流)恰恰要问这个 ——
|
|
86
|
+
* 重算过就必须重建命令流。用自增计数就能在**时序无关**的前提下回答它:
|
|
87
|
+
* 无论 `refreshParams()` 在何时被调用,比较两个代次即可。
|
|
88
|
+
*/
|
|
89
|
+
private __paramsRev;
|
|
80
90
|
private __absScratchA;
|
|
81
91
|
private __absScratchB;
|
|
82
92
|
private __transScratch;
|
|
@@ -258,6 +268,8 @@ declare abstract class ICEComponent extends ICEEventTarget {
|
|
|
258
268
|
* - 需要强制重算时请先置 `paramsDirty = true`(`setState` 已自动做这件事)。
|
|
259
269
|
*/
|
|
260
270
|
refreshParams(): void;
|
|
271
|
+
/** 派生参数的重算代次(只读)。几何缓存用它判断「重算过没有」,见 `__paramsRev`。 */
|
|
272
|
+
get paramsRev(): number;
|
|
261
273
|
/**
|
|
262
274
|
* 计算本地原点坐标,相对于组件本地坐标系。
|
|
263
275
|
* 此方法依赖于 width/height ,需要先计算组件的尺寸,然后才能调用此方法。
|
|
@@ -464,7 +476,7 @@ declare abstract class ICEComponent extends ICEEventTarget {
|
|
|
464
476
|
*
|
|
465
477
|
* `markDirty = false` 的含义是「这次操作**不要**主动把组件标记为要重绘」(批量挂载时的性能优化),
|
|
466
478
|
* 而**不是**「把它强制置干净」:对从未绘制过的组件置干净会让几何缓存永不建立,首次上屏画不出
|
|
467
|
-
* 自身的路径(`ICEPath.doRender` 只在 dirty
|
|
479
|
+
* 自身的路径(`ICEPath.doRender` 只在 dirty 时才可能调用 `createPathObject`)。
|
|
468
480
|
*
|
|
469
481
|
* 例:`UIButton` 构造函数里 `addChild(this.label, false)` 会把自己置干净,导致按钮的圆角矩形
|
|
470
482
|
* 背景/边框在首帧是空路径 —— 页面上表现为「白底白字、完全看不见的按钮」。
|
|
@@ -52,6 +52,15 @@ export default abstract class ICEDotPath extends ICEPath {
|
|
|
52
52
|
* @returns
|
|
53
53
|
*/
|
|
54
54
|
protected calcLocalOrigin(): any;
|
|
55
|
+
/**
|
|
56
|
+
* @overwrite
|
|
57
|
+
* 命令流 = `dots` 逐点连线(见 `createPathObject`),因此签名就是 `dots` 的**内容**。
|
|
58
|
+
*
|
|
59
|
+
* 这里逐点比较而不是比数组引用:`dots` 会被就地改写(`composeMatrix` 把点平移到「以原点为原点」、
|
|
60
|
+
* `addDot` / `rmDot` 直接 splice),只比引用会漏判成「没变」。逐点比较是 O(n) 但零分配,
|
|
61
|
+
* 与该类原本每帧 `JSON.stringify(dots)` 的开销相比仍然便宜。
|
|
62
|
+
*/
|
|
63
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
55
64
|
/**
|
|
56
65
|
* @returns
|
|
57
66
|
*/
|
|
@@ -52,6 +52,15 @@ export default abstract class ICEDotPath extends ICEPath {
|
|
|
52
52
|
* @returns
|
|
53
53
|
*/
|
|
54
54
|
protected calcLocalOrigin(): any;
|
|
55
|
+
/**
|
|
56
|
+
* @overwrite
|
|
57
|
+
* 命令流 = `dots` 逐点连线(见 `createPathObject`),因此签名就是 `dots` 的**内容**。
|
|
58
|
+
*
|
|
59
|
+
* 这里逐点比较而不是比数组引用:`dots` 会被就地改写(`composeMatrix` 把点平移到「以原点为原点」、
|
|
60
|
+
* `addDot` / `rmDot` 直接 splice),只比引用会漏判成「没变」。逐点比较是 O(n) 但零分配,
|
|
61
|
+
* 与该类原本每帧 `JSON.stringify(dots)` 的开销相比仍然便宜。
|
|
62
|
+
*/
|
|
63
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
55
64
|
/**
|
|
56
65
|
* @returns
|
|
57
66
|
*/
|
|
@@ -6,6 +6,50 @@ import ICEComponent from './ICEComponent.mjs';
|
|
|
6
6
|
*/
|
|
7
7
|
declare abstract class ICEPath extends ICEComponent {
|
|
8
8
|
path2D: any;
|
|
9
|
+
/**
|
|
10
|
+
* 上次构建命令流时的「派生参数代次」(见 `ICEComponent.paramsRev`)。
|
|
11
|
+
* `-1` = 还没建过。
|
|
12
|
+
*/
|
|
13
|
+
private __pathRev;
|
|
14
|
+
/** 上次构建命令流时的几何签名;`null` = 还没建过。 */
|
|
15
|
+
private __pathSig;
|
|
16
|
+
/** 几何签名的复用缓冲(每帧采样一次,避免为每个组件分配数组)。 */
|
|
17
|
+
private __pathSigScratch;
|
|
18
|
+
/** 本类是否提供了精确签名(惰性判定一次,避免给自定义子类每帧白采样)。 */
|
|
19
|
+
private __pathSigPrecise;
|
|
20
|
+
/**
|
|
21
|
+
* 本类是否覆盖了 `__pathSignature()`。
|
|
22
|
+
*
|
|
23
|
+
* 没覆盖的实现(默认返回 `null`)每帧采样都是白费 —— 结果恒为「无法判定」。
|
|
24
|
+
* 判定一次记在实例上,之后直接短路。第三方子类因此完全不付采样的钱。
|
|
25
|
+
*/
|
|
26
|
+
private __hasPrecisePathSignature;
|
|
27
|
+
/**
|
|
28
|
+
* 几何签名:**除「派生参数」之外**还有哪些 state 字段会改变命令流。
|
|
29
|
+
*
|
|
30
|
+
* 返回值的约定(这是本机制的安全边界,子类必须遵守):
|
|
31
|
+
* - `null`(**默认**)= 本类无法用签名判定 → 维持改造前的行为:`dirty` 就重建。
|
|
32
|
+
* 凡是在 `createPathObject()` 里读了额外 state 字段的自定义子类,都落在这一档上 ——
|
|
33
|
+
* 它们的语义与改造前**逐字一致**,不会因为漏判而画错。
|
|
34
|
+
* - `数组` = 精确判定:与建流时采样下来的那组值逐项 `Object.is` 比较,任一不同即重建。
|
|
35
|
+
*
|
|
36
|
+
* 内置的四个 builder(`ICERect` / `ICEEllipse` / `ICEDotPath` / `ICEPolyLine`)都覆盖了本方法 ——
|
|
37
|
+
* 它们读的字段是封闭的,因此「几何没变」的帧可以安全跳过重建(平移/旋转动画的大头就在这里)。
|
|
38
|
+
*
|
|
39
|
+
* @param out 复用的输出缓冲:实现里请 `push`,不要新建数组
|
|
40
|
+
*/
|
|
41
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
42
|
+
/**
|
|
43
|
+
* 命令流是否**可能**已经过期(几何被重算过,或签名里的值变了)。
|
|
44
|
+
*
|
|
45
|
+
* 只管几何,不管 `dirty` —— 调用方自己去与 `dirty` 取交集(`dirty` 的语义是「本帧要重绘」,
|
|
46
|
+
* 祖先移动、平移动画都会置脏,但它们不改变命令流)。
|
|
47
|
+
*/
|
|
48
|
+
private __pathStale;
|
|
49
|
+
/** 采样并记下当前签名(在命令流**建完之后**调用:`createPathObject()` 可能触发 `ensureDots()`)。 */
|
|
50
|
+
private __capturePathSignature;
|
|
51
|
+
/** 按「几何是否真的变了」决定要不要重建命令流(`dirty` 由调用方判断)。 */
|
|
52
|
+
private __rebuildPathIfStale;
|
|
9
53
|
/**
|
|
10
54
|
* 确保路径命令流是最新的。
|
|
11
55
|
*
|
|
@@ -6,6 +6,50 @@ import ICEComponent from './ICEComponent';
|
|
|
6
6
|
*/
|
|
7
7
|
declare abstract class ICEPath extends ICEComponent {
|
|
8
8
|
path2D: any;
|
|
9
|
+
/**
|
|
10
|
+
* 上次构建命令流时的「派生参数代次」(见 `ICEComponent.paramsRev`)。
|
|
11
|
+
* `-1` = 还没建过。
|
|
12
|
+
*/
|
|
13
|
+
private __pathRev;
|
|
14
|
+
/** 上次构建命令流时的几何签名;`null` = 还没建过。 */
|
|
15
|
+
private __pathSig;
|
|
16
|
+
/** 几何签名的复用缓冲(每帧采样一次,避免为每个组件分配数组)。 */
|
|
17
|
+
private __pathSigScratch;
|
|
18
|
+
/** 本类是否提供了精确签名(惰性判定一次,避免给自定义子类每帧白采样)。 */
|
|
19
|
+
private __pathSigPrecise;
|
|
20
|
+
/**
|
|
21
|
+
* 本类是否覆盖了 `__pathSignature()`。
|
|
22
|
+
*
|
|
23
|
+
* 没覆盖的实现(默认返回 `null`)每帧采样都是白费 —— 结果恒为「无法判定」。
|
|
24
|
+
* 判定一次记在实例上,之后直接短路。第三方子类因此完全不付采样的钱。
|
|
25
|
+
*/
|
|
26
|
+
private __hasPrecisePathSignature;
|
|
27
|
+
/**
|
|
28
|
+
* 几何签名:**除「派生参数」之外**还有哪些 state 字段会改变命令流。
|
|
29
|
+
*
|
|
30
|
+
* 返回值的约定(这是本机制的安全边界,子类必须遵守):
|
|
31
|
+
* - `null`(**默认**)= 本类无法用签名判定 → 维持改造前的行为:`dirty` 就重建。
|
|
32
|
+
* 凡是在 `createPathObject()` 里读了额外 state 字段的自定义子类,都落在这一档上 ——
|
|
33
|
+
* 它们的语义与改造前**逐字一致**,不会因为漏判而画错。
|
|
34
|
+
* - `数组` = 精确判定:与建流时采样下来的那组值逐项 `Object.is` 比较,任一不同即重建。
|
|
35
|
+
*
|
|
36
|
+
* 内置的四个 builder(`ICERect` / `ICEEllipse` / `ICEDotPath` / `ICEPolyLine`)都覆盖了本方法 ——
|
|
37
|
+
* 它们读的字段是封闭的,因此「几何没变」的帧可以安全跳过重建(平移/旋转动画的大头就在这里)。
|
|
38
|
+
*
|
|
39
|
+
* @param out 复用的输出缓冲:实现里请 `push`,不要新建数组
|
|
40
|
+
*/
|
|
41
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
42
|
+
/**
|
|
43
|
+
* 命令流是否**可能**已经过期(几何被重算过,或签名里的值变了)。
|
|
44
|
+
*
|
|
45
|
+
* 只管几何,不管 `dirty` —— 调用方自己去与 `dirty` 取交集(`dirty` 的语义是「本帧要重绘」,
|
|
46
|
+
* 祖先移动、平移动画都会置脏,但它们不改变命令流)。
|
|
47
|
+
*/
|
|
48
|
+
private __pathStale;
|
|
49
|
+
/** 采样并记下当前签名(在命令流**建完之后**调用:`createPathObject()` 可能触发 `ensureDots()`)。 */
|
|
50
|
+
private __capturePathSignature;
|
|
51
|
+
/** 按「几何是否真的变了」决定要不要重建命令流(`dirty` 由调用方判断)。 */
|
|
52
|
+
private __rebuildPathIfStale;
|
|
9
53
|
/**
|
|
10
54
|
* 确保路径命令流是最新的。
|
|
11
55
|
*
|
|
@@ -230,6 +230,12 @@ declare class ICEPolyLine extends ICEDotPath {
|
|
|
230
230
|
* 中间用「先水平后垂直」的曼哈顿折线连接。
|
|
231
231
|
*/
|
|
232
232
|
protected recalculateRoute(): void;
|
|
233
|
+
/**
|
|
234
|
+
* @overwrite
|
|
235
|
+
* 在 `ICEDotPath`(`dots` 内容)之上补一个 `curveType`:它决定命令流是折线还是贝塞尔曲线
|
|
236
|
+
* (见 `createPathObject`),不补就会被漏判成「几何没变」。
|
|
237
|
+
*/
|
|
238
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
233
239
|
/**
|
|
234
240
|
* @overwrite
|
|
235
241
|
* 支持贝塞尔曲线:curveType 为 quadratic/cubic 时用曲线连接,否则继承直线折线。
|
|
@@ -230,6 +230,12 @@ declare class ICEPolyLine extends ICEDotPath {
|
|
|
230
230
|
* 中间用「先水平后垂直」的曼哈顿折线连接。
|
|
231
231
|
*/
|
|
232
232
|
protected recalculateRoute(): void;
|
|
233
|
+
/**
|
|
234
|
+
* @overwrite
|
|
235
|
+
* 在 `ICEDotPath`(`dots` 内容)之上补一个 `curveType`:它决定命令流是折线还是贝塞尔曲线
|
|
236
|
+
* (见 `createPathObject`),不补就会被漏判成「几何没变」。
|
|
237
|
+
*/
|
|
238
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
233
239
|
/**
|
|
234
240
|
* @overwrite
|
|
235
241
|
* 支持贝塞尔曲线:curveType 为 quadratic/cubic 时用曲线连接,否则继承直线折线。
|
|
@@ -5,6 +5,12 @@ import ICEPath from '../ICEPath.mjs';
|
|
|
5
5
|
*/
|
|
6
6
|
declare class ICEEllipse extends ICEPath {
|
|
7
7
|
constructor(props?: any);
|
|
8
|
+
/**
|
|
9
|
+
* @overwrite
|
|
10
|
+
* 命令流的输入:两个半径 / 圆心相对本地原点的位置 / 旋转 / 起止角 / 方向 / 是否闭合。
|
|
11
|
+
* 见 `createPathObject`。圆(`ICECircle`)作为 `radiusX === radiusY` 的特例自动继承。
|
|
12
|
+
*/
|
|
13
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
8
14
|
/**
|
|
9
15
|
* 所有坐标点的坐标都是相对于父层组件,而不是全局坐标。
|
|
10
16
|
* @returns
|
|
@@ -5,6 +5,12 @@ import ICEPath from '../ICEPath';
|
|
|
5
5
|
*/
|
|
6
6
|
declare class ICEEllipse extends ICEPath {
|
|
7
7
|
constructor(props?: any);
|
|
8
|
+
/**
|
|
9
|
+
* @overwrite
|
|
10
|
+
* 命令流的输入:两个半径 / 圆心相对本地原点的位置 / 旋转 / 起止角 / 方向 / 是否闭合。
|
|
11
|
+
* 见 `createPathObject`。圆(`ICECircle`)作为 `radiusX === radiusY` 的特例自动继承。
|
|
12
|
+
*/
|
|
13
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
8
14
|
/**
|
|
9
15
|
* 所有坐标点的坐标都是相对于父层组件,而不是全局坐标。
|
|
10
16
|
* @returns
|
|
@@ -5,6 +5,12 @@ import ICEPath from '../ICEPath.mjs';
|
|
|
5
5
|
*/
|
|
6
6
|
declare class ICERect extends ICEPath {
|
|
7
7
|
constructor(props?: any);
|
|
8
|
+
/**
|
|
9
|
+
* @overwrite
|
|
10
|
+
* 命令流的输入只有「宽 / 高 / 圆角 / 本地原点 / 是否闭合」这几项(见 `createPathObject`),
|
|
11
|
+
* 因此可以用来判定「几何没变」——平移/旋转变换不进签名,它们由 CTM 承担。
|
|
12
|
+
*/
|
|
13
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
8
14
|
protected createPathObject(): any;
|
|
9
15
|
}
|
|
10
16
|
export default ICERect;
|
|
@@ -5,6 +5,12 @@ import ICEPath from '../ICEPath';
|
|
|
5
5
|
*/
|
|
6
6
|
declare class ICERect extends ICEPath {
|
|
7
7
|
constructor(props?: any);
|
|
8
|
+
/**
|
|
9
|
+
* @overwrite
|
|
10
|
+
* 命令流的输入只有「宽 / 高 / 圆角 / 本地原点 / 是否闭合」这几项(见 `createPathObject`),
|
|
11
|
+
* 因此可以用来判定「几何没变」——平移/旋转变换不进签名,它们由 CTM 承担。
|
|
12
|
+
*/
|
|
13
|
+
protected __pathSignature(out: any[]): any[] | null;
|
|
8
14
|
protected createPathObject(): any;
|
|
9
15
|
}
|
|
10
16
|
export default ICERect;
|
|
@@ -33,6 +33,15 @@ declare class CanvasRenderer extends ICEEventTarget {
|
|
|
33
33
|
private cache;
|
|
34
34
|
/** @internal 上一帧被视口裁剪掉的组件数(仅供性能观测与测试断言)。 */
|
|
35
35
|
__lastFrameCulled: number;
|
|
36
|
+
/** @internal 静态层位图的构建次数(仅供性能观测与测试断言)。 */
|
|
37
|
+
__layerBuilds: number;
|
|
38
|
+
/**
|
|
39
|
+
* 静态层位图:把「本帧不需要重画」的**连续一段**组件整体光栅化成一张位图,
|
|
40
|
+
* 之后每帧只清屏 + 贴一张图 + 画剩下的那几个(脏的)。见 `__renderWithStaticLayer`。
|
|
41
|
+
*/
|
|
42
|
+
private __layer;
|
|
43
|
+
/** @internal 静态层开关(默认开);关掉即完全回到「逐组件重画」的旧行为,供 A/B 与像素对比。 */
|
|
44
|
+
private __layerEnabled;
|
|
36
45
|
constructor(ice: ICE, options?: {
|
|
37
46
|
renderMode?: 'full' | 'dirty-rect';
|
|
38
47
|
});
|
|
@@ -47,6 +56,13 @@ declare class CanvasRenderer extends ICEEventTarget {
|
|
|
47
56
|
start(): this;
|
|
48
57
|
stop(): this;
|
|
49
58
|
private frameEvtHandler;
|
|
59
|
+
/**
|
|
60
|
+
* @internal 开关静态层位图(默认开)。关掉后完全回到「逐组件重画」的旧行为,
|
|
61
|
+
* 供像素对比测试与 A/B 性能对比使用。
|
|
62
|
+
*/
|
|
63
|
+
setStaticLayerEnabled(enabled: boolean): void;
|
|
64
|
+
/** @internal 静态层位图当前是否开启。 */
|
|
65
|
+
isStaticLayerEnabled(): boolean;
|
|
50
66
|
private refreshQueue;
|
|
51
67
|
private __rebuildQueue;
|
|
52
68
|
/**
|
|
@@ -59,6 +75,35 @@ declare class CanvasRenderer extends ICEEventTarget {
|
|
|
59
75
|
*/
|
|
60
76
|
private __zOrderChanged;
|
|
61
77
|
private doRenderFull;
|
|
78
|
+
/**
|
|
79
|
+
* 组件能否进静态层。
|
|
80
|
+
*
|
|
81
|
+
* 保守判据(任一不满足就排除在层外,回到逐组件重画):
|
|
82
|
+
* - 本帧不脏(脏组件必须现画,且它一动整层就得重建);
|
|
83
|
+
* - 可见(`display:false` 的组件在上屏快照里有旧墨迹要擦,不能打进层里);
|
|
84
|
+
* - 没有 `clipChildren` 祖先 —— 位图里没有那层裁剪,除非祖先也在同一层内(这里不做特判,一律排除);
|
|
85
|
+
* - 落墨不是 `globalCompositeOperation` 混合模式:位图会先与层内的透明底合成,
|
|
86
|
+
* 再整体贴回主画布,`destination-out` 这类依赖「画布已有内容」的算子会算出不同结果。
|
|
87
|
+
*/
|
|
88
|
+
private __layerEligible;
|
|
89
|
+
/**
|
|
90
|
+
* 在 z 序队列里挑出**最长的一段连续可入层组件**。
|
|
91
|
+
*
|
|
92
|
+
* 为什么必须是「连续」:队列是全局 zIndex 排序,位图只能整层贴回;
|
|
93
|
+
* 成员与非成员在 z 序上交错的话,叠放次序会变(画错)。所以只认连续段。
|
|
94
|
+
*/
|
|
95
|
+
private __pickLayerRun;
|
|
96
|
+
/**
|
|
97
|
+
* 静态层位图路径:命中并渲染成功返回 true(调用方直接结束本帧)。
|
|
98
|
+
*
|
|
99
|
+
* 收益量级:1 万个静态组件逐组件重画约 20ms,整层贴回约 0.3ms —— 实测这一档场景 24ms → 1.3ms。
|
|
100
|
+
* 只在「局部重绘不成立」之后才走这里:小范围损伤时脏矩形仍然是更便宜的那条路。
|
|
101
|
+
*/
|
|
102
|
+
private __renderWithStaticLayer;
|
|
103
|
+
/** 把成员整体光栅化到一张离屏位图(栅格对齐纪律与 `ObjectCache.build` 完全一致)。 */
|
|
104
|
+
private __buildLayer;
|
|
105
|
+
/** 清屏 → 在位图对应的 z 位置整层贴回 → 逐组件画非成员(脏组件与段外组件)。 */
|
|
106
|
+
private __compositeLayer;
|
|
62
107
|
/**
|
|
63
108
|
* 收集脏区域并做局部重绘可行性判定。
|
|
64
109
|
* 返回 null = 不满足局部条件(由调用方回退全量);否则返回本帧脏区域 [minX,minY,maxX,maxY]。
|
|
@@ -119,6 +164,24 @@ declare class CanvasRenderer extends ICEEventTarget {
|
|
|
119
164
|
* 供命中检测做「廉价包围盒预筛」,避免对屏外组件做矩阵反变换 + 形状判定。无快照返回 null。
|
|
120
165
|
*/
|
|
121
166
|
getWorldBox(component: any): Float64Array | null;
|
|
167
|
+
/**
|
|
168
|
+
* 命中测试要用的「已排好序」的组件 / 工具队列(都是 zIndex 升序)。
|
|
169
|
+
*
|
|
170
|
+
* 为什么给命中测试用:`hitTestComponents` 原来每次都 `flattenAllComponents()` + `sort()`,
|
|
171
|
+
* 那是**每次鼠标命中**都要展平整棵树、分配并排序一个大数组(实测 1 万组件下 0.8ms/次,
|
|
172
|
+
* 而 hover 类交互是逐次 mousemove 调的)。渲染器手里本来就有一份同样口径、
|
|
173
|
+
* 且只在结构/zIndex 变化时才重建的队列,直接复用即可。
|
|
174
|
+
*
|
|
175
|
+
* 语义口径与渲染队列完全一致(`flattenTree` 先组件后工具 + zIndex 稳定排序),
|
|
176
|
+
* 因此「点得到的位置」与「画出来的样子」仍然严格对齐 —— 这两者一旦漂移,
|
|
177
|
+
* 就会变成「看得见却点不中」这类最难查的问题。
|
|
178
|
+
*
|
|
179
|
+
* @internal 供 `hitTestComponents` 复用;调用方不要改这两个数组。
|
|
180
|
+
*/
|
|
181
|
+
getOrderedQueues(): {
|
|
182
|
+
components: any[];
|
|
183
|
+
tools: any[];
|
|
184
|
+
};
|
|
122
185
|
/**
|
|
123
186
|
* 当前视口对应的可见世界矩形 [minX,minY,maxX,maxY]。画布尺寸缺失时返回 null(不裁剪)。
|
|
124
187
|
*/
|
|
@@ -33,6 +33,15 @@ declare class CanvasRenderer extends ICEEventTarget {
|
|
|
33
33
|
private cache;
|
|
34
34
|
/** @internal 上一帧被视口裁剪掉的组件数(仅供性能观测与测试断言)。 */
|
|
35
35
|
__lastFrameCulled: number;
|
|
36
|
+
/** @internal 静态层位图的构建次数(仅供性能观测与测试断言)。 */
|
|
37
|
+
__layerBuilds: number;
|
|
38
|
+
/**
|
|
39
|
+
* 静态层位图:把「本帧不需要重画」的**连续一段**组件整体光栅化成一张位图,
|
|
40
|
+
* 之后每帧只清屏 + 贴一张图 + 画剩下的那几个(脏的)。见 `__renderWithStaticLayer`。
|
|
41
|
+
*/
|
|
42
|
+
private __layer;
|
|
43
|
+
/** @internal 静态层开关(默认开);关掉即完全回到「逐组件重画」的旧行为,供 A/B 与像素对比。 */
|
|
44
|
+
private __layerEnabled;
|
|
36
45
|
constructor(ice: ICE, options?: {
|
|
37
46
|
renderMode?: 'full' | 'dirty-rect';
|
|
38
47
|
});
|
|
@@ -47,6 +56,13 @@ declare class CanvasRenderer extends ICEEventTarget {
|
|
|
47
56
|
start(): this;
|
|
48
57
|
stop(): this;
|
|
49
58
|
private frameEvtHandler;
|
|
59
|
+
/**
|
|
60
|
+
* @internal 开关静态层位图(默认开)。关掉后完全回到「逐组件重画」的旧行为,
|
|
61
|
+
* 供像素对比测试与 A/B 性能对比使用。
|
|
62
|
+
*/
|
|
63
|
+
setStaticLayerEnabled(enabled: boolean): void;
|
|
64
|
+
/** @internal 静态层位图当前是否开启。 */
|
|
65
|
+
isStaticLayerEnabled(): boolean;
|
|
50
66
|
private refreshQueue;
|
|
51
67
|
private __rebuildQueue;
|
|
52
68
|
/**
|
|
@@ -59,6 +75,35 @@ declare class CanvasRenderer extends ICEEventTarget {
|
|
|
59
75
|
*/
|
|
60
76
|
private __zOrderChanged;
|
|
61
77
|
private doRenderFull;
|
|
78
|
+
/**
|
|
79
|
+
* 组件能否进静态层。
|
|
80
|
+
*
|
|
81
|
+
* 保守判据(任一不满足就排除在层外,回到逐组件重画):
|
|
82
|
+
* - 本帧不脏(脏组件必须现画,且它一动整层就得重建);
|
|
83
|
+
* - 可见(`display:false` 的组件在上屏快照里有旧墨迹要擦,不能打进层里);
|
|
84
|
+
* - 没有 `clipChildren` 祖先 —— 位图里没有那层裁剪,除非祖先也在同一层内(这里不做特判,一律排除);
|
|
85
|
+
* - 落墨不是 `globalCompositeOperation` 混合模式:位图会先与层内的透明底合成,
|
|
86
|
+
* 再整体贴回主画布,`destination-out` 这类依赖「画布已有内容」的算子会算出不同结果。
|
|
87
|
+
*/
|
|
88
|
+
private __layerEligible;
|
|
89
|
+
/**
|
|
90
|
+
* 在 z 序队列里挑出**最长的一段连续可入层组件**。
|
|
91
|
+
*
|
|
92
|
+
* 为什么必须是「连续」:队列是全局 zIndex 排序,位图只能整层贴回;
|
|
93
|
+
* 成员与非成员在 z 序上交错的话,叠放次序会变(画错)。所以只认连续段。
|
|
94
|
+
*/
|
|
95
|
+
private __pickLayerRun;
|
|
96
|
+
/**
|
|
97
|
+
* 静态层位图路径:命中并渲染成功返回 true(调用方直接结束本帧)。
|
|
98
|
+
*
|
|
99
|
+
* 收益量级:1 万个静态组件逐组件重画约 20ms,整层贴回约 0.3ms —— 实测这一档场景 24ms → 1.3ms。
|
|
100
|
+
* 只在「局部重绘不成立」之后才走这里:小范围损伤时脏矩形仍然是更便宜的那条路。
|
|
101
|
+
*/
|
|
102
|
+
private __renderWithStaticLayer;
|
|
103
|
+
/** 把成员整体光栅化到一张离屏位图(栅格对齐纪律与 `ObjectCache.build` 完全一致)。 */
|
|
104
|
+
private __buildLayer;
|
|
105
|
+
/** 清屏 → 在位图对应的 z 位置整层贴回 → 逐组件画非成员(脏组件与段外组件)。 */
|
|
106
|
+
private __compositeLayer;
|
|
62
107
|
/**
|
|
63
108
|
* 收集脏区域并做局部重绘可行性判定。
|
|
64
109
|
* 返回 null = 不满足局部条件(由调用方回退全量);否则返回本帧脏区域 [minX,minY,maxX,maxY]。
|
|
@@ -119,6 +164,24 @@ declare class CanvasRenderer extends ICEEventTarget {
|
|
|
119
164
|
* 供命中检测做「廉价包围盒预筛」,避免对屏外组件做矩阵反变换 + 形状判定。无快照返回 null。
|
|
120
165
|
*/
|
|
121
166
|
getWorldBox(component: any): Float64Array | null;
|
|
167
|
+
/**
|
|
168
|
+
* 命中测试要用的「已排好序」的组件 / 工具队列(都是 zIndex 升序)。
|
|
169
|
+
*
|
|
170
|
+
* 为什么给命中测试用:`hitTestComponents` 原来每次都 `flattenAllComponents()` + `sort()`,
|
|
171
|
+
* 那是**每次鼠标命中**都要展平整棵树、分配并排序一个大数组(实测 1 万组件下 0.8ms/次,
|
|
172
|
+
* 而 hover 类交互是逐次 mousemove 调的)。渲染器手里本来就有一份同样口径、
|
|
173
|
+
* 且只在结构/zIndex 变化时才重建的队列,直接复用即可。
|
|
174
|
+
*
|
|
175
|
+
* 语义口径与渲染队列完全一致(`flattenTree` 先组件后工具 + zIndex 稳定排序),
|
|
176
|
+
* 因此「点得到的位置」与「画出来的样子」仍然严格对齐 —— 这两者一旦漂移,
|
|
177
|
+
* 就会变成「看得见却点不中」这类最难查的问题。
|
|
178
|
+
*
|
|
179
|
+
* @internal 供 `hitTestComponents` 复用;调用方不要改这两个数组。
|
|
180
|
+
*/
|
|
181
|
+
getOrderedQueues(): {
|
|
182
|
+
components: any[];
|
|
183
|
+
tools: any[];
|
|
184
|
+
};
|
|
122
185
|
/**
|
|
123
186
|
* 当前视口对应的可见世界矩形 [minX,minY,maxX,maxY]。画布尺寸缺失时返回 null(不裁剪)。
|
|
124
187
|
*/
|
|
@@ -18,8 +18,21 @@ export interface CachedSurface {
|
|
|
18
18
|
ox: number;
|
|
19
19
|
oy: number;
|
|
20
20
|
contentKey: string;
|
|
21
|
+
/**
|
|
22
|
+
* 内容指纹的**比较基准**(`contentKey` 的向量形态):数组元素已深拷贝,
|
|
23
|
+
* 因此每帧只需与它逐项比较,不必再拼字符串。见 `contentKeyVector`。
|
|
24
|
+
*/
|
|
25
|
+
contentKeys: any[];
|
|
21
26
|
linearKey: string;
|
|
22
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* 指纹向量比较。
|
|
30
|
+
*
|
|
31
|
+
* **必须严于字符串比较**:字符串不同时这里必须判「不同」(宁可多重一次位图,也不能贴旧位图)。
|
|
32
|
+
* 反向不要求 —— 判成「不同」只是多重建一次,安全。所以这里的边角取舍一律偏保守:
|
|
33
|
+
* `NaN` / `undefined` / 稀疏数组在 `JSON.stringify` 里都会被抹平,这里一律判为「不同」。
|
|
34
|
+
*/
|
|
35
|
+
export declare function keyEquals(a: any, b: any): boolean;
|
|
23
36
|
declare class ObjectCache {
|
|
24
37
|
private ice;
|
|
25
38
|
private map;
|
|
@@ -37,6 +50,8 @@ declare class ObjectCache {
|
|
|
37
50
|
private __vp;
|
|
38
51
|
/** 本帧的渲染视口是否与上一帧不同。视口变化帧一律不缓存,见 `beginFrame()`。 */
|
|
39
52
|
private __vpChanged;
|
|
53
|
+
/** `contentKeyVector` 的复用缓冲(每帧复用,避免为每个组件分配一个 40 元素数组)。 */
|
|
54
|
+
private __keyScratch;
|
|
40
55
|
constructor(ice: any);
|
|
41
56
|
/** 渲染视口:世界 → 画布设备像素的仿射映射(`device = world * scale + (tx, ty)`)。 */
|
|
42
57
|
private __rvp;
|
|
@@ -49,6 +64,13 @@ declare class ObjectCache {
|
|
|
49
64
|
* 手势停下后的第一帧再统一重建一次。
|
|
50
65
|
*/
|
|
51
66
|
beginFrame(): void;
|
|
67
|
+
/**
|
|
68
|
+
* 本帧的渲染视口是否相对上一帧变过(由 `beginFrame()` 记录)。
|
|
69
|
+
*
|
|
70
|
+
* @internal 给静态层位图用:位图与组件位图同一条纪律 —— **视口变化的帧一律不建位图**
|
|
71
|
+
* (栅格已错位,加一次"重建 + 贴回"比直接画还贵),手势停下后的第一帧再统一重建。
|
|
72
|
+
*/
|
|
73
|
+
viewportChangedThisFrame(): boolean;
|
|
52
74
|
/** 位图是否与当前渲染视口同源(缩放与平移都必须一致,否则栅格不再对齐)。 */
|
|
53
75
|
private __sameViewport;
|
|
54
76
|
/**
|
|
@@ -60,8 +82,20 @@ declare class ObjectCache {
|
|
|
60
82
|
/**
|
|
61
83
|
* 内容指纹:决定位图是否需要重新光栅化。只涵盖影响文本外观的 state 字段,
|
|
62
84
|
* 不包含 left/top/transform(由 linearKey 单独处理,以便纯平移复用)。
|
|
85
|
+
*
|
|
86
|
+
* 字符串形态只给「需要人看的场合 / 测试」用;热路径请用 `contentKeyVector` + `keyEquals`。
|
|
63
87
|
*/
|
|
64
88
|
contentKey(component: any): string;
|
|
89
|
+
/**
|
|
90
|
+
* 内容指纹的**输入向量**(顺序即指纹顺序)。
|
|
91
|
+
*
|
|
92
|
+
* 与 `contentKey()` 是同一份口径:字符串指纹由本向量拼出,两者不可能漂移。
|
|
93
|
+
* 单独拆出来的意义是热路径可以复用缓冲、逐项比较,省掉每帧为每个已缓存组件
|
|
94
|
+
* 拼一个长字符串的开销(实测 2000 文本动画场景里这一项占 1.29ms/帧)。
|
|
95
|
+
*
|
|
96
|
+
* @param out 复用缓冲:调用方负责清空
|
|
97
|
+
*/
|
|
98
|
+
contentKeyVector(component: any, out: any[]): any[];
|
|
65
99
|
private __styleKey;
|
|
66
100
|
/** 线性变换指纹(composedMatrix 的 a,b,c,d);平移分量单独保存,供纯平移复用。 */
|
|
67
101
|
linearKey(component: any): string;
|
|
@@ -18,8 +18,21 @@ export interface CachedSurface {
|
|
|
18
18
|
ox: number;
|
|
19
19
|
oy: number;
|
|
20
20
|
contentKey: string;
|
|
21
|
+
/**
|
|
22
|
+
* 内容指纹的**比较基准**(`contentKey` 的向量形态):数组元素已深拷贝,
|
|
23
|
+
* 因此每帧只需与它逐项比较,不必再拼字符串。见 `contentKeyVector`。
|
|
24
|
+
*/
|
|
25
|
+
contentKeys: any[];
|
|
21
26
|
linearKey: string;
|
|
22
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* 指纹向量比较。
|
|
30
|
+
*
|
|
31
|
+
* **必须严于字符串比较**:字符串不同时这里必须判「不同」(宁可多重一次位图,也不能贴旧位图)。
|
|
32
|
+
* 反向不要求 —— 判成「不同」只是多重建一次,安全。所以这里的边角取舍一律偏保守:
|
|
33
|
+
* `NaN` / `undefined` / 稀疏数组在 `JSON.stringify` 里都会被抹平,这里一律判为「不同」。
|
|
34
|
+
*/
|
|
35
|
+
export declare function keyEquals(a: any, b: any): boolean;
|
|
23
36
|
declare class ObjectCache {
|
|
24
37
|
private ice;
|
|
25
38
|
private map;
|
|
@@ -37,6 +50,8 @@ declare class ObjectCache {
|
|
|
37
50
|
private __vp;
|
|
38
51
|
/** 本帧的渲染视口是否与上一帧不同。视口变化帧一律不缓存,见 `beginFrame()`。 */
|
|
39
52
|
private __vpChanged;
|
|
53
|
+
/** `contentKeyVector` 的复用缓冲(每帧复用,避免为每个组件分配一个 40 元素数组)。 */
|
|
54
|
+
private __keyScratch;
|
|
40
55
|
constructor(ice: any);
|
|
41
56
|
/** 渲染视口:世界 → 画布设备像素的仿射映射(`device = world * scale + (tx, ty)`)。 */
|
|
42
57
|
private __rvp;
|
|
@@ -49,6 +64,13 @@ declare class ObjectCache {
|
|
|
49
64
|
* 手势停下后的第一帧再统一重建一次。
|
|
50
65
|
*/
|
|
51
66
|
beginFrame(): void;
|
|
67
|
+
/**
|
|
68
|
+
* 本帧的渲染视口是否相对上一帧变过(由 `beginFrame()` 记录)。
|
|
69
|
+
*
|
|
70
|
+
* @internal 给静态层位图用:位图与组件位图同一条纪律 —— **视口变化的帧一律不建位图**
|
|
71
|
+
* (栅格已错位,加一次"重建 + 贴回"比直接画还贵),手势停下后的第一帧再统一重建。
|
|
72
|
+
*/
|
|
73
|
+
viewportChangedThisFrame(): boolean;
|
|
52
74
|
/** 位图是否与当前渲染视口同源(缩放与平移都必须一致,否则栅格不再对齐)。 */
|
|
53
75
|
private __sameViewport;
|
|
54
76
|
/**
|
|
@@ -60,8 +82,20 @@ declare class ObjectCache {
|
|
|
60
82
|
/**
|
|
61
83
|
* 内容指纹:决定位图是否需要重新光栅化。只涵盖影响文本外观的 state 字段,
|
|
62
84
|
* 不包含 left/top/transform(由 linearKey 单独处理,以便纯平移复用)。
|
|
85
|
+
*
|
|
86
|
+
* 字符串形态只给「需要人看的场合 / 测试」用;热路径请用 `contentKeyVector` + `keyEquals`。
|
|
63
87
|
*/
|
|
64
88
|
contentKey(component: any): string;
|
|
89
|
+
/**
|
|
90
|
+
* 内容指纹的**输入向量**(顺序即指纹顺序)。
|
|
91
|
+
*
|
|
92
|
+
* 与 `contentKey()` 是同一份口径:字符串指纹由本向量拼出,两者不可能漂移。
|
|
93
|
+
* 单独拆出来的意义是热路径可以复用缓冲、逐项比较,省掉每帧为每个已缓存组件
|
|
94
|
+
* 拼一个长字符串的开销(实测 2000 文本动画场景里这一项占 1.29ms/帧)。
|
|
95
|
+
*
|
|
96
|
+
* @param out 复用缓冲:调用方负责清空
|
|
97
|
+
*/
|
|
98
|
+
contentKeyVector(component: any, out: any[]): any[];
|
|
65
99
|
private __styleKey;
|
|
66
100
|
/** 线性变换指纹(composedMatrix 的 a,b,c,d);平移分量单独保存,供纯平移复用。 */
|
|
67
101
|
linearKey(component: any): string;
|