view-anchor 0.1.2 → 0.2.1
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 +111 -39
- package/README.zh-CN.md +119 -47
- package/dist/index.d.ts +6 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -15
- package/dist/measure-loop.d.ts +9 -28
- package/dist/measure-loop.d.ts.map +1 -1
- package/dist/measure-loop.js +57 -17
- package/dist/protocol-publisher.d.ts +41 -0
- package/dist/protocol-publisher.d.ts.map +1 -0
- package/dist/protocol-publisher.js +207 -0
- package/dist/protocol-types.d.ts +36 -0
- package/dist/protocol-types.d.ts.map +1 -0
- package/dist/protocol-types.js +10 -0
- package/dist/protocol.d.ts +35 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +128 -0
- package/dist/react.d.ts +18 -30
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +125 -122
- package/dist/size-advertiser.d.ts +9 -14
- package/dist/size-advertiser.d.ts.map +1 -1
- package/dist/size-advertiser.js +29 -28
- package/dist/types.d.ts +29 -73
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -15
- package/dist/view-anchor.d.ts +36 -77
- package/dist/view-anchor.d.ts.map +1 -1
- package/dist/view-anchor.js +230 -181
- package/docs/bidirectional-design.md +78 -106
- package/docs/index.html +772 -0
- package/docs/mechanism.md +116 -0
- package/docs/performance-report.md +63 -0
- package/docs/protocol.md +108 -0
- package/package.json +37 -14
- package/src/index.ts +8 -24
- package/src/measure-loop.ts +56 -42
- package/src/protocol-publisher.ts +254 -0
- package/src/protocol-types.ts +43 -0
- package/src/protocol.ts +181 -0
- package/src/react.ts +175 -139
- package/src/size-advertiser.ts +33 -31
- package/src/types.ts +35 -82
- package/src/view-anchor.ts +259 -236
- package/docs/anchor-3d.html +0 -615
- package/docs/mechanism.mdx +0 -119
package/docs/mechanism.mdx
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: view-anchor
|
|
3
|
-
description: 让主进程原生视图(Electron WebContentsView)始终贴住一个 DOM 元素的矩形。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# view-anchor
|
|
7
|
-
|
|
8
|
-
让一块主进程原生视图(Electron `WebContentsView`)始终贴住一个 DOM 元素的屏幕矩形。它测量目标元素的 `getBoundingClientRect()`,把矩形交给注入的 `publish` 回调(由你接上 IPC → `setBounds`),并在元素位移或缩放时重新发布。
|
|
9
|
-
|
|
10
|
-
核心不依赖 React、Electron 或任何宿主布局引擎;涉及 React 的代码只在适配层。
|
|
11
|
-
|
|
12
|
-
<iframe
|
|
13
|
-
src="./anchor-3d.html"
|
|
14
|
-
title="view-anchor 交互演示"
|
|
15
|
-
style={{ width: '100%', height: '560px', border: '0', borderRadius: '12px' }}
|
|
16
|
-
/>
|
|
17
|
-
|
|
18
|
-
## 架构
|
|
19
|
-
|
|
20
|
-
```mermaid
|
|
21
|
-
flowchart LR
|
|
22
|
-
subgraph R["渲染进程 · WebContents"]
|
|
23
|
-
DIV["占位 div<br/>(CSS 布局,自身不渲染)"]
|
|
24
|
-
end
|
|
25
|
-
subgraph M["主进程"]
|
|
26
|
-
WCV["WebContentsView<br/>(原生图层,覆盖在网页之上)"]
|
|
27
|
-
end
|
|
28
|
-
DIV -->|"getBoundingClientRect()"| VA["view-anchor"]
|
|
29
|
-
VA -->|"publish(bounds)<br/>IPC → setBounds"| WCV
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
`WebContentsView` 是主进程对象,位置只能由主进程 `setBounds` 设定;而布局是渲染进程用 CSS 算的。两边不直接接触,view-anchor 就是它们之间的桥:占位 div 在 DOM 里占位但不渲染,原生视图浮在其上,由 view-anchor 维持贴合。`publish` 是注入的,所以核心对 Electron 一无所知——测试里它是 spy,生产里是一次 IPC 发送。
|
|
33
|
-
|
|
34
|
-
## createViewAnchor(target, opts)
|
|
35
|
-
|
|
36
|
-
命令式核心,把一块原生视图绑定到一个元素,返回 `{ update, dispose }`。
|
|
37
|
-
|
|
38
|
-
```ts
|
|
39
|
-
const handle = createViewAnchor(target, {
|
|
40
|
-
present: true, // 是否挂载原生视图
|
|
41
|
-
publish: (bounds) => { ... }, // 接收实时矩形,负责 IPC → setBounds
|
|
42
|
-
})
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
| 状态 / 调用 | 行为 |
|
|
46
|
-
|---|---|
|
|
47
|
-
| `present: true` | 立即发布测量矩形,之后在每次 `ResizeObserver` 触发、窗口 `resize` 时**同步**重新发布。 |
|
|
48
|
-
| `present: false` | 停止观察,发布一次 `{0,0,0,0}`。 |
|
|
49
|
-
| `update(opts)` | 按新选项同步重新应用(重置去重基线,强制重发一次)。 |
|
|
50
|
-
| `dispose()` | 停止观察、移除监听,此后不再发布(也不补发零矩形)。 |
|
|
51
|
-
|
|
52
|
-
测量矩形按 `Math.round` 取整;**width/height 钳到 ≥0(0=隐藏信号),但 x/y 允许负**——元素滚出上 / 左边缘时原点本就该是负的,钳零会把原生视图钉在边缘而非跟随它移出屏外(各消费者的 IPC schema 自己定 origin 策略)。
|
|
53
|
-
|
|
54
|
-
**同步发布,不走 RAF**:原生 overlay 是跨进程 `WebContentsView`,`setBounds` 本就比渲染进程的 DOM 绘制晚约 1 个合成帧;再用 RAF 推迟一帧 → 拖拽时 overlay 可见拖尾(放大时露背景最明显)。在触发回调里直接测量+发布去掉这一自加的帧。RAF 原本承担的「合并同帧多次触发」由**同值去重**接管:若本次测量矩形与上次已发布的逐字段相等就丢弃,所以一次连续拖拽里每个不同矩形至多发一次。`update` 会先把去重基线清空,保证状态变化(zoom 骑在 `publish` 闭包里、不在 `Bounds` 里)即使几何不变也重发一次。
|
|
55
|
-
|
|
56
|
-
撤销安全:没有排队的帧可以「跑赢」状态变化——每次发布开头同步读 `disposed`/`present`,`update`/`dispose` 之后的触发立即 bail。
|
|
57
|
-
|
|
58
|
-
## present / 零矩形 语义
|
|
59
|
-
|
|
60
|
-
- **`present`** —— 「原生视图是否该挂载」的唯一事实来源,与 DOM 生命周期解耦。
|
|
61
|
-
- **`{0,0,0,0}`(ZERO)** —— 收起信号。宿主把零面积读作「摘除子视图,但保留其 `WebContents` 存活」,即收起而非销毁,重新挂载瞬时且状态完整。
|
|
62
|
-
- **dispose 后保持静默** —— 不补发零矩形。元素真正消失时,应由调用方先发 ZERO 再 dispose(适配层已替你处理)。
|
|
63
|
-
|
|
64
|
-
## createPlacementAnchor(target, opts) — 显式可见性变体
|
|
65
|
-
|
|
66
|
-
`createViewAnchor` 用零矩形 `{0,0,0,0}` 兼任「收起」信号,可见性是从几何推断的。`createPlacementAnchor` 把可见性提升为显式判别式,发的是 `Placement` 而非 `Bounds`:
|
|
67
|
-
|
|
68
|
-
```ts
|
|
69
|
-
const handle = createPlacementAnchor(target, {
|
|
70
|
-
visible: true, // 调用方意图:视图是否该在屏
|
|
71
|
-
publish: (placement) => { ... },
|
|
72
|
-
})
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
| 状态 / 选项 | 行为 |
|
|
76
|
-
|---|---|
|
|
77
|
-
| `visible: true` | 发 `measurePlacement(target)`(`{ visible:true, bounds }`),并在每次 `ResizeObserver` / `resize` 触发**同步**重发。 |
|
|
78
|
-
| `visible: false` | 发 `{ visible:false }`(**不是**零矩形),停止观察。 |
|
|
79
|
-
| `guardDisplayNone`(默认 false) | 开启后,测出零面积的 target(display:none / 卸载 / 首帧未稳)发 `{ visible:false }` 而非 `{ visible:true, bounds:0×0 }`,并挂 `IntersectionObserver` 捕捉 `ResizeObserver` 不上报的 display:none 切换。 |
|
|
80
|
-
| `followScroll`(默认 false) | 捕获阶段监听 `window` 的 `scroll`,祖先滚动容器滚动 target 时重测重发。 |
|
|
81
|
-
| `followGeometry`(默认 false) | 按需开窗的 RAF 几何哨兵,捕捉无 DOM 事件上报的祖先 transform / reflow 位移;几何静止几帧后自动关窗,空闲时零成本。可由 `pulse(durationMs?)` 显式开窗。 |
|
|
82
|
-
|
|
83
|
-
去重携带判别式(`samePlacement`),所以可见性翻转绝不会被同值合并掉——即使几何看上去一样。真正 0×0 但在屏的视图(`{ visible:true, bounds:{...,width:0} }`)与隐藏视图(`{ visible:false }`)由此可区分,而零矩形约定下二者会塌缩成同一个值。
|
|
84
|
-
|
|
85
|
-
## useViewAnchor(opts)
|
|
86
|
-
|
|
87
|
-
React 适配层,返回一个挂到占位元素上的 ref 回调。
|
|
88
|
-
|
|
89
|
-
```tsx
|
|
90
|
-
const ref = useViewAnchor({
|
|
91
|
-
present, // boolean
|
|
92
|
-
publish, // (bounds) => void
|
|
93
|
-
deps: [signature], // 可选:会移动矩形、但 DOM 看不见的状态
|
|
94
|
-
})
|
|
95
|
-
return <div ref={ref} />
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
| 时机 | 行为 |
|
|
99
|
-
|---|---|
|
|
100
|
-
| 挂载 | `createViewAnchor(el, opts)` |
|
|
101
|
-
| `opts` / `deps` 变化 | `update` |
|
|
102
|
-
| 卸下(`ref → null`)或卸载 | 先发一帧零矩形,再 `dispose` |
|
|
103
|
-
|
|
104
|
-
**`deps`** —— `ResizeObserver` 只响应纯几何变化。会移动矩形却不改变被观察元素尺寸的状态(布局拓扑签名、路由切换、兄弟标签页 `display:none`)要放进 `deps`。数组长度需在每次渲染间保持稳定。
|
|
105
|
-
|
|
106
|
-
> 适配层已处理 React 18 StrictMode 的 effect 双触发,以及 hidden→shown 重挂载——保证恰好发布一次真实矩形、不误发零矩形。
|
|
107
|
-
|
|
108
|
-
## 文件
|
|
109
|
-
|
|
110
|
-
| 文件 | 作用 |
|
|
111
|
-
|---|---|
|
|
112
|
-
| `src/view-anchor.ts` | 正向命令式核心 `createViewAnchor` / `createPlacementAnchor` / `measurePlacement`。无 React、无 Electron。 |
|
|
113
|
-
| `src/react.ts` | React 适配层 `useViewAnchor`(基于 `createViewAnchor`)。 |
|
|
114
|
-
| `src/size-advertiser.ts` | 反向命令式核心 `createSizeAdvertiser`。 |
|
|
115
|
-
| `src/measure-loop.ts` | 反向专用的 RAF 合并 / 去重 / dispose 引擎 `createMeasureLoop`(不导出)。 |
|
|
116
|
-
| `src/types.ts` | `Bounds`、`Placement`、各核心的选项与句柄类型、反向的 `AdvertisedAxis` / `AdvertisedSize`。 |
|
|
117
|
-
| `src/index.ts` | 对外公开面。 |
|
|
118
|
-
|
|
119
|
-
正向运行时依赖只有 `react`(仅适配层)和浏览器 API(`ResizeObserver`、`getBoundingClientRect`、`window` 的 `resize` 监听)。`requestAnimationFrame` 只在反向 `createSizeAdvertiser`(经 `createMeasureLoop`)和 `createPlacementAnchor` 的 opt-in `followGeometry` 哨兵里用;正向的 `createViewAnchor` 同步发布、不走 RAF。
|