view-anchor 0.2.2 → 1.0.0-beta.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.
Files changed (46) hide show
  1. package/README.md +71 -87
  2. package/README.zh-CN.md +59 -64
  3. package/dist/index.d.ts +5 -7
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +2 -3
  6. package/dist/protocol-publisher.d.ts +7 -9
  7. package/dist/protocol-publisher.d.ts.map +1 -1
  8. package/dist/protocol-publisher.js +2 -8
  9. package/dist/protocol-types.d.ts +5 -8
  10. package/dist/protocol-types.d.ts.map +1 -1
  11. package/dist/protocol-types.js +3 -6
  12. package/dist/protocol.d.ts +2 -2
  13. package/dist/protocol.d.ts.map +1 -1
  14. package/dist/protocol.js +10 -2
  15. package/dist/react.d.ts +2 -15
  16. package/dist/react.d.ts.map +1 -1
  17. package/dist/react.js +43 -38
  18. package/dist/{size-advertiser.d.ts → size-anchor.d.ts} +6 -5
  19. package/dist/size-anchor.d.ts.map +1 -0
  20. package/dist/size-anchor.js +131 -0
  21. package/dist/types.d.ts +26 -32
  22. package/dist/types.d.ts.map +1 -1
  23. package/dist/view-anchor.d.ts +52 -33
  24. package/dist/view-anchor.d.ts.map +1 -1
  25. package/dist/view-anchor.js +186 -265
  26. package/docs/bidirectional-design.md +29 -34
  27. package/docs/index.html +656 -366
  28. package/docs/mechanism.md +35 -57
  29. package/docs/performance-report.md +12 -54
  30. package/docs/protocol.md +12 -3
  31. package/package.json +4 -6
  32. package/src/index.ts +5 -19
  33. package/src/protocol-publisher.ts +10 -16
  34. package/src/protocol-types.ts +5 -8
  35. package/src/protocol.ts +13 -7
  36. package/src/react.ts +51 -64
  37. package/src/size-anchor.ts +131 -0
  38. package/src/types.ts +29 -37
  39. package/src/view-anchor.ts +221 -287
  40. package/dist/measure-loop.d.ts +0 -24
  41. package/dist/measure-loop.d.ts.map +0 -1
  42. package/dist/measure-loop.js +0 -89
  43. package/dist/size-advertiser.d.ts.map +0 -1
  44. package/dist/size-advertiser.js +0 -94
  45. package/src/measure-loop.ts +0 -101
  46. package/src/size-advertiser.ts +0 -108
@@ -2,57 +2,55 @@
2
2
 
3
3
  view-anchor 支持双向几何同步:
4
4
 
5
- - **正向(`createViewAnchor`)**:测量 DOM 占位元素的位置和尺寸,通过 `publish(bounds)` 把 `{ x, y, width, height }` 交给应用,由应用更新外部画面。
6
- - **反向(`createSizeAdvertiser`)**:内容区域测量自身尺寸,通过 `publish(size)` 把 `{ axis, extent }` 交给应用,由应用调整占位元素大小。
5
+ - **正向(`createViewAnchor`)**:测量 DOM 占位元素的可见性和位置尺寸,通过 `publish(placement)` 把 `{ visible: true, bounds }` 或 `{ visible: false }` 交给应用,由应用更新外部画面。
6
+ - **反向(`createSizeAnchor`)**:内容区域测量自身尺寸,通过 `publish(size)` 把 `{ axis, extent }` 交给应用,由应用调整占位元素大小。
7
7
 
8
8
  常见场景:嵌套在宿主中的工具栏或面板,其宽度由宿主布局决定,高度则由子视图自身的内容决定。
9
9
 
10
- ## 1. 为什么正向走同步、反向走 RAF
10
+ ## 1. 正向与反向的调度差异
11
11
 
12
- 两个方向在性能和交互上的要求不同,因此没有共用同一套调度逻辑:
13
-
14
- - **正向(`createViewAnchor`)采用同步发布。**
15
- 外部画面的更新本身可能已有延迟;测量后再等一次 `requestAnimationFrame` 会增加拖拽时的跟随延迟。因此正向在 `ResizeObserver` 和窗口 `resize` 回调中**同步测量并调用 `publish(bounds)`**,相同矩形直接跳过。
16
- - **反向(`createSizeAdvertiser`)采用 RAF 调度(`createMeasureLoop`)。**
17
- 反向构成一条反馈环:内容上报尺寸 → 应用调整占位大小 → 内容重新布局与测量 → 再次上报。在这个链路中,将上报频率限制在每帧至多一次(与屏幕刷新率对齐)能够有效避免高频震荡,同时提供平滑的缓冲。
12
+ - **正向(`createViewAnchor`)同步发布。** 外部画面的更新本身可能已有延迟;测量后再等一次 `requestAnimationFrame` 会增加拖拽时的跟随延迟。正向在 `ResizeObserver` 和窗口 `resize` 回调中同步测量并调用 `publish(placement)`。默认 `dedupe: true`,与上一次接受的 `Placement` 完全相同时跳过发布;传 `dedupe: false` 则每次测量都发布,即使值不变。
13
+ - **反向(`createSizeAnchor`)用 RAF 调度。** 反向构成一条反馈环:内容上报尺寸 → 应用调整占位大小 → 内容重新布局与测量 → 再次上报。将上报频率限制在每帧至多一次能避免高频震荡。同样默认 `dedupe: true`;`dedupe: false` 时每次测量都发布,即使 extent 不变。
18
14
 
19
15
  ## 2. 反向接口说明
20
16
 
21
17
  ```ts
22
- export type AdvertisedAxis = 'block' | 'inline'
18
+ export type SizeAxis = 'block' | 'inline'
23
19
 
24
- export interface AdvertisedSize {
25
- readonly axis: AdvertisedAxis // 固定轴,供宿主做白名单检查
26
- readonly extent: number // 内容尺寸(CSS 像素),四舍五入且 >= 0
20
+ export interface SizeMeasurement {
21
+ readonly axis: SizeAxis // 固定轴,供宿主做白名单检查
22
+ readonly extent: number // 目标元素该轴的 border-box 尺寸(CSS 像素),四舍五入且 >= 0
27
23
  }
28
24
 
29
- export interface SizeAdvertiserOptions {
30
- axis: AdvertisedAxis // 创建后固定,每个 advertiser 只负责一条轴
31
- publish: Publisher<AdvertisedSize> // 接收尺寸发布的回调
25
+ export interface SizeAnchorOptions {
26
+ axis: SizeAxis // 创建后固定,每个 size anchor 只负责一条轴
27
+ publish: Publisher<SizeMeasurement> // 接收尺寸发布的回调
32
28
  signal?: AbortSignal // abort 后停止监听并取消已排队的帧
29
+ dedupe?: boolean // 默认 true,与上次发布的 extent 相同则跳过
33
30
  }
34
31
 
35
- export interface SizeAdvertiserHandle {
36
- update(publish: Publisher<AdvertisedSize>): void // 切换 publish 回调
32
+ export interface SizeAnchorHandle {
33
+ update(opts: { publish: Publisher<SizeMeasurement>; dedupe?: boolean }): void // 应用一份完整的新选项并立即重新报告当前尺寸;省略 dedupe 重置为默认值 true
37
34
  dispose(): void // 停止监听并取消 RAF
38
35
  }
39
36
 
40
- export function createSizeAdvertiser(
37
+ export function createSizeAnchor(
41
38
  target: HTMLElement,
42
- opts: SizeAdvertiserOptions,
43
- ): SizeAdvertiserHandle
39
+ opts: SizeAnchorOptions,
40
+ ): SizeAnchorHandle
44
41
  ```
45
42
 
46
43
  - 运行在下游视图的渲染环境中;从 `ResizeObserverEntry.borderBoxSize` 直接读取尺寸,避免在回调中触发 `getBoundingClientRect` 引起强制回流(reflow)。
47
44
  - 上报前会执行 `Math.round` 取整并钳位至 `>= 0`。
48
45
  - 目标元素 `target` 应当在所负责的轴上根据内容自适应(shrink-to-fit),不能被宿主设置的尺寸反向影响。
49
- - 如果需要同时汇报宽和高,应当针对不同轴分别创建两个独立的 advertiser,而不是放在同一条消息中,以保持数据流向的清晰。
46
+ - 如果需要同时汇报宽和高,应当针对不同轴分别创建两个独立的 size anchor,而不是放在同一条消息中,以保持数据流向的清晰。
47
+ - 节流场景下丢弃这次值应返回 `false` 以便之后重试;只保留最新值稍后发送的节流无需关闭 `dedupe`。
50
48
 
51
49
  ## 3. 单轴控制与收敛性
52
50
 
53
51
  为了避免死循环,必须遵循单轴控制原则:
54
52
 
55
- - 一个 advertiser 只测量并上报它负责的那条轴;另一条轴由应用单向设置,内容区域只读。
53
+ - 一个 size anchor 只测量并上报它负责的那条轴;另一条轴由应用单向设置,内容区域只读。
56
54
  - 典型案例:宿主决定宽度,下游决定高度。因为高度是内容流式排版的结果而不是输入,整个尺寸传递构成一个有向无环图(DAG),更新在单步内即可收敛。
57
55
  - 如果内容的高度又反过来改变宽度(或测量了 `<body>`/`<html>`),就会形成循环调整,导致界面抖动。
58
56
 
@@ -70,14 +68,13 @@ export function createSizeAdvertiser(
70
68
  | 轴白名单校验 | 宿主 | 检查 `axis` 是否与宿主预期的控制轴一致 |
71
69
  | 位置与层级锁定 | 宿主 | 下游不能擅自修改自身的坐标位置或 z-index |
72
70
 
73
- **针对错误选择 target 的提醒**:
74
- 如果把 `target` 设为 `document.body` 或 `documentElement`,其尺寸直接等于宿主给定的视口大小,会导致无法正确收缩。对此 `createSizeAdvertiser` 在初始化时提供了一次性控制台警告,帮助快速排查问题。
71
+ 如果把 `target` 设为 `document.body` 或 `documentElement`,其尺寸直接等于宿主给定的视口大小,无法正确收缩。`createSizeAnchor` 在初始化时会输出一次控制台警告。
75
72
 
76
73
  ## 5. 宿主与下游的配合方式
77
74
 
78
- 正向与反向并不直接通信,它们通过宿主中的**占位 DOM 元素**进行连接:
75
+ 正向与反向通过宿主中的占位 DOM 元素连接:
79
76
 
80
- - 下游的 `createSizeAdvertiser` 将内容尺寸通知宿主,宿主更新占位元素的高度。
77
+ - 下游的 `createSizeAnchor` 将内容尺寸通知宿主,宿主更新占位元素的高度。
81
78
  - 宿主的 `createViewAnchor` 监听占位元素的矩形变化,将新位置同步给外部视图。
82
79
 
83
80
  ```mermaid
@@ -85,17 +82,15 @@ flowchart LR
85
82
  C["内容容器\n高度由自身内容决定"] -->|"① publish(size)"| H["应用回调\n校验并限制数值"]
86
83
  H -->|"② 写入 style.height"| DIV["占位元素"]
87
84
  DIV -->|"ResizeObserver 观测"| VA["createViewAnchor"]
88
- VA -->|"③ publish(bounds)"| A["应用回调"]
85
+ VA -->|"③ publish(placement)"| A["应用回调"]
89
86
  A -->|"④ 应用矩形"| S["外部画面"]
90
87
  ```
91
88
 
92
- 1. **内容区域**:通过 `createSizeAdvertiser` 测量高度并调用 `publish(size)`。
89
+ 1. **内容区域**:通过 `createSizeAnchor` 测量高度并调用 `publish(size)`。
93
90
  2. **应用**:限制收到的高度(例如在 `minHeight` 和 `maxHeight` 之间),然后写入占位元素的样式。
94
- 3. **占位元素**:尺寸改变后,`createViewAnchor` 的 `ResizeObserver` 会测量新的绝对矩形并调用 `publish(bounds)`。
95
- 4. **应用**:把矩形用于定位外部画面。
91
+ 3. **占位元素**:尺寸改变后,`createViewAnchor` 的 `ResizeObserver` 会测量新的 `Placement` 并调用 `publish(placement)`。
92
+ 4. **应用**:把 `visible`/`bounds` 用于定位或隐藏外部画面。
96
93
 
97
94
  ## 6. 何时需要反向尺寸上报
98
95
 
99
- 只有外部画面的内容尺寸需要反过来改变宿主布局时,才需要 `createSizeAdvertiser`。如果应用已经能直接知道或设置这个尺寸,不需要引入反向反馈链路。
100
-
101
- 反向方案适用于需要测量特定内部 DOM 节点的场景。保持“一个方向只控制一个轴”,并在应用侧限制接收值,能避免尺寸互相驱动导致的抖动。
96
+ 只有外部画面的内容尺寸需要反过来改变宿主布局时,才需要 `createSizeAnchor`;应用已经能直接知道或设置这个尺寸时,不需要反向链路。保持"一个方向只控制一个轴",并在应用侧限制接收值,能避免尺寸互相驱动导致的抖动。