gis-view-webgl2 0.1.5 → 0.1.7
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 +6 -3
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +172 -8
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +121 -0
- package/docs/GisView.md +1 -1
- package/docs/ai/06-/344/274/232/350/257/235/347/264/242/345/274/225/344/270/216/345/244/215/346/237/245/346/226/271/346/263/225.md +117 -0
- package/docs/demos/LabelDemo.vue +2 -2
- package/docs/demos/MinimapDemo.vue +154 -1
- package/docs/demos/SearchFilterDemo.vue +113 -1
- package/docs/demos/StyleDemo.vue +1 -1
- package/docs/demos/api-index.js +22 -14
- package/docs/demos/demos.js +1 -1
- package/docs/demos/gis-view-demo/useGisViewDemo.js +2 -2
- package/docs/internals/13-/351/271/260/347/234/274/345/233/276.md +2 -0
- package/docs/internals/16-01-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/212.md +13 -2
- package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +11 -1
- package/docs/internals/18-/346/200/247/350/203/275/345/256/236/346/265/213/344/270/216WebGPU/350/257/204/344/274/260.md +61 -1
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +5 -4
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +38 -10
- package/docs/manual/05-/347/224/273/350/264/250/344/270/216/346/211/271/351/207/217/346/233/264/346/226/260.md +1 -1
- package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +7 -6
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +59 -3
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +14 -1
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +23 -5
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +12 -7
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +16 -8
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +27 -2
- package/docs/manual/19-/350/257/273/346/225/260/344/270/216/346/213/226/346/213/275/351/242/204/350/247/210.md +4 -0
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +9 -3
- package/docs/manual/21-/350/257/264/346/230/216/344/270/216/345/270/270/350/247/201/351/227/256/351/242/230.md +1 -1
- package/docs/steps/README.md +5 -5
- package/docs/steps/selftest-demos.html +105 -6
- package/docs/steps/selftest-docs.html +3 -3
- package/docs/steps/selftest-features.html +1 -1
- package/docs/steps/selftest-full.html +42 -1
- package/docs/steps/selftest-perf.html +600 -421
- package/docs/steps/selftest-select.html +168 -1
- package/package.json +1 -1
package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md
CHANGED
|
@@ -36,15 +36,19 @@
|
|
|
36
36
|
| `zoomTo(scale, origin?)` | 直接设成指定比例(像素 / 世界单位) |
|
|
37
37
|
| `zoomToBounds(bounds, padding?)` | 把一段世界坐标矩形适应到视图内,退化成一点时只居中不改比例 |
|
|
38
38
|
| `focusOn(target, options?)` | 把视角移到目标上:单条(搜索命中 / 要素 id / `{ type: 'point' \| 'line' \| 'channel', id \| index }` / `{ channelId }` 整条通道 / `{ type: 'group', channelId?, groupId }` / 坐标 / 矩形 / `{ lngLat }`)或**一批**(数组取整体包围盒 —— 搜到三个节点就是这三个点的中心当屏幕中心);字符串先节点后线路,裸字符串不表示通道,同 id 用 `type` 消歧,分组必须显式写 `type: 'group'`(全局唯一 `groupId` 可省略通道)。**缺省只居中、比例一点不动**;`options.zoom: true` 才按解析出的真实范围适配(节点 / 线路 / 通道 / 分组共用这一条,不额外放大),`scale` 优先级高于 `zoom`。`options` 还认 `center: true`(显式只居中,与缺省一致)、`padding`、`maxZoom`、`select`(顺带选中这批,`mode` 决定怎么并进当前选中集) |
|
|
39
|
+
| `flyTo(target, options?)` | 平滑飞到与 `focusOn` 相同的目标:目标写法与 `zoom` / `scale` / `center` / `padding` / `maxZoom` / `select` / `mode` 完全同义,另认 `duration`(毫秒,缺省 450)与 `easing`(`linear` / `ease-in` / `ease-out` / `ease-in-out` 或 `(progress) => progress`)。中心线性插值、比例按对数插值;`duration <= 0` 或系统要求减少动态效果时立即落位。再次飞行、用户拖拽 / 滚轮或任何程序化改相机都会停在当前帧,不补跳到终点 |
|
|
39
40
|
| `search(keyword \| options)` | 按关键字搜索节点 / 线路 / 通道 / 通道分组并返回命中:`scope` 认复数的 `points` / `lines` / `channels` / `groups`,也认与 `focusOn` 一致的单数别名 `point` / `line` / `channel` / `group`;结果分 `points` / `lines` / `channels` / `groups` 四类,分组命中带 `channelIndex` / `groupId` / `groupIndex`,可直接交给 `focusOn` / `selectFeatures`。**不动视角**:要不要聚焦由调用方自己调 `focusOn`,见「搜索与定位」 |
|
|
40
41
|
| `searchAsync(keyword \| options)` | 同一份口径的异步版:数据量大时把扫描放进 Worker(10 万点 / 10 万线下同步要占主线程 40 ms 上下的那一段),返回 `Promise<GisViewSearchResult>`;结果带 `worker` 与 `stale` 两个字段,见「异步搜索」 |
|
|
41
42
|
| `setCamera(camera)` / `getCamera()` | 读写 `{ center, scale }` |
|
|
43
|
+
| `flyToCamera(camera, options?)` | 直接飞到相机目标 `{ center?, scale? }`:不写的那一项沿用当前相机,不经过要素解析、也不改选中与数据;`duration` / `easing` 与 `flyTo` 同义,参数非法时保持当前相机 |
|
|
44
|
+
| `stopCameraAnimation()` / `getCameraAnimation()` | 停止当前相机飞行(停在当前帧,返回实例)/ 读当前飞行 `{ active, progress, duration, from, to }`(没有动画时 `null`) |
|
|
42
45
|
| `getZoomBaseScale()` / `setZoomBaseScale(scale?)` | 读 / 重定**缩放联动的基准比例**(缺省是首次适应视图时的比例):不传参数就取当下相机的比例 —— 把现在这一屏当成「原样大小」,之后放大缩小都从它算倍数;配合 `pointZoomScale` / `lineZoomScale` 使用 |
|
|
43
|
-
| `getBounds()` | 数据范围(没有点时回退到背景 GeoJSON 的 `rawBounds`,也就是 `aspectScale`
|
|
46
|
+
| `getBounds()` | 数据范围(没有点时回退到背景 GeoJSON 的 `rawBounds`,也就是 `aspectScale` 之前的真实经纬度范围);每次返回独立副本,重复调用命中缓存 |
|
|
44
47
|
| `getViewRect()` | 当前可视范围的世界坐标矩形;自建缩略图 / 坐标读数用它换算视口框,内置鹰眼图见「鹰眼图」 |
|
|
45
48
|
| `getMinimapElement()` | 鹰眼图根节点(没配 `minimap` 时为 `null`):接整图拖动或自定义手势;配合 `minimap.container` 挂到外部宿主时拿到的仍是同一根节点 |
|
|
46
49
|
| `getMinimapZoom()` / `setMinimapZoom(zoom)` / `zoomMinimapBy(factor, origin?)` | 读 / 写鹰眼图自身的倍数:`set` 夹在 `minimap.minZoom` ~ `maxZoom` 之间,`zoomMinimapBy` 以画布 CSS 像素 `origin` 为锚点缩放(缺省小图中心),没配 `minimap` 时是空操作 |
|
|
47
50
|
| `getPoint(ref)` | 按 id / 下标 / `{ id }` / `{ index }` 取一个节点:拾取载荷(`item` / `x` / `y` / `layer` / `layerRank`)之外再给一份屏幕半径 `radius`;找不到返回 `null`。自建读数面板、右键菜单用它,省掉「自己遍历 `getData()` + 算投影」那一段 |
|
|
51
|
+
| `getPointStyle(index)` / `getLineStyle(index)` | 节点 / 线路解析之后的样式快照:节点的 `size` / `symbol` 取样式链(自身 > 规则 > 全局),线路的 `width` / `color` / `opacity` / `zIndex` 同源;不含悬停 / 选中样式、整体倍率与热力倍率,下标不存在时返回 `null`。合并节点与鹰眼图沿用主图尺寸时走这一份 |
|
|
48
52
|
| `getLine(ref)` | 取一条线路:拾取载荷 + `path` / `bounds` / `color` / `width`(与 `listLines()` 的一项同一份口径),通道回线还带 `channelId` / `groupId` / `channelIndex`;找不到返回 `null` |
|
|
49
53
|
| `getChannel(ref)` | 取一条通道:数据那一份(`index` / `item` / `id`)+ 成员表(`lineIndexes` / `skeleton` / `offsets` / `paths`)+ 分组上下文 `context`(几组 / 当前哪组 / 每组拐点数)、`from` / `to` / `anchored` / `spacing`;找不到返回 `null` |
|
|
50
54
|
| `listPoints(options?)` | 按条件列节点快照 `{ index, item, x, y, radius, layerRank, screen? }`:选项见 `GisViewListOptions`(`indexes` / `inView` / `limit` / `withScreen` / `visibleOnly`),可见判定与 `isPointVisible` 是同一份 |
|
|
@@ -67,18 +71,19 @@
|
|
|
67
71
|
| `createGeoMapper(options?)` | 建一个「数据坐标 → GeoJSON 坐标」的映射器:`mode: 'lngLat' \| 'fit' \| 'scale'`、`scale`(节点与 geojson 的大小比例)、`scaleX` / `scaleY` / `offsetX` / `offsetY` / `center` / `bounds` / `data` / `padding` / `target`,返回 `map` / `unmap` / `mapPoint` / `mapLine` / `mapChannel` / `mapData` 与最终的比例偏移;没有 GeoJSON 背景时是 `null` |
|
|
68
72
|
| `mapDataToGeoJson(data, options?)` | `createGeoMapper(options).mapData(data)` 的快捷写法:把 `{ points, lines, channels }` 整份搬到 GeoJSON 坐标,原数据不动;没有 GeoJSON 背景时是 `null` |
|
|
69
73
|
| `pick(x, y)` | 拾取指定画布位置上的要素(`clickable: false` 的要素不会返回) |
|
|
70
|
-
| `pickAt(clientX, clientY)` | 按**浏览器客户端坐标**拾取(就是鼠标事件里的 `clientX` / `clientY`):与指针事件载荷同一份结果(`type` / `index` / `item` / `distance` / `screen` / `coordinate` 与通道字段),另加 `target`(`'point'` / `'line'` / `'channel'`)与 `lngLat
|
|
74
|
+
| `pickAt(clientX, clientY)` | 按**浏览器客户端坐标**拾取(就是鼠标事件里的 `clientX` / `clientY`):与指针事件载荷同一份结果(`type` / `index` / `item` / `distance` / `screen` / `coordinate` 与通道字段),另加 `target`(`'point'` / `'line'` / `'channel'`)与 `lngLat`;命中通道回线时 `group` 是当前组、`groups` 是整条通道的全部分组;没命中返回 `null`。只读,不改选中也不动光标 |
|
|
75
|
+
| `selectAt(clientX, clientY)` | 只有客户端坐标时用它像普通单击一样立即选中命中要素:节点 / 线路直接选中,通道回线按 `channelSelectMode` 摊开;空白或聚合圆返回 `null` 且不改选中。不等待 `selectTrigger: 'dblClick'`,也不派发 `onClick` / `onDblClick`;每次成功命中都发 `onSelect`,选中集实际变化时再发 `onSelectChange`,顺序 `onSelect → onSelectChange`(`cause: 'api'`)。选中通道回线时载荷带当前 `group` / 完整 `groups`,多通道看 `channelContexts`。已知节点 / 线路 id 时直接用 `selectFeatures`,见「按坐标单击选中(selectAt)」 |
|
|
71
76
|
| `hoverAt(clientX, clientY)` / `clearHover()` / `getHover()` | 程序化悬停:`hoverAt` 让指针「停」到某个客户端坐标上(悬停高亮、光标与 `onHover` 一起更新,返回与 `onHover` 载荷同一份读数,空白处 `null`;不走悬停阈值,也不用先订阅事件)、`clearHover()` 撤掉并补发一条 `onHover(null)`、`getHover()` 随时读当前这一份 —— 外部列表 / 表格 hover 到某一行时联动高亮图上的要素用它 |
|
|
72
77
|
| `getFeaturesInRect(rect)` | 画布 CSS 像素矩形里有什么(与框选 / `selectInBox` 同一套坐标与判定):返回 `{ rect, worldRect, points, lines }` —— `points` 是框内的节点(与 `onSelect` 的 `items` 同一口径)、`lines` 是包围盒沾到框的线路(与 `listLines` 同一份)、`worldRect` 是这块像素对应的世界坐标范围;只读,不改选中也不画框 |
|
|
73
78
|
| `getChannelsInView()` | 当前视口里有哪几条通道(同一通道的几回线只算一条):每条给 `{ channelId, channelIndex, channel, lineIndexes, groupIds }`,侧边栏「画面里的通道」、按视口统计用它 |
|
|
74
|
-
| `selectFeatures(refs, options?)` | 接口选中(换一份选中集;`selectFeatures(null)` / 空数组就是清空):`refs` 认节点 / 线路 id、id 数组、`{ type, index }`、通道写法(`{ type: 'channel', channelId }` / `{ channelId, groupId }` / `{ channelId, lineIds }`)、分组写法(`{ type: 'group', channelId?, groupId }`,全局唯一 `groupId` 可省略通道)以及拾取载荷与搜索命中;`options.mode` 给 `'replace'`(缺省,换一份)/ `'add'` / `'toggle'`(在就取消、不在就选上)/ `'remove'`,见「接口选中(selectFeatures)与读回选中」 |
|
|
79
|
+
| `selectFeatures(refs, options?)` | 接口选中(换一份选中集;`selectFeatures(null)` / 空数组就是清空):`refs` 认节点 / 线路 id、id 数组、`{ type, index }`、通道写法(`{ type: 'channel', channelId }` / `{ channelId, groupId }` / `{ channelId, lineIds }`)、分组写法(`{ type: 'group', channelId?, groupId }`,全局唯一 `groupId` 可省略通道)以及拾取载荷与搜索命中;`options.mode` 给 `'replace'`(缺省,换一份)/ `'add'` / `'toggle'`(在就取消、不在就选上)/ `'remove'`。会同步选中样式与读数;选中集实际变化时按 `onSelect → onSelectChange` 派发(`cause: 'api'`),载荷带同一份通道上下文(单通道摊平,多通道看 `channelContexts`),不发 `onClick` / `onDblClick`,见「接口选中(selectFeatures)与读回选中」 |
|
|
75
80
|
| `selectWaypoint(lineRef, pathIndex?, options?)` | 代码选中一个拐点:`selectWaypoint('line-1', 2)` / `{ lineIndex, pathIndex }` / `{ lineId, pathIndex }`,并同时选中所在线路;有效下标从 `getWaypoints()` 取。传 `null` 只取消拐点选中、不改要素选中集,见「代码选中拐点(selectWaypoint)」 |
|
|
76
|
-
| `deselect(refs?)` | 取消选中:写法与 `selectFeatures` 完全一样,把这些从当前选中集里去掉、其余选中原样留着;不传 / `null` 等于取消全部(也就是 `selectFeatures(null)
|
|
81
|
+
| `deselect(refs?)` | 取消选中:写法与 `selectFeatures` 完全一样,把这些从当前选中集里去掉、其余选中原样留着;不传 / `null` 等于取消全部(也就是 `selectFeatures(null)`),空数组是「没有目标」什么都不做。改变选中集时按 `onSelect → onSelectChange` 派发(`cause: 'api'`;清空时 `onSelect` 载荷为 `null`),见「取消选中(deselect / selectFeatures(null))」 |
|
|
77
82
|
| `getSelections()` | 读回选中结果 `[{ type, index, item, … }]`(按选中顺序):要 id 列表 / 个数 / 包围盒看 `getSelectionData()` |
|
|
78
83
|
| `isSelected(ref)` | 这个要素在不在当前选中集里:`isSelected('node-0')`(id)/ `isSelected({ type: 'line', index: 12 })`(也可以只写下标,按 id 认不出来时当下标),返回 `true` / `false`。面板上高亮「当前这一行」时不必自己比对 `getSelections()`;选中集变化时缓存一份键集合(见「接口选中」),十万级选中下这一问也是常数级 |
|
|
79
84
|
| `getSelectionData()` | 选中集一次取全:读数(`ids` / `count` / `pointCount` / `lineCount` / `channelCount`)加要素本身(`points` / `lines` / `channels`(按通道去重,与 `getData()` 同一批对象)),另有 `items`(与 `getSelections()` 同一份)、`pointIndexes` / `lineIndexes` 与 `bounds`(世界包围盒,空选中时 `null`),见「接口选中(selectFeatures)与读回选中」 |
|
|
80
85
|
| `getCascadeHighlight()` | 当前点击级联高亮的读数:`{ origin, scope, points, lines, pointIndexes, lineIndexes }`;没有高亮或 `cascadeHighlight` 关着时返回 `null`。它只读、不改选中集,见「点击级联高亮」 |
|
|
81
|
-
| `selectInBox(box, options?)` | 按画布 CSS 像素矩形框选,等价于 Shift + 拖动(跟随 `shortcuts.boxSelect`);`options.selectionKinds` 可只覆盖这一次的节点 / 线路 /
|
|
86
|
+
| `selectInBox(box, options?)` | 按画布 CSS 像素矩形框选,等价于 Shift + 拖动(跟随 `shortcuts.boxSelect`);`options.selectionKinds` 可只覆盖这一次的节点 / 线路 / 通道类型过滤。每次框选操作都发 `onSelect`(空框的载荷为 `null`),选中集实际变化时再发 `onSelectChange`,顺序 `onSelect → onSelectChange`(`cause: 'boxSelect'`);通道回线的分组字段与鼠标框选同一份 |
|
|
82
87
|
| `beginCopyPoint(options?)` | 开始复制一个节点:给 `id` / `index` 直接出预览(`commit: true` 出预览即落地,`follow: true` 跟随鼠标,`autoCommit: false` 停在预览态),不给来源就是「先点按钮再选节点」模式,见「节点复制」 |
|
|
83
88
|
| `commitPointCopy()` / `cancelPointCopy()` | 落地 / 取消预览中的复制(返回 `end` / `cancel` 载荷) |
|
|
84
89
|
| `updateCopyPoint(patch)` | 改预览节点的数据(等价于回调里的 `payload.update` / `setItem`),返回改完的节点 |
|
|
@@ -189,14 +194,14 @@
|
|
|
189
194
|
| `getPointEffects()` | 这一帧涟漪的读数 `{ index, x, y, color, radius, count, period, from }`(世界坐标 + 颜色 + 圈数 + 周期):自建覆盖层与自测用它;拖动就地挪过圈之后读到的就是挪完的位置,没配涟漪那些节点不在表里 |
|
|
190
195
|
| `getPrebake()` / `runPrebake()` | 预热读数(这次配了哪些符号 / 颜色 / 尺寸 / 逐组合,没配是 `null`)/ 按当前配置再烤一遍并返回组合条数,见「贴图符号预热(prebake)」 |
|
|
191
196
|
| `getSymbolCombos()` | 收出这份数据真正会用到的贴图组合 `{ combos, symbols, pointColors, sizes, lineColors }`:喂给 `prebake.combos` 就是不重不漏的一份(一条一张、逐条命中),见「贴图符号预热(prebake)」 |
|
|
192
|
-
| `el` / `canvas` / `gl` |
|
|
197
|
+
| `el` / `canvas` / `gl` / `cameraAnimating` / `dataVersion` | 根节点、画布元素、WebGL2 上下文,只读的「相机是否正在飞行」,以及数据修订号(数据变更 +1,只改全局配置 / 样式不变) |
|
|
193
198
|
| `requestRender()` | 请求重画一帧:WebGL 自定义层的数据源变了、组件自己不知道时调用;返回实例本身 |
|
|
194
199
|
| `addWebGLLayer(layer)` | 加一个 WebGL 自定义层(同 id 覆盖),返回卸载函数;`draw` 拿 `gl` / `canvas` / `viewport` / `camera` / `frame` / `project` / `unproject` / `requestRender`,在内置内容之后绘制 |
|
|
195
200
|
| `updateWebGLLayer(id, patch)` / `removeWebGLLayer(id)` / `clearWebGLLayers()` / `getWebGLLayers()` | 改 / 删 / 清空 / 读自定义层;`order` 小的先画,同 `order` 保持加入顺序,`enabled: false` 跳过,层不参与拾取与导出 |
|
|
196
201
|
|
|
197
202
|
返回 `this` 的方法都可以链式调用;返回其它值的方法(`get*` / `list*` / `export*` / `search` / `pick*` / `add*` / `remove*` / `copy*` / `merge*` / `locate*` 这些)在下面的「参数速查」里都标了返回类型。
|
|
198
203
|
|
|
199
|
-
**怎么确认「组件到底有哪些属性和方法」**:上面那张表按功能分组介绍,下面的「参数速查」把实例的**每个成员逐个列全**(当前
|
|
204
|
+
**怎么确认「组件到底有哪些属性和方法」**:上面那张表按功能分组介绍,下面的「参数速查」把实例的**每个成员逐个列全**(当前 265 个:260 个方法 + `el` / `canvas` / `gl` / `cameraAnimating` / `dataVersion` 五个属性),`options` 表列全 96 个键,数据字段表列全 250 个字段。这三份清单不靠手抄 —— 它们直接对着源码与类型对账:
|
|
200
205
|
|
|
201
206
|
- `node docs/steps/gen-api-index.mjs`:从 `create.js` 的实例定义、本文档的两张表与 `types.d.ts` 生成 `docs/demos/api-index.js`(`API_SUMMARY` / `API_SECTIONS` / `API_OPTIONS`),demo 的「实例 API」面板读的就是它;带 `--check` 时只校验有没有过期。
|
|
202
207
|
- `node docs/steps/check-doc-coverage.mjs`:10 项双向对账 —— 实例成员 × 文档 / × 参数速查 / × 类型、参数速查与实例方法主表各自反查过期条目、类型反查过期声明、数据字段 × 文档、options 键 × 文档 / options 表反查过期字段、demo 清单是否最新。加一个实例成员、改一个 options 键而没同步文档时,这个脚本会直接报出来。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 17 参数速查(逐个方法)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 265 个成员逐个列全:参数、默认值与返回。
|
|
4
4
|
|
|
5
5
|
### 参数速查(逐个方法)
|
|
6
6
|
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
- **同步方法返回 `this` 时可链式调用**:所有 `update*` / `set*` / `add*` / `remove*`、选中与视角方法都先完成本地状态更新再返回实例;需要结果时用相邻的 `get*` / `list*` 读回。少数返回数组、对象或 `null` 的方法在「返回」列单独标出。
|
|
16
16
|
- **数据写入只发一次通知**:`updatePoint`、`updateLine`、`patch*`、`add*`、`remove*` 等每次成功改数据都会发一条对应的 `dataChange`(底层统一为 `updatePoints` / `updateLines` / `updateChannels` 等类型)并落一步历史;在 `batch()` 里连续调用会合并成一条。`emit: false` 只写数据、不发通知,适合宿主自己在保存成功后统一刷新。
|
|
17
|
-
- **选中方法会同步选中集**:`selectFeatures` / `selectWaypoint` / `selectInBox` 先更新引擎选中态,再同步 `getSelections()`、`getSelectionData()` 与 `isSelected()
|
|
18
|
-
- **视角方法会更新相机**:`focusOn` / `zoomTo` / `setCamera` / `fitView` 成功落到目标范围后更新 `getCamera()`,并触发 `onCameraChange`;目标认不出来、视口尚未量出或参数非法时保持当前相机。`focusOn` 缺省只居中、不改比例;`zoom` / `scale` / `center` / `padding` / `maxZoom` 只影响这一次调用,不会改写持久配置。
|
|
17
|
+
- **选中方法会同步选中集**:`selectFeatures` / `selectWaypoint` / `selectInBox` 先更新引擎选中态,再同步 `getSelections()`、`getSelectionData()` 与 `isSelected()`。点击、`selectFeatures` / `deselect` / `selectAt` / `focusOn({ select })`、`selectInBox` 改的都是同一份选中 state。真实操作(点击 / Ctrl 取反 / 框选 / `selectAt`)每次成功操作都发 `onSelect`;按引用接口(`selectFeatures` / `deselect` / `focusOn({ select })`)只在实际改变时发。`onSelectChange` 覆盖所有来源且只在集合变化时发,顺序固定为 `onSelect → onSelectChange`。接口路径不会派发 `onClick` / `onDblClick`,`cause` 为 `'api'` 或 `'boxSelect'`;选中通道时两条载荷都带单通道摊平的 `channelId` / `group` / `groups`,多通道看 `channelContexts`。没有命中任何目标时不会凭空选中,`selectWaypoint(null)` 只清拐点、不动要素选中集。
|
|
18
|
+
- **视角方法会更新相机**:`focusOn` / `flyTo` / `zoomTo` / `setCamera` / `fitView` 成功落到目标范围后更新 `getCamera()`,并触发 `onCameraChange`;目标认不出来、视口尚未量出或参数非法时保持当前相机。`focusOn` 缺省只居中、不改比例;`flyTo` 只是同一条目标解析的平滑版,飞行中用户拖拽 / 滚轮、再次飞行或任何程序化改相机都会停在当前帧。`zoom` / `scale` / `center` / `padding` / `maxZoom` 只影响这一次调用,不会改写持久配置。
|
|
19
19
|
- **只读方法返回快照或副本**:`getSelectionData()`、`getFilter()`、`getStats()`、`snapshotScene()`、`listPoints()` / `listLines()` 返回的是调用当时的读数,不会随后续数据变更自动更新;需要最新值就再调一次。对象型字段的含义以 `types.d.ts` 同名接口为准。
|
|
20
20
|
- **失败是可控的**:找不到 id / 下标越界 / 不支持的写法会跳过这一条,能返回 `null` 或 `{ ok: false, reason }` 的就按对应签名返回;需要排查时订阅 `onWarning`。异步筛选 / 搜索通过完成事件、`getFilterResult()` / 搜索结果或返回的 Promise 取结果,不要在调用下一行假定 Worker 已经跑完。
|
|
21
21
|
|
|
@@ -59,13 +59,17 @@
|
|
|
59
59
|
| `zoomTo(scale, origin?)` | `scale: number` 目标缩放值(世界单位 → CSS 像素的比例);`origin?: [x, y]` 缩放中心 | `this` |
|
|
60
60
|
| `zoomToBounds(bounds, padding?)` | `bounds: GisViewBounds` 要装进画面的世界矩形 `[minX, minY, maxX, maxY]`(也认 `{ minX, minY, maxX, maxY }`);`padding?: number` 四周留白 | `this` |
|
|
61
61
|
| `focusOn(target, options?)` | `target: GisViewFocusTargets` 要对准的东西:id / `{ type: 'point' \| 'line' \| 'channel' \| 'group', id \| index }` / `{ channelId }`(整条通道)/ `{ type: 'group', channelId?, groupId }` / `[x, y]` 坐标 / 矩形 / `{ lngLat }` / `{ center }`,或者**一批**(数组取整体包围盒)。字符串按「先节点、再线路」,裸字符串不能表示通道;同 id 用显式 `type` 消歧,分组必须用 `type: 'group'`(`groupId` 全局唯一时可省略 `channelId`)。**缺省只居中、比例一点不动**;`options.zoom: true` 才按解析出的真实范围适配,四类目标共用这一条、没有额外放大倍率;`scale` 优先级高于 `zoom`,`maxZoom` 只给单点 / 单线这类零面积目标补最小范围。`options?: GisViewFocusOptions`:`zoom: true` 显式适配、`scale` 直接给比例、`center: true` 显式只居中(与缺省一致)、`padding` 要四周留白(CSS 像素,缺省 40)、`maxZoom` 限制这次适配的最大比例、`select` 顺带选中(`true` 选这次目标,也可另给一批写法)、`mode` 决定怎么并进当前选中集。目标认不出来或参数非法时什么都不做 | `this`(相机更新后发 `onCameraChange`;`select` 生效时另发 `onSelect` / `onSelectChange`) |
|
|
62
|
+
| `flyTo(target, options?)` | 目标与世界坐标解析、`zoom` / `scale` / `center` / `padding` / `maxZoom` / `select` / `mode` 均与 `focusOn` 同义。`options?: GisViewFlyOptions` 另认 `duration`(毫秒,缺省 450;`<= 0` 立即落位)与 `easing`(`'linear'` / `'ease-in'` / `'ease-out'` / `'ease-in-out'`,缺省 `'ease-in-out'`,也可给自定义 `(progress) => eased`)。中心线性、比例按对数插值;系统 `prefers-reduced-motion: reduce` 时立即落位。再次 `flyTo`、用户拖拽 / 滚轮、`setCamera` / `centerOn` / `zoom*` / `fitView` / `resetView` / `applyInitialView` 都会停在当前帧 | `this`(相机变化时发 `onCameraChange`;`select` 生效时另发选中事件) |
|
|
63
|
+
| `flyToCamera(camera, options?)` | `camera: GisViewCameraTarget` 直接给相机目标 `{ center?, scale? }`:没写 center / scale 的那一项沿用当前相机,不经过要素解析,也不改选中与数据;`options?: GisViewFlyCameraOptions` 的 `duration` / `easing` 与 `flyTo` 同义。中心 / 比例插值、减少动态效果、用户手势取消都走同一条相机动画 | `this`(相机变化时发 `onCameraChange`;目标非法时保持当前相机) |
|
|
62
64
|
| `search(input)` | `input: string \| GisViewSearchOptions` 关键字,或 `{ keyword, scope, fields, limit }` 这类选项。`scope` 可选 `'points'` / `'lines'` / `'channels'` / `'groups'`,也认与 `focusOn` 一致的单数别名 `'point'` / `'line'` / `'channel'` / `'group'` 及数组;省略或写错时四类全搜。`limit` 对每一类分别计数。返回的 `groups` / `hits` 带分组命中(`channelIndex` / `groupId` / `groupIndex`,`item` 是轻量快照)—— **只返回结果、不动视角**,要定位就自己调 `focusOn(result.hits, …)` | `GisViewSearchResult`:`keyword` / `scope` / `points` / `lines` / `channels` / `groups` / `hits` / `total` / `truncated` / `durationMs` |
|
|
63
65
|
| `searchAsync(input)` | 同上;分组范围的当前轻量快照会随请求交给 Worker | `Promise<GisViewSearchResult>`:多一个 `worker`(是不是在 Worker 里算的)与 `stale`(回来时数据已经改过、或又搜过一次,这一份不作数了) |
|
|
64
66
|
| `setCamera(camera)` | `camera: Partial<GisViewCamera>` 要改的相机字段 `{ x, y, scale }`(只写一个也行) | `this` |
|
|
65
67
|
| `getCamera()` | 无 | `GisViewCamera` |
|
|
68
|
+
| `stopCameraAnimation()` | 无 | `this`:停止当前相机飞行并停在当前帧;没有飞行时是空操作 |
|
|
69
|
+
| `getCameraAnimation()` | 无 | `GisViewCameraAnimation \| null`:`{ active, progress, duration, from, to }`,`progress` 是时间进度(0 ~ 1),没有动画时 `null` |
|
|
66
70
|
| `getZoomBaseScale()` | 无 | `number` 缩放联动的基准比例(`pointZoomScale` / `lineZoomScale` 共用那一份;缺省是首次适应视图时的 `camera.scale`) |
|
|
67
71
|
| `setZoomBaseScale(scale?)` | `scale?: number` 新的基准比例;不传取当下相机的比例(把现在这一屏当成「原样大小」) | `this` |
|
|
68
|
-
| `getBounds()` | 无 | `GisViewBounds
|
|
72
|
+
| `getBounds()` | 无 | `GisViewBounds`:每次返回独立副本;重复调用命中数据包围盒缓存,改点 / 改线 / 追加或拖拽后下一次读数自动重建 |
|
|
69
73
|
| `getViewport()` | 无 | `{ cssWidth, cssHeight, pixelRatio, displayScale }`:画布 CSS 尺寸、设备像素比与渲染分辨率比例 |
|
|
70
74
|
| `getViewRect()` | 无 | `GisViewBounds` |
|
|
71
75
|
| `isLayoutReady()` | 无 | `boolean` |
|
|
@@ -103,7 +107,8 @@
|
|
|
103
107
|
| `mapDataToGeoJson(data, options?)` | `data: { points?, lines?, channels? }` 要搬的数据;`options?: GisViewGeoMapperOptions` 同 `createGeoMapper` | `GisViewPoint[]; lines?: GisViewLine[]; channels?: GisViewChannel[] }>(data: T, options?: GisViewGeoMapperOptions): T \| null` |
|
|
104
108
|
| `getPointer()` | 无 | `GisViewPointerState` |
|
|
105
109
|
| `pick(x, y)` | `x` / `y` 画布 CSS 像素 | `GisViewPickResult \| null` |
|
|
106
|
-
| `pickAt(clientX, clientY)` | `clientX` / `clientY` 浏览器客户端坐标(`PointerEvent`
|
|
110
|
+
| `pickAt(clientX, clientY)` | `clientX` / `clientY` 浏览器客户端坐标(`PointerEvent` 原样给);命中通道回线时返回的 `group` 是当前组、`groups` 是整条通道的全部分组 | `GisViewPickAtResult \| null` |
|
|
111
|
+
| `selectAt(clientX, clientY)` | `clientX` / `clientY` 浏览器客户端坐标;按普通单击的命中与通道摊开规则立即选中,空白 / 聚合圆返回 `null`、不改选中,不派发 `onClick` / `onDblClick`。每次成功命中都发 `onSelect`,选中集实际变化时再发 `onSelectChange`(`cause: 'api'`,顺序 `onSelect → onSelectChange`);通道回线载荷带当前 `group` / 完整 `groups`,多通道看 `channelContexts`。已知 id 时优先 `selectFeatures` | `GisViewSelectPayload \| null` |
|
|
107
112
|
| `hoverAt(clientX, clientY)` | `clientX` / `clientY` 客户端坐标 | `GisViewHoverPayload \| null` |
|
|
108
113
|
| `clearHover()` | 无 | `null` |
|
|
109
114
|
| `getHover()` | 无 | `GisViewHoverPayload \| null` |
|
|
@@ -115,6 +120,7 @@
|
|
|
115
120
|
| `getDropPreview()` | 无 | `GisViewDropPreview \| null`:当前拖拽落点预览的读数,没有预览时 `null` |
|
|
116
121
|
| `getPoint(ref)` | `ref: GisViewFeatureRef` 节点定位:id、下标、`{ id }` / `{ index }` | `GisViewPointInfo \| null` |
|
|
117
122
|
| `getPointStyle(index)` | `index: number` 节点下标 | `GisViewPointStyleSnapshot \| null`:节点解析后的样式快照 `{ index, size, symbol, zIndex }`,`size` / `symbol` 取样式链(节点自身 > 规则 > `pointStyle`)定下来的那一份,含 `sizeScale`、不含悬停 / 选中样式与热力倍率;落点预览的读数与它同源 |
|
|
123
|
+
| `getLineStyle(index)` | `index: number` 线路在 `lines` 里的下标 | `GisViewLineStyleSnapshot \| null`:`{ index, width, color, opacity, zIndex }`;`width` 取样式链(线路自身 > 规则 > `lineStyle`)定下来的原始线宽,不含 `lineWidthScale` / 缩放联动 / 热力倍率,`color` 是解析后的 RGBA(0 ~ 1)。局部更新与线路窄样式快路径之后读到的都是最新值;下标不存在时 `null` |
|
|
118
124
|
| `getLine(ref)` | `ref` 线路定位(同上) | `GisViewLineInfo \| null` |
|
|
119
125
|
| `getChannel(ref)` | `ref` 通道定位:通道 id 或下标 | `GisViewChannelSnapshot \| null` |
|
|
120
126
|
| `listPoints(options?)` | `options?: GisViewListOptions`:`indexes`(只看这几个下标)、`inView: boolean`(只要当前视口里的)、`withScreen: boolean`(连屏幕坐标一起给)、`visibleOnly: boolean`(跳过隐藏的)、`limit: number`(最多几条) | `GisViewListedPoint[]` |
|
|
@@ -140,14 +146,14 @@
|
|
|
140
146
|
|
|
141
147
|
| 方法 | 参数(名字: 类型 = 默认值) | 返回 |
|
|
142
148
|
| --- | --- | --- |
|
|
143
|
-
| `selectFeatures(refs, options = {})` | `refs: GisViewSelectionRefs` 要选中的东西:节点 / 线路 id、id 数组、`{ type, index }`、通道写法(`{ type: 'channel', channelId \| channelIndex }`,可再给 `groupIds` / `lineIds` / `lineIndexes` 取子集)、分组写法(`{ type: 'group', channelId?, groupId }`,`groupId` 全局唯一时可省略通道)、拾取载荷 / 搜索命中,传 `null` / 空数组等于清空一份新的选中集。认不出来的条目逐条跳过;`options?: GisViewSelectOptions` 只有 `mode`:`'replace'` 缺省换一份、`'add'` 追加、`'toggle'` 在就取消 / 不在就选上、`'remove'` 去掉 | `this
|
|
149
|
+
| `selectFeatures(refs, options = {})` | `refs: GisViewSelectionRefs` 要选中的东西:节点 / 线路 id、id 数组、`{ type, index }`、通道写法(`{ type: 'channel', channelId \| channelIndex }`,可再给 `groupIds` / `lineIds` / `lineIndexes` 取子集)、分组写法(`{ type: 'group', channelId?, groupId }`,`groupId` 全局唯一时可省略通道)、拾取载荷 / 搜索命中,传 `null` / 空数组等于清空一份新的选中集。认不出来的条目逐条跳过;`options?: GisViewSelectOptions` 只有 `mode`:`'replace'` 缺省换一份、`'add'` 追加、`'toggle'` 在就取消 / 不在就选上、`'remove'` 去掉 | `this`(选中真的变了才按 `onSelect → onSelectChange` 发,`cause` 为 `api`;载荷带通道上下文) |
|
|
144
150
|
| `selectWaypoint(lineRef, pathIndex?, options = {})` | `lineRef` 线路 id、`lines` 下标或 `{ lineIndex, pathIndex }` / `{ lineId, pathIndex }` 对象;`pathIndex?: number` 必须是 `getWaypoints()` 给出的可编辑中间拐点下标(首尾端点不算,通常直接从读数里取);`options?: GisViewSelectOptions` 的 `mode` 决定所在线路怎么并进当前选中集(缺省 `replace`)。有效拐点会同时选中线路;线路不存在 / 下标非法 / 拐点不可见时只清拐点选中,不猜位置;传 `null` 也只清拐点,不改要素选中集 | `this` |
|
|
145
|
-
| `deselect(refs = null)` | `refs?: GisViewSelectionRefs` 要取消的东西(写法与 `selectFeatures` 一致:节点 / 通道 / 线路 / 混合一批);不传 / `null` 等于取消全部(= `selectFeatures(null)`),空数组是「没有目标」什么都不做 | `this` |
|
|
151
|
+
| `deselect(refs = null)` | `refs?: GisViewSelectionRefs` 要取消的东西(写法与 `selectFeatures` 一致:节点 / 通道 / 线路 / 混合一批);不传 / `null` 等于取消全部(= `selectFeatures(null)`),空数组是「没有目标」什么都不做 | `this`(选中真的变了才按 `onSelect → onSelectChange` 发,`cause` 为 `api`;清空时 `onSelect` 载荷为 `null`) |
|
|
146
152
|
| `getSelections()` | 无 | `GisViewPickResult[]` |
|
|
147
153
|
| `isSelected(ref)` | `ref: GisViewFeatureRef \| GisViewFeatureKey \| GisViewSelectionRefObject` 要素定位(id / 下标 / `{ id }` / `{ index }` / `{ type, index }` / `{ item }`) | `boolean` |
|
|
148
154
|
| `getSelectionData()` | 无 | `GisViewSelectionData`:`{ ids, count, pointCount, lineCount, channelCount, points, lines, channels, items, pointIndexes, lineIndexes, bounds }`(`bounds` 是世界包围盒,空选中时 `null`) |
|
|
149
155
|
| `getCascadeHighlight()` | 无 | `GisViewCascadeHighlightInfo \| null`:当前点击级联高亮的来源、范围与节点 / 线路读数 `{ origin, scope, points, lines, pointIndexes, lineIndexes }`,不改变选中集 |
|
|
150
|
-
| `selectInBox(box, options = {})` | `box: GisViewRect` **画布 CSS 像素**矩形 `{ x, y, width, height }`(与手动框选同一套坐标);`options?: GisViewSelectInBoxOptions`:`selectionKinds` 临时覆盖这一次保留哪几类(`'point'` / `'line'` / `'channel'`、数组、`'all'` / `true` / `false` / `null`,不写沿用全局 `selectionKinds`),`originalEvent` 原样带进 `onSelect`;第二段也兼容旧的 Event 写法。空框不会选中任何要素 | `this
|
|
156
|
+
| `selectInBox(box, options = {})` | `box: GisViewRect` **画布 CSS 像素**矩形 `{ x, y, width, height }`(与手动框选同一套坐标);`options?: GisViewSelectInBoxOptions`:`selectionKinds` 临时覆盖这一次保留哪几类(`'point'` / `'line'` / `'channel'`、数组、`'all'` / `true` / `false` / `null`,不写沿用全局 `selectionKinds`),`originalEvent` 原样带进 `onSelect`;第二段也兼容旧的 Event 写法。空框不会选中任何要素 | `this`(每次框选都发 `onSelect`,空框为 `null`;集合变化时再发 `onSelectChange`,顺序 `onSelect → onSelectChange`,`cause` 为 `boxSelect`) |
|
|
151
157
|
| `beginCopyPoint(options = {})` | `options: GisViewCopyStartOptions`:`id` / `index` / `item` 三选一给来源,都不给就进入「下一次点选节点」模式;`coordinate` / `screen` 是落点(缺省按 `copy.offset`);`follow` 让预览跟指针;`autoCommit` 覆盖 `copy.autoCommit`;`commit: true` 出预览即落地;`via` / `originalEvent` 原样带回。必须配置 `copy`(`enabled` 不为 `false`),否则返回 `null`。预览节点插在 `points` 末尾且不带 id,不会把绑定源 id 的线路 / 通道带过去 | `GisViewPointCopyPayload \| null`:`phase: 'start'` 的预览载荷;来源认不出来时返回 `null` |
|
|
152
158
|
| `commitPointCopy()` | 无:把当前复制预览落地,先发 `commit`、落地后发 `end`;`commit` 阶段载荷里的 `cancel()` 可以拦住这次复制 | `GisViewPointCopyPayload \| null`:成功的 `end` 载荷;没有预览或已被取消时为 `null` |
|
|
153
159
|
| `cancelPointCopy()` | 无:撤掉预览并补一条 `cancel`,数据与历史不动 | `GisViewPointCopyPayload \| null`:取消载荷;没有预览时为 `null` |
|
|
@@ -353,6 +359,8 @@
|
|
|
353
359
|
| `el` | `HTMLElement` | 组件插入的 `.gis-view` 根节点(`destroy()` 会移除它;容器本身不动) |
|
|
354
360
|
| `canvas` | `HTMLCanvasElement` | WebGL2 画布元素(导出 / 截图 / 自己叠加 DOM 时定位用它) |
|
|
355
361
|
| `gl` | `WebGL2RenderingContext \| null` | 引擎拿到的 WebGL2 上下文,创建失败时 `null`(这时 `onError` 会报一条) |
|
|
362
|
+
| `cameraAnimating` | `boolean` | 相机是否正在飞行(只读)。按钮据此在「飞向 / 停止」之间切换,不触发重绘 |
|
|
363
|
+
| `dataVersion` | `number` | 数据修订号(只读):每次数据变更 +1,`onDataChange` 载荷里是同一个数;只改全局配置 / 样式不推。外部缓存、Worker 请求与增量计算据它判断是否重算 |
|
|
356
364
|
|
|
357
365
|
---
|
|
358
366
|
|
|
@@ -181,6 +181,16 @@ view.focusOn({ type: 'group', groupId: 'global-group' }) // groupId 全局唯一
|
|
|
181
181
|
view.focusOn([120, 80], { center: true }) // 显式只居中(与缺省一致)
|
|
182
182
|
view.focusOn({ lngLat: [116.4, 39.9] })
|
|
183
183
|
|
|
184
|
+
// flyTo(target, options?):与 focusOn 同一套目标解析,平滑飞过去
|
|
185
|
+
view.flyTo(hit, { zoom: true, duration: 600, easing: 'ease-in-out' })
|
|
186
|
+
view.flyTo(['station-1', 'station-2'], { select: true, duration: 0 }) // 立即落位
|
|
187
|
+
view.flyTo({ center: [120, 80], scale: 12, easing: progress => progress * progress })
|
|
188
|
+
|
|
189
|
+
// flyToCamera(camera, options?):直接飞相机目标;没写的 center / scale 沿用当前相机
|
|
190
|
+
view.flyToCamera({ center: [120, 80], scale: 3 }, { duration: 600, easing: 'ease-out' })
|
|
191
|
+
view.flyToCamera({ scale: 5 }, { duration: 0 }) // 中心不动,比例立即落到 5
|
|
192
|
+
view.flyToCamera({ center: [116.4, 39.9] }) // 比例沿用当前相机
|
|
193
|
+
|
|
184
194
|
// search(input) / searchAsync(input):搜索(只返回结果、不动视角)
|
|
185
195
|
const hits = view.search('甲站')
|
|
186
196
|
const scoped = view.search({ keyword: '甲', scope: ['points', 'channels', 'groups'], limit: 10 })
|
|
@@ -194,6 +204,10 @@ view.setCamera({ scale: 10 })
|
|
|
194
204
|
view.setCamera({ x: 200, y: 120 })
|
|
195
205
|
const camera = view.getCamera()
|
|
196
206
|
|
|
207
|
+
// stopCameraAnimation() / getCameraAnimation():停止飞行 / 读当前飞行读数(没有动画时为 null)
|
|
208
|
+
const flight = view.getCameraAnimation() // { active, progress, duration, from, to }
|
|
209
|
+
view.stopCameraAnimation() // 停在当前帧,不补跳到终点
|
|
210
|
+
|
|
197
211
|
// getZoomBaseScale() / setZoomBaseScale(scale?):缩放联动的基准比例(配合 pointZoomScale / lineZoomScale)
|
|
198
212
|
const zoomBase = view.getZoomBaseScale() // 基准(缺省首次适应视图时的 camera.scale)
|
|
199
213
|
view.setZoomBaseScale() // 以当下这一屏为「原样大小」,之后放大缩小都从它算倍数
|
|
@@ -278,6 +292,7 @@ const pointer = view.getPointer()
|
|
|
278
292
|
// pick(x, y) / pickAt(clientX, clientY):按画布像素 / 客户端坐标拾取
|
|
279
293
|
const hit = view.pick(320, 200)
|
|
280
294
|
const hitAt = view.pickAt(event.clientX, event.clientY)
|
|
295
|
+
hitAt?.groups // 命中通道回线:同一通道的全部分组;hitAt.group 是当前这一组
|
|
281
296
|
|
|
282
297
|
// hoverAt(clientX, clientY) / clearHover() / getHover():程序化悬停 / 撤掉 / 读当前那一份
|
|
283
298
|
view.hoverAt(event.clientX, event.clientY)
|
|
@@ -308,6 +323,9 @@ const channelInfo = view.getChannel('c1')
|
|
|
308
323
|
// getPointStyle(index):节点解析后的样式快照(样式链定下来的那一份)
|
|
309
324
|
const pointStyle = view.getPointStyle(0)
|
|
310
325
|
|
|
326
|
+
// getLineStyle(index):线路解析后的样式快照(原始线宽 / RGBA / 透明度 / 层级)
|
|
327
|
+
const lineStyle = view.getLineStyle(0) // { index, width, color, opacity, zIndex } 或 null
|
|
328
|
+
|
|
311
329
|
// listPoints(options?) / listLines(options?):按条件列一批
|
|
312
330
|
const listedPoints = view.listPoints({ limit: 5, withScreen: true })
|
|
313
331
|
const listedLines = view.listLines({ inView: true, limit: 200 })
|
|
@@ -340,7 +358,7 @@ const state = view.getViewState()
|
|
|
340
358
|
#### 选中、复制与合并
|
|
341
359
|
|
|
342
360
|
```js
|
|
343
|
-
// selectFeatures(refs, options?):接口选中(与点击选中走同一份 state
|
|
361
|
+
// selectFeatures(refs, options?):接口选中(与点击选中走同一份 state;实际变化时按 onSelect → onSelectChange 发,载荷带通道上下文)
|
|
344
362
|
view.selectFeatures('a') // 选中一个
|
|
345
363
|
view.selectFeatures(['a', 'b']) // 一批
|
|
346
364
|
view.selectFeatures('a', { mode: 'add' }) // mode:replace(缺省)/ add / toggle / remove
|
|
@@ -350,6 +368,11 @@ view.selectFeatures({ type: 'group', channelId: 'c1', groupId: 'c1::2' })
|
|
|
350
368
|
view.selectFeatures({ type: 'group', groupId: 'global-group' }) // groupId 全局唯一时可省略通道
|
|
351
369
|
view.selectFeatures(null) // 清空(空数组同义)
|
|
352
370
|
|
|
371
|
+
// selectAt(clientX, clientY):每次命中发 onSelect,集合变化时再发 onSelectChange(不动指针、不派发 onClick);已知 id 直接用 selectFeatures
|
|
372
|
+
view.selectAt(event.clientX, event.clientY) // 节点 / 线路命中即选中;空白返回 null
|
|
373
|
+
view.updateOptions({ channelSelectMode: 'channel' })
|
|
374
|
+
view.selectAt(event.clientX, event.clientY) // 命中任一回线 = 选中整条通道
|
|
375
|
+
|
|
353
376
|
// selectWaypoint(lineRef, pathIndex? / { lineIndex | lineId, pathIndex }, options?):选中拐点并带选线路
|
|
354
377
|
view.selectWaypoint('l1', 2) // pathIndex 从 getWaypoints() 取
|
|
355
378
|
view.selectWaypoint({ lineIndex: 0, pathIndex: 1 }, { mode: 'add' })
|
|
@@ -1062,10 +1085,12 @@ offMod() // 注册时返回的注
|
|
|
1062
1085
|
#### 属性
|
|
1063
1086
|
|
|
1064
1087
|
```js
|
|
1065
|
-
// el / canvas / gl
|
|
1088
|
+
// el / canvas / gl / cameraAnimating / dataVersion:容器、画布、WebGL2 上下文、相机是否在飞行、数据修订号
|
|
1066
1089
|
const host = view.el
|
|
1067
1090
|
const canvas = view.canvas
|
|
1068
1091
|
const gl = view.gl
|
|
1092
|
+
const cameraAnimating = view.cameraAnimating
|
|
1093
|
+
const dataVersion = view.dataVersion // 数据变更 +1;只改全局样式 / 配置不变
|
|
1069
1094
|
```
|
|
1070
1095
|
|
|
1071
1096
|
---
|
|
@@ -13,6 +13,8 @@ const view = createGisView(el, { data, ... })
|
|
|
13
13
|
view.getPoint('station-1') // { index, item, x, y, radius, layerRank, … }
|
|
14
14
|
view.getLine(0) // { index, item, path, bounds, color, width, channelId?, … }
|
|
15
15
|
view.getChannel('ch-1') // { index, item, id, lineIndexes, skeleton, offsets, paths, context }
|
|
16
|
+
view.getPointStyle(0) // { index, size, symbol, zIndex }:样式链解析后的节点快照
|
|
17
|
+
view.getLineStyle(0) // { index, width, color, opacity, zIndex }:线路快照(原始线宽,不含整体倍率)
|
|
16
18
|
|
|
17
19
|
// 一批要素:按条件列,列表项自带你需要的几何
|
|
18
20
|
view.listPoints({ limit: 5, withScreen: true }) // 前 5 个可见节点 + 屏幕坐标
|
|
@@ -36,6 +38,8 @@ canvas.addEventListener('contextmenu', (event) => {
|
|
|
36
38
|
|
|
37
39
|
`listPoints()` 用的就是引擎画点那一份可见判定(旧版单独暴露的 `getVisiblePoints()` 并进了这一条):`radius` / 图层号 / 屏幕坐标这些几何读数都在它给的条目里。`focusOn()` 只移动相机、不改数据:缺省(以及显式 `{ center: true }`)只居中、比例不动,`{ zoom: true }` 才按目标范围适配,`{ select: true }` 顺带把这批选上,传一批目标(数组)就是整体居中。
|
|
38
40
|
|
|
41
|
+
`getPointStyle()` / `getLineStyle()` 给的是**当前解析结果**:节点看尺寸 / 图标 / 层级,线路看原始线宽 / RGBA / 透明度 / 层级;悬停 / 选中样式、整体倍率、热力倍率都不算在里面。逐条自己写死的字段优先于规则与全局样式,所以用它们对账「预览里多大、落地后多大」或者鹰眼图的小图尺寸时,不需要自己重跑样式链。
|
|
42
|
+
|
|
39
43
|
只要「屏幕里这一片」的节点时用 `forEachPointInView()`:它按数据顺序逐个回调 `(index, item, x, y, radius)`,不建中间数组,回调返回 `false` 可以提前结束 —— 放大看一小片时不会去碰整份数据(十万级数据下,面板读数、导出与排序用上面那两条,按视口画自己的东西用这一条):
|
|
40
44
|
|
|
41
45
|
```js
|
package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md
CHANGED
|
@@ -46,7 +46,7 @@ const stats = view.getStats() // clusterCount / clusterPointCount /
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
- **配置**:写 `true` 用缺省档(半径 60px、至少 2 点、不限缩放);`radius` 夹在 8 ~ 500 CSS 像素,`minPoints` 最小 2,`maxZoom: 0` 表示不限。`maxZoom` 以 `zoomBaseScale`(或首次适应视图比例)为基准,相机比例超过它之后 `active` 变 `false`,原始点逐点展开。
|
|
49
|
-
- **算法开销**:`cluster.js` 用固定网格把邻近点串成组,每个点只查相邻 9 格,单次建组按总量 O(n);成员用 `Int32Array`
|
|
49
|
+
- **算法开销**:`cluster.js` 用固定网格把邻近点串成组,每个点只查相邻 9 格,单次建组按总量 O(n);成员用 `Int32Array` 链表保存,稀疏场景不为每个单成员常驻组对象。平移不重算聚合;缩放只在聚合启用且当前档有成员要重算 / 收掉时标脏,关闭聚合或当前缩放档本来就不聚合时不会触发整份场景重排。数据、配置、选中集、拖动、合并与复制预览变化仍立即标脏。相机缩放引起的成员变化在运动期先沿用上一档,停稳后自动重算一次。
|
|
50
50
|
- **绘制与拾取**:聚合圆单独一批绘制,原始点批次过滤掉被收起的成员;标签、涟漪、节点装饰、框选、最近点查询与 `listPoints({ visibleOnly: true })` 都按同一份隐藏集合跳过内部点。`getClusters()` 为需要全量展示组的场景补上单成员,绘制与数量文字只遍历多成员圆。
|
|
51
51
|
- **隔离规则**:选中点、拖动成员、待确认合并的源 / 目标以及复制预览强制保持单成员,避免用户点不到正在操作的点;聚合开启时发散初始化与追加式补点暂停,保证成员关系按整份数据一次算清。
|
|
52
52
|
- **和 denseMode 的关系**:两者可以同时开。`denseMode` 管标签避让 / 截断与动画降级,`pointCluster` 管点本身的展示密度;只希望更快但仍要看每个符号时可关掉聚合,只保留密集档。十万点档在 `?demo=cluster` 可现场切 2 千 / 1.2 万 / 6 万 / 10 万,并观察聚合耗时与展示项数。
|
|
@@ -59,8 +59,10 @@ const stats = view.getStats() // clusterCount / clusterPointCount /
|
|
|
59
59
|
- 拾取与框选共用网格索引:点走按数据范围重建的均匀网格,线走随线段重建的 `createSegmentGrid`,每次交互只遍历光标附近的候选,耗时与总量基本脱钩,`getStats().pickMs` 是随手可读的实测值(10 万规模下热态约 0.2 ~ 0.3 ms / 次)。线段按格线步进登记进它真正穿过的格子(早期按包围盒填格,一条斜跨全图的线会占掉上万格、只好整条丢进「超长线」单,而十万条线里绝大多数都是这种,于是每次拾取都要重算十万条的距离);剩下的长线只记一条包围盒,查询时按盒子先筛一遍。10 万点 / 10 万线的合成数据里(随机配对、长线极密),500 次 `pick` 从 3530 ms 降到 164 ms(0.3 ms / 次)。
|
|
60
60
|
- 拖动时「哪些线跟着这个点走」按需建索引:`collectDragLinks` 从前每次都把全部线路的端点与组内成员逐个比一遍,10 万点 / 12 万线下这一步占总 CPU 的 27%,是拖动路径上唯一还按总量线性付费的循环。现在线路超过 `LINE_BIND_TOLERANCE_SCAN_LIMIT`(2048)条时改用一张线路归属表(按 `from` / `to` 的 id 与端点坐标建,整份换数据 / 增删线 / 改绑定才重建,只改坐标就地把那一条换掉),只对命中的候选逐字段判定 —— 十万级拖动降到 CPU 的 1% 以下,单条 `updatePoint`(改名这类不动几何的改动)55 → 3.3 ms(稳态 0.02 ms)。线路不多时保留原来的浮点容差扫描(行为与从前逐字不差);索引那条路只认 id 绑定与坐标精确相等 —— 这个量级坐标都是同一份来源算出来的,容差是给手写数据留的余量。
|
|
61
61
|
- 视口里的线路改走线段网格:线路标签排版这类「只看屏幕里这一片」的路径(`forEachLineInView`)原先要把每条线的包围盒过一遍,10 万级 + 标签的 CPU profile 里它占 47 ~ 58%。现在线路超过 `LINE_VIEW_GRID_MIN_LINES`(2048)条、且估计出来的候选线段只占总数的 1/32 以内(`LINE_VIEW_GRID_COST_SHARE`)时改查拾取用的那张线段网格,再逐条按包围盒确认;候选排回要素下标顺序,脏线与没有线段的退化线单独补一遍,访问顺序与判定口径与整份扫描逐行相同。网格脏着(正在拖线 / 改线)或线路不多时照旧整份扫描。
|
|
62
|
-
- 视口里的节点遍历也先查点网格:`forEachPointInView` 在节点超过 `POINT_VIEW_GRID_MIN_POINTS`(2048)、点网格不脏且估计候选不足总数的 1/32(`POINT_VIEW_GRID_COST_SHARE`)时,只查视口覆盖的格子、把候选排回数据顺序再逐个精确判定;网格脏着(刚改过数据 / 拖拽中)或视口几乎铺满时仍走整份扫描,避免为了省一趟遍历先付出建网格和排序的成本。`createPointGrid.estimate(..., stopAfter)` 与查询共用同一套格子口径,传 `stopAfter` 时一旦超过阈值立即返回,只判断候选是否过密时不必扫完视口里的全部格子;框选线路则复用已有的 `forEachLineInView`,不再为每条候选线复制路径 / 包围盒 / 颜色快照。
|
|
62
|
+
- 视口里的节点遍历也先查点网格:`forEachPointInView` 在节点超过 `POINT_VIEW_GRID_MIN_POINTS`(2048)、点网格不脏且估计候选不足总数的 1/32(`POINT_VIEW_GRID_COST_SHARE`)时,只查视口覆盖的格子、把候选排回数据顺序再逐个精确判定;网格脏着(刚改过数据 / 拖拽中)或视口几乎铺满时仍走整份扫描,避免为了省一趟遍历先付出建网格和排序的成本。`createPointGrid.estimate(..., stopAfter)` 与查询共用同一套格子口径,传 `stopAfter` 时一旦超过阈值立即返回,只判断候选是否过密时不必扫完视口里的全部格子;框选线路则复用已有的 `forEachLineInView`,不再为每条候选线复制路径 / 包围盒 / 颜色快照。
|
|
63
|
+
- 数据包围盒读数改为缓存:整份解析本来已经顺路算出点与线的范围,`getBounds()` 直接接住这一份;定向改点 / 改线、拖拽、追加要素与复制预览只把缓存标脏,下一次真被问到时才用点坐标和每条线的 `line.bounds` 重建,不再逐顶点重扫。`getBounds()` 每次返回独立副本,调用方就地修改不会污染下一次读数;追加到原范围之外、拖出边界这类增量也会立即失效。10 万点 + 10 万线(每条 10 顶点)实测连续 10 次 `getBounds()`:优化前 **5.2 ~ 6.6 ms / 次**,优化后 **0.00 ms / 次**;改一个点后缓存重建一次 **3.6 ms**,读数正确。
|
|
63
64
|
- 悬停、选中、拖拽只做增量更新:选中集合用点 / 线两张下标 `Set` 维护,样式变化按要素下标重新解析,顶点与实例数据就地改写后按「实例槽位」上传,不重建整批缓冲。
|
|
65
|
+
- 选中事件的通道分组读数一次成型:同一条通道的 `groups` 只构造一份,`onSelect` / `onSelectChange` 与载荷顶层共享对象引用,不深拷贝;单通道不再额外生成 `channelContexts`,`items[]` 里的回线只保留 `channelId` / `channelIndex` / `groupId` / `groupIndex` 标量,不逐条重复挂 `group` / `groups`。单通道也不会先构造随后丢弃的 `lineIndexes` / `lineIds`,这两份只在多通道 `channelContexts` 里按选中顺序补一次。通道重建时顺手按 `channelIndex` 收好分组状态与组序号,后续悬停 / 选中查同通道分组和组号都是 O(1),不再每次筛整张通道表;构造一条通道的全部分组读数从 O(组数 × 通道数) 降为 O(组数)。`selectFeatures` 对同内容、同顺序的重设会在引擎刷新和 `describeSelection` 之前直接返回;事件本身只在选中操作 / 选中集变化时发生,不进入逐帧渲染路径。
|
|
64
66
|
- 状态样式就地更新:悬停 / 选中样式只改颜色、大小、描边时按实例槽位覆盖,不重建整批;符号换成 / 换出贴图、阴影条数变化这类批次结构变化才回退整批重排。哪些要素当前真的按状态样式画过另有一份记账(`stateStyledSignatures`),整体重建后立刻重算,取消选中时按它精确还原本体样式,不会留下残留。
|
|
65
67
|
- 动效只维护真正的动画线路:`lineEffectRuns` 让每帧的动点循环只遍历配了 `effect` 的线路,路径就地变化时只重算这一条的总长与包围盒;普通线路再多,也不会进入动画的逐帧扫描。
|
|
66
68
|
- 热力图的要素采样与密度烘焙分开缓存:坐标 / 权重 / 显隐变化才重新采集,核参数 / 色带变化复用采样重烤,`density.opacity` 与 `layer` 只改绘制;拖动期间延迟到停手统一补烤。
|
|
@@ -70,7 +72,8 @@ const stats = view.getStats() // clusterCount / clusterPointCount /
|
|
|
70
72
|
- 配置变更走一次合并:`updateOptions({ points, lines, channels, background, ... })` 只在需要时重建场景;demo 里的通道宽度、内收距离用 `v-model.lazy` 提交,避免拖动滑块时每帧重排。
|
|
71
73
|
- 配置变更走一次快速通道:`updateOptions` 先按 `pickEngineOptions` 的键表挑一遍「引擎真的会收到的那几项」——纯标签 / 提示 / 根样式(`label` / `lineLabel` / `tooltip` / `minimap` 里的样式与规则 / `ariaLabel`)一个字节都不发给引擎,只把覆盖层重刷一遍;小图配置变更另外作废组件这一层的小图数据缓存并重画(那份数据已经按 `levels` / `cascade` 过滤过,引擎不变它也不会自己更新);光标、网格、交互开关、快捷键、回调、尺寸倍率这些「只影响怎么画」的字段只发这几项、引擎标一帧脏;状态样式(`pointHoverStyle` / `pointSelectStyle` / `lineHoverStyle` …)与 `defaultHighlight` 走「只重解析在悬停 / 选中里的那几个要素」那条路,不必整份重排。实测(10 万点 / 10 万线,`updateOptions` 连调两次取第二次):空补丁、`label`、`lineLabel`、`tooltip`、`ariaLabel`、`statsInterval`、`initialView`、`pointSelectStyle`、`defaultHighlight`、`selectionBoxStyle` 全部从 82 ~ 120 ms 降到 0 ms,同一批用例在「快速通道」与「强制整份重排」两条路上的截图逐像素相同(8 个用例的 mean / max / 变化比例全为 0)。改配置频繁的面板(读数间隔、光标、标签文案、状态样式)现在可以放心连着拨;`pointStyle` / `lineStyle` / `pointRules` / `lineRules` 这类样式补丁这一轮也进了快路径(109 ~ 116 ms → 40 ~ 55 ms),见下一条。
|
|
72
74
|
- 样式类配置也走这条路:`pointStyle` / `lineStyle` / `pointRules` / `lineRules` / `heatmap` 这些「只换外观、不动几何」的补丁,引擎按 `STYLE_PATCH_KEYS` 认出这一类,逐要素按新配置重解析后直接覆盖到实例槽位上,不再重建整份场景 —— 数据包围盒、背景、通道骨架与装饰锚点都跟样式无关,一律不重算(只换样式时装饰锚点整趟不重定位,线多、装饰多时这一笔最大);只有「结构真的变了」才回退整份重排(符号换成 / 换出贴图、阴影条数变化、`zIndex` 层级变化、虚线图案变化)。面板上的常见写法是「基准样式 + 悬停 / 选中样式 + 尺寸倍率」一起下发,判据因此放宽成「至少一项样式键,其余是尺寸倍率 / 画法开关 / 状态样式 / 回调」;引擎另外记一份「这一轮重解析用的输入」,同一份配置原样再发一次只标一帧脏,只动一半时另一半的十万个要素一个都不碰。实测 10 万点 / 10 万线:`pointStyle` 从 109 ~ 116 ms 降到 40 ~ 55 ms(本轮再降到 16.8 ms),原样重发 48.2 → **0.1 ms**、混合补丁 120.6 → 35 ms、换热力图色带 241.9 → 90.1 ms;两条路画出来的截图逐像素相同(9 个用例的 mean / max / 变化比例全为 0),快照逐字段一致另有 `selftest-perf` 的 10 条断言兜着;`heatmap` 配置是按引用存的,只要补丁里出现它就按「变过」记、照旧按当前数据重算一遍(自动值域跟着动)。
|
|
73
|
-
- 样式窄路再收窄一层(尺寸链 / 配色链):面板上拖「节点大小」滑块、换调色板、拖透明度下发的是「只改了 `pointStyle` 里某一类字段」的补丁,引擎按一张字段表(`size` / `sizeScale` / `color` / `strokeColor` / `strokeWidth` / `opacity`)认出尺寸链与配色链,逐要素只重算真的换了的那条链,就地写回上一轮的解析结果(规则匹配、热力取值、阴影与效果归一化、那份 15 个字段的结果对象一概不跑);逐点自己写了这一项(或者状态样式盖住了这一层)的要素算出来与上一轮相同,**这一条直接跳过,连实例槽位都不重写** —— 「一百万个点各自带着颜色、只换基准色」这类场景因此几乎只剩一趟判断。两条链在一次补丁里都变也认(先配色后尺寸,中间那一份不记进预热账本)。实测(100 万点,`docs/steps/bench-scale.html` 的 `styleColor` / `styleToggle`):只换基准色 **首轮 242.8 → 137.8 ms、稳态 156.1 ~ 203.1 → 91.1 ~ 127.8 ms**;只换基准尺寸(每条数据自己写了 `size`、一个要素都不用重算)**95.6 ~ 117.1 → 17.1 ~ 21.2 ms**(过程见实现原理 15.54)。贴图符号(SVG / 图片)整份退回原来的路(尺寸会换烘焙档位、颜色可能进烘焙键),画面与整份重建逐字段一致由 `selftest-perf` 的断言兜着(逐要素重解析之后不再回落整份重排:换配色的贴图就地把实例搬进新配方那一组,见《实现原理》15.91 / 16.42
|
|
75
|
+
- 样式窄路再收窄一层(尺寸链 / 配色链):面板上拖「节点大小」滑块、换调色板、拖透明度下发的是「只改了 `pointStyle` 里某一类字段」的补丁,引擎按一张字段表(`size` / `sizeScale` / `color` / `strokeColor` / `strokeWidth` / `opacity`)认出尺寸链与配色链,逐要素只重算真的换了的那条链,就地写回上一轮的解析结果(规则匹配、热力取值、阴影与效果归一化、那份 15 个字段的结果对象一概不跑);逐点自己写了这一项(或者状态样式盖住了这一层)的要素算出来与上一轮相同,**这一条直接跳过,连实例槽位都不重写** —— 「一百万个点各自带着颜色、只换基准色」这类场景因此几乎只剩一趟判断。两条链在一次补丁里都变也认(先配色后尺寸,中间那一份不记进预热账本)。实测(100 万点,`docs/steps/bench-scale.html` 的 `styleColor` / `styleToggle`):只换基准色 **首轮 242.8 → 137.8 ms、稳态 156.1 ~ 203.1 → 91.1 ~ 127.8 ms**;只换基准尺寸(每条数据自己写了 `size`、一个要素都不用重算)**95.6 ~ 117.1 → 17.1 ~ 21.2 ms**(过程见实现原理 15.54)。贴图符号(SVG / 图片)整份退回原来的路(尺寸会换烘焙档位、颜色可能进烘焙键),画面与整份重建逐字段一致由 `selftest-perf` 的断言兜着(逐要素重解析之后不再回落整份重排:换配色的贴图就地把实例搬进新配方那一组,见《实现原理》15.91 / 16.42)。
|
|
76
|
+
- 线路侧的同一条窄路:`updateLineStyle` / `updateOptions({ lineStyle })` 只改 `width` / `color` / `opacity` 时不重跑线路规则、热力、虚线、阴影、动点与方向箭头,逐条只重算「基准宽 / 半宽 / 颜色 / 透明度」再按槽位补实例;逐线自己写死的字段仍然优先,存在线路规则或热力图时自动回退整份解析。1000 点 + 50000 线连续 5 次交替:换色稳态 **10.1 ~ 11.7 ms → 6.1 ~ 7.4 ms**、换宽 **9.2 ~ 10.4 ms → 5.8 ~ 6.4 ms**(均约 40%),首轮 18.9 / 17.3 ms;profile 从 `applyResolvedStyles` + `refreshLine` 转到 `applyLineNarrowFast`、`patchLineSlotsInner` 与 `resolveLinePaintInto`,两边共用同一份取值链。读最新解析结果用 `getLineStyle(index)`;这条快路不动数据,`dataVersion` 不变。
|
|
74
77
|
- 筛选放进 Web Worker:点 + 线总数超过 `filterWorker.threshold`(默认 20000)且条件里没有函数时,判定在 Worker 里跑,主线程只应用隐藏下标;字段投影带缓存(数据与条件字段没变就复用,单条改动只补那一条),条件变化只传条件本身,10 万级数据下连续切换条件也不会卡住交互。
|
|
75
78
|
- 搜索带文本缓存:每个要素的「所有可搜索字段的小写拼接串」按对象缓存在 `WeakMap` 里(`createSearchCache`),先用它排除肯定不命中的要素,可能命中的那几个才摊平字段算出「命中哪个字段、原文是什么」,载荷与逐字扫描时完全一致。10 万点 / 10 万线默认全字段搜索:改造前每次 30 ms 上下,现在第一次约 80 ms(这一遍顺带把 10 万个要素的拼接串建进缓存)、之后 15 ms 左右,不命中的关键词从 30 ms 降到 9 ms 左右 —— 换来的是「边输边搜」这种连搜场景。
|
|
76
79
|
- 批量规则与标签都是「按需字段」:规则只读条件里出现的字段;标签只对当前视口内的可见点建 DOM,节点出入视口时 DOM 回收进池子复用。缺省(`labelOptimize: false`)是「强制显示」档:视口里该出现的标签一条不少,密集场景画多少条由数据自己决定 —— 十万级数据要显式写 `labelOptimize: true` 打开优化档,按 `maxCount` 截断、按候选上限 `label.scanLimit`(缺省按 `maxCount × 8` 估算、不低于 256,`0` 表示不限)收候选:为了几十个标签把整屏节点都写成 DOM 再量一遍才是真正的开销,扫够视口内的节点就停。实测这一项让「开标签 vs 关标签」的帧率差从 13 倍降到 1.34 倍(见下节与「标签显示优化开关」的两档对照)。
|
|
@@ -79,6 +82,7 @@ const stats = view.getStats() // clusterCount / clusterPointCount /
|
|
|
79
82
|
- 线路补丁「散到一定程度就整批重写」:回线在缓冲里是一条通道一段、段与段之间还夹着别的线路,一次要改的线一多,攒批就退化成几百次小上传。现在先用 `estimateLinePatchRuns` 估一次这次改动会切成多少段(按「槽位跨度 - 改动占用的槽位 + 1」,扫一遍、不排序),段数超过 `LINE_PATCH_REWRITE_RUNS`(48)且改动条数占到全部线路的 1/4 以上时,直接把线路 / 阴影 / 圆头三张缓冲按槽位顺序整份重写、各传一次(`rewriteLineSlots` 会逐条校验槽位表,对不上就退回攒批、由它转整份重排)。实测:2164 点 / 3669 线的真实数据,拖「节点放大」滑块 1 秒,`bufferSubData` 从 30320 次降到 80 次;同一状态下「整批重写」与「逐段攒批」的截图逐像素一致,每步的同步耗时仍是 5 ~ 8 ms。
|
|
80
83
|
- 圆头批次在拖动期间「欠账」:`join: 'round'` 时每个顶点一个圆头,行数比线段还多(10 万条两点折线 = 20 万行 / 12 MB)。一次拖动要改的行数超过 `CAP_DEFER_ROWS`(8192)时这一轮不写、整批圆头先不画,松手后按 `CAP_SYNC_SLICE`(8192 条线)一批分摊到后续几帧补齐 —— 拖动期间圆头一个字节都不传,松手也不再是一帧的卡顿。10 万点 / 10 万线整屏框选后拖动,每帧补丁从 1391 ms 降到 339 ms,其中圆头那部分(8 帧合计 9.8 s)在拖动期间归零。
|
|
81
84
|
- 交互期间自动切轻量渲染:按住拖动 / 滚轮缩放时跳过阴影、强调环、贴图高亮与高亮重算,停手 160 ms 后补一帧全质量;带大选中集合时整帧耗时约减半,画面里的节点与线路位置仍然实时跟随。
|
|
85
|
+
- 程序化相机运动复用同一条轻量渲染路径:`zoomBy` / `setCamera` / `centerOn` / `flyToCamera` 等 API 改相机时,运动帧同样使用细线快路径与非 MSAA 后备缓冲;每次真实位置变化重新计时 160 ms,停稳后自动补一张全质量帧,并刷新通道宽度与恢复符号预烤。同一相机位置重复 `setCamera` 不会续期,避免外部定时同步视角把画面永久钉在轻量档。关闭聚合后连续缩放不再空跑 `layoutScene()` 和整份点实例上传;10 万点 / 5 万线单次实测缩放 p50 从 67.9 ms 降到 22.5 ms,p95 从 76.2 ms 降到 25.1 ms。
|
|
82
86
|
- 标签图层在平移 / 缩放 / 拖点期间跟着相机整体变换,不重排 DOM:标签位置只跟相机与节点坐标有关,整层一次 `translate3d` / `scale` 就能和地图对齐(拖点只挪动过的那几个标签),停手 180 ms 后再补一次真实排版(回收移出视口的、补上新进来的)。十万级数据下这一项把拖动平移从 17.9 FPS 提到 57.9、滚轮缩放从 13.5 提到 57.9、拖动节点从 20.1 提到 55.0,都是「开标签 ≈ 关标签」的水平。
|
|
83
87
|
- 框选抬起时只做必要的工作:候选下标先按数值排好序再逐个造结果,状态差值的去重键用数值(不是字符串),差值用的三个 `Set` 循环复用,圆 / 方符号的解析结果有一层不拼字符串的热缓存。整屏框住约 9.9 万个节点的耗时从 97 ms 降到 52 ms(50% 的框 28 ms、25% 的框 5 ms 上下)。
|
|
84
88
|
- 可见点缓存只在数据或相机真的变化时重建:拖点换了坐标就就地作废,只有整体倍率(`pointSizeScale` 这类)变化时就地按比例缩放缓存的 `radius`,不为一次缩放重建十万个对象。
|
|
@@ -99,6 +103,8 @@ const stats = view.getStats() // clusterCount / clusterPointCount /
|
|
|
99
103
|
- 追加数据只补这几条、标签没变就不排、搜索搬进 Worker:三件都还是同一条思路 —— **把「整份」换成「这一份」、把主线程上的活挪走**。① **追加式补点**:引擎认得出「数据末尾就多了这几条」(`appendPoints`),只解析这几条、只往现成的实例批次里补这几条 —— 实例缓冲按容量记账(够就 `bufferSubData` 只传新段)、形状符号插在圆点那一段末尾并把后面的背景点 / 装饰 / 抬层点整体后移、贴图符号追加到现成那一组、包围盒只扩不缩、背景与背景文字跟着新范围重算;`addPoint` / `addPoints` 因此从「每次整份重建」变成增量(10 万点下逐个 `addPoint` 16.4 → 1.0 ms,第一次 4.6 ms 是整份实例缓冲重传一次),场景结构做不到等价时(新点不在最上面那一层、贴图没有现成的组、通道或线路按 id 认领了它)自己回落整份重建,画出来与整份重建**逐像素相同**(分层 / 贴图 / 阴影三种场景都验过)。② **标签层**多了一道「输入指纹」判据:改动的字段与标签画出来的东西无关时整层重排整个跳过(`overlay.labelInputKey` 把一条要素里影响它那份标签的字段拼成指纹,`updatePoint` / `updateLine` / 批量补丁三处都接),10 万点 + 标签改一条 0.75 → 0.099 ms,改 `name` 这种真会换标签的仍是 0.956 ms。③ **搜索**搬进了 Worker(`search.worker.js` + `search-runner.js` + `view.searchAsync`):数据交给 Worker 一次(改过的按条补、结构变了重新交一份),之后每次搜索只发关键字,命中只回下标与字段、要素与包围盒由主线程按下标补回来,结果按「请求序号 + 数据版本」作废(回来晚了的那一份带着 `stale: true` 交回去,视角不会跟着它动),环境不支持 Worker / 数据量不到 `searchWorker.threshold`(默认 20000)/ Worker 报错时同步回落。10 万点 / 10 万线连着搜 6 次占主线程 43.3 → 0.1 ms(第一次含把数据交给 Worker 的 42.7 ms)。④ 另外 `refresh({ reflow: false })` 只重画一帧、跳过整份重解析(10 万点 / 1 万线 41.0 → 0.1 ms),给「数据没变、就是想重画一遍」的场合一个明确的口子。
|
|
100
104
|
- 标签尺寸按「内容签名」缓存、避让判定不再读布局:整层重排从前要为每条候选 `getBoundingClientRect()` 一次(每读一次就是一次强制重排),根因是 `parseLabelTransform` 的正则从来没匹配上(`0px?` 的写法)—— 量好的盒子每次都被 `writeLabelTransform` 作废。现在:正则修对、解析结果缓存在 `state.placement` 上,尺寸按「画上去的那一份签名」(kind / class / 文字 / 内联样式 / 字体代号)缓存(LRU 8192,与节点身份解耦)、位置从节点自己写下的 `transform` 里解析,避让判定只剩算术,只有签名第一次出现才真量一次;一批里没量过的节点先全部挂进 DOM 再一趟读完(写读分离)。实测 10 万点 / 10 万线、54 条标签下单次定向更新 73.8 → 3.5 ms、建场景 453 → 257 ms;重标签场景(1000 条标签 / 8000 候选)500 ms 上下 → 25 ~ 45 ms。
|
|
101
105
|
- MSAA 与交互期细线:决定十万级交互帧率的是**光栅化的总覆盖量 × MSAA 倍数**,不是条数 —— 微基准(898×518 画布、十二万段、同一份实例数据、真 GPU)里每条线展开成四边形是 192 ms/帧、换成 `gl.LINES` 是 112 ms/帧,关掉 MSAA 之后分别是 38 / 23 ms,而 JS + 命令提交只要 0.01 ms(空页面 7.2 ms、只 clear 7.0 ms)。于是 `antialias` 进选项(软件光栅自动关),交互期新增细线快路径 `interactionThinLines`(线宽 ≤ 3px 且无虚线时改走 `gl.LINES`,停手回到四边形展开;两种画法共用同一张实例缓冲,所以位置不跳),`getStats()` 多出 `antialias` / `software` / `thinLines` 三个读数。实测:同一页交替开关 6 轮(两次独立运行)交互期 p50 194 ~ 208 → 132 ~ 139 ms、同一 2.2 s 窗口帧数 13 → 19 ~ 24;同样 10 万点换成网格邻居线(19.9 万条、线短不叠)拖动 p50 只要 7 ms。再往后一轮把这条账的另一半收掉:交互期与动画期干脆不带 MSAA 画 —— `interactionNoMsaa`(缺省开)让拖动 / 缩放与动画在跑的那些帧先画进一张不带 MSAA 的后备贴图、帧末整幅贴回,停手 / 停帧后那一帧照旧走带 MSAA 的画布(静止画面逐像素不变);真机实测交互期单帧 133 → 36.6 ms、拖动 p50 130 → 16 ~ 22 ms、缩放 p50 239.5 → 16.7 ms、平移 p95 129 → 17 ms、动画帧 212 → 53 ms/帧(只开涟漪 187 → 48、只开线路动点 199 → 47),读数看 `getStats().noMsaa`。
|
|
106
|
+
- **运动期保持全分辨率**:`interactionNoMsaa` 的非 MSAA 后备缓冲只换抗锯齿与细线画法,不降低 viewport / 后备存储尺寸。相机拖动、滚轮、`zoomBy` / `setCamera` / 飞行与尺寸倍率变化的帧都按画布实际像素绘制,整幅贴回保持 1:1,停手 160 ms 后恢复 MSAA 静态帧。上一轮曾按整帧间隔把运动帧降到 **0.65× / 0.5×** 再线性放大贴回,实测会让缩放 / 移动中的画面短暂模糊,现已整体移除;与之配套的 `drawWidth` / `drawHeight` / `framePixelRatio`、`u_offsetScale` 和 UV 裁剪也一并删除。尺寸倍率连续变化仍复用 160 ms 的 `scaleMotion` 窗口,暂停符号贴图重烤与预烤,停手后再对齐当前尺寸档;这部分只省重烤,不改变当前帧分辨率。最终全分辨率基准(1280×800,无头 Chrome + ANGLE / AMD D3D11,`quality: 'high'`,三次运行取中位):10 万点 / 5 万线缩放 p50 **23.2 ms**、p95 **28.6 ms**,平移 **16.7 / 18.2 ms**;10 万点 / 10 万线缩放 **42.6 / 46.3 ms**,平移 **16.7 / 24.5 ms**。`selftest-perf.html` 另有回归断言记录每次 `gl.viewport`,人为拉长运动帧间隔时也必须始终等于画布全尺寸。
|
|
107
|
+
- **片元管线专用化(为什么此前「感觉没提升多少」)**:之前的优化主要落在相机排帧、聚合脏标记、整份重排与符号预烤上;10 万线交互的 CPU profile 约 **98.6% idle**、`render` 自身约 **0.1%**,说明主线程不是瓶颈。真正剩余的是每条可见线在 GPU 上重复执行虚线取模 / `smoothstep` / 光晕分支的填充成本。`shaders.js` 现在给交互期 `gl.LINES` 一条只输出颜色的 `THIN_LINE_FRAGMENT_SHADER`,并给「全实线、无光晕」的四边形批次一条 `SOLID_LINE_FRAGMENT_SHADER`:跳过虚线取模、`smoothstep` 与光晕分支,只保留颜色与(非 MSAA 帧的)边缘覆盖率;有虚线或热力光晕的批次仍走通用片元。`engine.js` 的 `drawLineBatch` 按批选择,`refreshLineThinLedger` 同时给出是否全实线;线宽、端点圆头、全分辨率与停稳后的 MSAA 静态帧都不变。实测(1280×800,无头 Chrome + ANGLE / AMD D3D11,`quality: 'high'`):10 万点 / 5 万线缩放 p50 **23.2 → 16.7 ms**、p95 **28.6 → 16.8 ~ 16.9 ms**;10 万点 / 10 万线缩放三次中位 p50 **42.6 → 18.0 ms**、p95 **46.3 → 21.4 ms**。16 ~ 18 ms 已接近 60Hz 帧节拍,剩余成本仍是 GPU 填充率;细线快路径的实际门槛是**最大半宽 ≤ 0.6 设备像素且无虚线**(dpr = 1 时约 1.2px 线宽),2px 实线不会走 `gl.LINES`,但轻量四边形片元仍把同一 10 万线场景从约 **82.4 → 63.9 ms**。要继续明显提速,方向是减少可见线段数或屏幕覆盖面积,而不是继续抠 JS。
|
|
102
108
|
|
|
103
109
|
- 框选提交(`pointerup`)只做必要的工作:选中集每次同步整份换新,抬层集合因此只比引用(没有拖动 / 合并这类额外抬层时直接引用选中集本身,空集合三处共用一份),撤销选中与「同内容再框一次」不再逐个搬 / 逐个比 3.4 万个下标;组件层的选中集读数(id 表、键集合、点 / 线 / 通道条数)在一趟循环里收好并按数组引用缓存,`selectionInfo()` / `selectChanged()` / `selectChange` 载荷直接取现成的;选中集的存活校验走 `isKeyAlive` 的键查表,不再为每个键造对象、投影一次;抬层欠下的整份重排记在 `raiseLayoutPending` 上、由 `flushRaiseLayout` 在下一帧渲染之前补,`pointerup` 回调里不再同步排十万级场景。实测(10 万点 / 10 万线):框住 3.4 万个节点的 `pointerup` 48.7 → 15.7 ms(框小一些的几轮 9.5 / 6.5 / 6.8 ms),提交后第一帧 19.5 ~ 24.2 → 15.6 / 6.7 ~ 9.5 ms。
|
|
104
110
|
- 整份重排(`layoutScene`)也少扫几趟十万条:点 glyph 从对象池里复用(逐字段就地改写,不再每轮造十万个对象)、圆点实例用 `writeCircleGlyph` 直写进 `prepareInstances` 的批次数组(不再逐条造 `circleFromGlyph` 的中间对象);没有线路装饰 / 没有跑动动画 / 没有节点阴影 / 没有逐要素状态样式时,各自的十万级空扫直接跳过(`hasLineDecorations` / `hasLineEffects` / `hasPointShadows`,节点侧另有 `hasPointStateStyles()`);细线快路径的账本只在「线宽或虚线开关真的变了」时才标脏;`zIndex` 分层只有一层(绝大多数场景)时不再重铺点顺序。同一份 10 万级的 CPU profile 里 `syncRaiseSets` 6.2 → 0 ms、`buildLineEffectGlyphs` 3.69 → 0 ms、`buildLineDecorationGlyphs` 1.66 → 0.62 ms、`thinLineBatchEligible` 2.2 → 0 ms。
|
|
@@ -139,7 +139,7 @@
|
|
|
139
139
|
配 `quality: 'auto'`:在「全开 → 关光晕 → 关装饰 → 0.75 倍分辨率」之间升降(掉档快、升档慢),判定看两路读数里更差的那一路 —— 渲染耗时(`getQuality().frameMs`)与**整帧间隔**(`getQuality().gapMs`,只在交互 / 动画连续画帧时量),预算与分辨率下限用 `{ mode: 'auto', budgetMs, minScale }` 调;`view.getQuality()` 随时能读当前档位,`view.on('qualityChange', …)` 能拿到每次升降,见「画质」。
|
|
140
140
|
|
|
141
141
|
**十万级数据在无头 / 软件光栅 / 低端显卡上为什么还是卡?卡在哪一半?**
|
|
142
|
-
先分清卡的是哪一半:`getStats().frameMs` 量的是引擎自己那一趟(算变换、攒实例、写缓冲、发绘制),十万级下它常年 0.1 ~ 0.8 ms;真正花时间的是**填充率** —— 十万个节点加十几万条线铺满一屏,光栅化与浏览器合成都由 GPU(没有 GPU 时是软件光栅)扛,这一半不在 `frameMs` 里。于是按顺序看四件事:① **总覆盖量**(线的屏幕总长 × 宽度 × 条数,再算上互相重叠)—— 同样 10 万点,网格邻居线(19.9 万条、线短不叠)拖动 p50 只要 7 ms,而「每 5 个点连一条到 0 号点」的长线在圆心附近叠十几层,p50 就是 194 ~ 208 ms;② **MSAA** —— 同一份几何实测 MSAA 4× 是关掉时的近 5 倍(`antialias: false`,软件光栅下引擎已经自动关),读数看 `getStats().antialias`;交互期与动画期还有更省事的一档 `interactionNoMsaa`(缺省开:拖动 / 缩放与动画在跑的那些帧画进不带 MSAA 的后备贴图、收尾整幅贴回,停手 / 停帧后照旧走 MSAA)—— 十万级实测交互期单帧 133 → 36.6 ms、拖动 p50 130 → 16 ~ 22 ms、动画帧 212 → 53 ms,读数看 `getStats().noMsaa`;③ 交互期默认走**细线快路径**(`interactionThinLines
|
|
142
|
+
先分清卡的是哪一半:`getStats().frameMs` 量的是引擎自己那一趟(算变换、攒实例、写缓冲、发绘制),十万级下它常年 0.1 ~ 0.8 ms;真正花时间的是**填充率** —— 十万个节点加十几万条线铺满一屏,光栅化与浏览器合成都由 GPU(没有 GPU 时是软件光栅)扛,这一半不在 `frameMs` 里。于是按顺序看四件事:① **总覆盖量**(线的屏幕总长 × 宽度 × 条数,再算上互相重叠)—— 同样 10 万点,网格邻居线(19.9 万条、线短不叠)拖动 p50 只要 7 ms,而「每 5 个点连一条到 0 号点」的长线在圆心附近叠十几层,p50 就是 194 ~ 208 ms;② **MSAA** —— 同一份几何实测 MSAA 4× 是关掉时的近 5 倍(`antialias: false`,软件光栅下引擎已经自动关),读数看 `getStats().antialias`;交互期与动画期还有更省事的一档 `interactionNoMsaa`(缺省开:拖动 / 缩放与动画在跑的那些帧画进不带 MSAA 的后备贴图、收尾整幅贴回,停手 / 停帧后照旧走 MSAA)—— 十万级实测交互期单帧 133 → 36.6 ms、拖动 p50 130 → 16 ~ 22 ms、动画帧 212 → 53 ms,读数看 `getStats().noMsaa`;③ 交互期默认走**细线快路径**(`interactionThinLines`):最大半宽 ≤ 0.6 设备像素(dpr = 1 时约 1.2px 线宽)且没有虚线时,拖动 / 缩放期间改用 `gl.LINES` 画,实测 p50 194 ~ 208 ms → 132 ~ 139 ms、同一窗口帧数 13 → 19 ~ 24,读数看 `getStats().thinLines`;2px 实线不会走这一档,但全实线四边形另有轻量片元;④ 画质档的杠杆是**分辨率**,`fast` 档按 0.75× 渲染(画布像素少一半),同一画面实测 156 → 92 ms/帧,自动档还看整帧间隔(连续画帧时掉到 40fps 以下就逐档往下掉);高分屏 / 全屏下按 `maxCanvasPixels`(缺省 400 万像素预算)压渲染倍率、或把窗口开小,都是直接减填充量。见「画质」与「大数据量」。
|
|
143
143
|
|
|
144
144
|
**一次改很多要素,回调被刷屏、历史被撑爆?**
|
|
145
145
|
把这一段包进 `view.batch(fn, { label })`:收尾只发一条 `type: 'batch'` 的 `onDataChange`(`changes` 按顺序列出每一步),历史里也只占一步,见「批量更新」。
|
package/docs/steps/README.md
CHANGED
|
@@ -16,11 +16,11 @@ npm run dev # 或 npx vite
|
|
|
16
16
|
控制台里 `__gisSteps.view` 就是当前这一步的视图,`__gisSteps.DATA` 是那份园区数据,可以随手调 API。
|
|
17
17
|
地址栏里带步骤号(`index.html#7`)可以直接打开某一步,也能收藏 / 分享。
|
|
18
18
|
|
|
19
|
-
自测页就在同一个目录下(`selftest.html` / `selftest-demo.html` / `selftest-demos.html` / `selftest-labels.html` / `selftest-select.html` / `selftest-actions.html` / `selftest-scale.html` / `selftest-perf.html` / `selftest-features.html` / `selftest-full.html` / `selftest-effects.html` / `selftest-data.html` / `selftest-decoration.html` / `selftest-docs.html`):打开就跑,标题报 `PASS` / `FAIL`,页面上的清单是逐条断言。**无头环境下 `requestAnimationFrame` 基本不推进**,动画类断言要用真时间(CDP 或直接开浏览器)跑 —— `selftest-effects.html` 因此自己装了一个「帧泵」(把 `requestAnimationFrame` 收进 Map、手动按帧推进),涟漪与动点的断言才测得动。另有 docs/index.html(文档中心:把仓库里的 markdown 读成 HTML)、docs/api/index.html(API 速查:
|
|
19
|
+
自测页就在同一个目录下(`selftest.html` / `selftest-demo.html` / `selftest-demos.html` / `selftest-labels.html` / `selftest-select.html` / `selftest-actions.html` / `selftest-scale.html` / `selftest-perf.html` / `selftest-features.html` / `selftest-full.html` / `selftest-effects.html` / `selftest-data.html` / `selftest-decoration.html` / `selftest-docs.html`):打开就跑,标题报 `PASS` / `FAIL`,页面上的清单是逐条断言。**无头环境下 `requestAnimationFrame` 基本不推进**,动画类断言要用真时间(CDP 或直接开浏览器)跑 —— `selftest-effects.html` 因此自己装了一个「帧泵」(把 `requestAnimationFrame` 收进 Map、手动按帧推进),涟漪与动点的断言才测得动。另有 docs/index.html(文档中心:把仓库里的 markdown 读成 HTML)、docs/api/index.html(API 速查:265 个实例成员逐个一张卡,参数 / 返回 / 说明 / 示例与复制,搜索 + `#成员名` 深链、左侧分组跟随与深浅主题)、docs/guide/sample-document.html(sample-document.json 的交互图解)与 docs/steps/selftest-docs.html(上面这三个页面的自测:清单 / 渲染 / 点选标注一页跑完)。
|
|
20
20
|
|
|
21
21
|
## 怎么查全部属性与方法
|
|
22
22
|
|
|
23
|
-
组件的完整 API 是同一份清单的五个去处(当前:实例成员 **
|
|
23
|
+
组件的完整 API 是同一份清单的五个去处(当前:实例成员 **265** 个 = 260 个方法 + `el` / `canvas` / `gl` / `cameraAnimating` / `dataVersion` 五个属性,`options` **96** 个键,数据字段 **250** 个):
|
|
24
24
|
|
|
25
25
|
- **手册里查**:`docs/manual/` 把整本手册拆成 21 章(索引页 `docs/GisView.md` 有目录与按任务的快速跳转)—— 找方法先看 [`16 实例方法总表`](../manual/16-实例方法总表.md)、要把每个成员逐个列全(参数写「名字: 类型 = 默认值」、返回值只写类型)看 [`17 参数速查`](../manual/17-参数速查.md)、要能直接复制的调用看 [`18 逐方法示例`](../manual/18-逐方法示例.md);`options` 表与数据字段表分别在 [`03 配置项(options)`](../manual/03-配置项.md) 与 [`02 坐标与数据`](../manual/02-坐标与数据.md)。同名的接口在 `src/gis-view/types.d.ts`。
|
|
26
26
|
- **页面上查**:`docs/api/index.html`(API 速查)—— 左栏按 16 个分组列出全部成员,带搜索(Ctrl+K)与方法 / 属性 / options 三档开关,当前读到的分组会跟着高亮;右栏每个成员一张卡,参数 / 返回 / 说明之外,示例块直接来自 [`18 逐方法示例`](../manual/18-逐方法示例.md),带高亮与「复制名字 / 复制代码」,旁边还有「在调试页跑 →」;地址栏带 `#updateLine` 这样的 hash 直接跳到那一条。右上角主题按钮与文档中心共用 `gisDocs.reading`,跟随系统 / 浅色 / 深色三档换页不会各记一套;深链落点还留了 `scroll-margin-top`,不会被吸顶栏盖住。页面数据来自 `docs/demos/api-index.js`(生成物,禁止手改),自测页 `docs/steps/selftest-docs.html` 盯着它别掉队。
|
|
@@ -109,18 +109,18 @@ API 名字与前面几步是一一对应的;第 11 步再补两件「数据之
|
|
|
109
109
|
- `selftest-features.html`(672 条):真组件上的线型 / 线路装饰 / 光标 / 表现型配置断言 —— 把画面用 `exportImage` 读回 2D 画布数像素(实线 > 虚线 > 点线的墨量、`dc_line` 中点装饰的像素差、`showGrid` 只重画一帧),光标则用合成的指针事件与 Ctrl 键读出 `canvas.style.cursor`;装饰那几条还会量「几何变了装饰跟不跟着走」—— 写回新路径、拖绑定的节点、加 / 删拐点之后,装饰的紫色像素质心都要落在新中点上(容差 3px);另外两条量的是**固定档**:`anchor: 'fixed'` + `at` 先把装饰钉在写死的世界坐标上,再把线从水平拖成斜的,装饰必须留在原坐标、不能跑到新中点。线路标签在单独一张画布上验:默认挂在中点、内容取 `name`,`position: 0` 落到起点,`hideOnInteract` 在缩放期间给整层加 `--hidden`、停手恢复,关掉 `lineLabel` 之后标签消失。装饰缺省 `auto` 的那一串也在这里量:没有拐点时拖端点装饰保持同一个比例(一直挂在中点)、加上拐点之后钉在那一刻的坐标上(改折线也不动)、把拐点删回直线又回到比例跟随、`anchor: 'follow'` 则一直跟到折线的新中点;`parallel` 用一条横条符号验(默认包围盒 26×6,平行之后转成 4×26),方形符号走的是 gl.POINTS 那条路(旋转后外接盒从 24×24 撑成 32×32)。通道里「每条回线各显示自己的标签」也在这里验:没有逐条配置时会被 32px 屏幕小格去重(只显示 1 ~ 2 条),给每条回线写上自己的 `lineLabelPosition` 之后 8 条全都在、位置各不相同,斜线上的标签按线路方向转、朝左的自动翻半圈(rotate = 0)。线路的层序(`zIndex`)也在这里验:画布正中一横一竖两条粗线交叉,默认后写的竖线在上(中心像素蓝),规则里给横线写 `zIndex: 5` 就换成红,`updateLine` 给竖线写 `zIndex: 9` 又换回蓝,写回 0 再回到红。这一轮另外验了插件上下文:`options.plugins` 与 `view.use()` 拿到的第二个参数是同一个冻结对象(`apiVersion === 1`、`view` 指回实例),旧的一参插件写法照常可用。这一轮另补 3 条合并触发时机断言(center 差 10px 不触发、继续拖入目标区域触发并带 trigger、collision 同距离照旧触发)。这一轮再补 1 条:13 个旧快捷方法已从实例删除,`updatePoint` / `updateLine` / `updateChannel` / `updateOptions` / `setFilter` / `setDropPreview` 仍在。对账组另补 8 条:`getChangedChannels({ ignoreGroups })` 忽略分组增删、已有分组拐点仍报、`reasons` 收窄与线路字段 / 路径 / 显隐变化,以及删除通道 / 删除线路 / 无通道场景同步清账。本轮再补实例命令的注册 / 同名覆盖 / 异常与销毁断言,并再补 16 条:筛选恢复后通道宽度与回线车道、`focusOn` 显式 `point` / `line` / `channel` / `group` 与统一缩放口径、`selectFeatures` 分组写法、`selectWaypoint` 带选线路,以及 `lineFollowsNode` 的隐藏 / 筛选 / 删除 / 撤销与动态切换。
|
|
110
110
|
- `selftest-decoration.html`(75 条):动态装饰器草稿专项自测 —— 开启后的 `start` 载荷、默认 14px 触发半径与每条线路不限量、指针在线路上 `hover`(命中线 / 自身 `decoration` / 平行角)、同一位置重复移动不重复发事件、移出目标 `leave`、在线路与空白处 `pointerup` 的 `ready` 差异、按下 / 抬起 `action.type` 都是 `decorationDraft`、单线 / 分组 / 整条通道三档作用域与非通道回退、`maxPerLine` 满额时悬浮与松手都不触发且提交前复核不写数据、`allowedLines` / `allowedChannels` / `allowedGroups` 三种白名单在悬浮与提交两处都拦截、`update` 改图标 / 尺寸 / 方向、`commit` 一次 `batch` 写四条且只发一条 `dataChange`、`cancel` 不改数据、重复 `begin` 替换旧草稿、整份换数据后过期草稿静默清掉。节点侧另有 32 条:`target: 'point'` 命中与只展开一个节点目标、方向 / 距离换算成屏幕偏移、相对偏移 / 固定世界坐标 / 鼠标位置三种位置来源、连接线样式随提交保存、`allowedPoints` 与 `maxPerPoint` 在悬浮 / 提交两处拦截、`both` 等距时节点优先且最近节点被白名单拦住时回退线路、节点与线路的重复 `reject` / `offset` 两种策略、节点装饰 get / update / remove 与整批提交、度转角与连接线进入矢量快照。这一轮另补 8 条节点断言:既有的 2 条「装饰随移动」仍保留;新增 6 条覆盖 `pointer + follow`、`fixed + follow`、`offset + fixed` 三种跨组合的预览与提交结果,确认位置来源和落地关系解耦后分别写成 `offset` 或 `at`。
|
|
111
111
|
- `selftest-demo.html`:demo 级自测 —— 面板上的「定位直流线路」按当前数据集的 `dc_line` 条数走(默认数据集 `new.json` 里现在自带 5 条,点一下直接选中并定位到第一条;一条都没有的数据集才切到样例数据、定位到「直流送电」,中点挂着 `PATH_DC_LINE` 装饰),断言按数据算出来的条数写、不写死数字,读数与选中面板都跟着更新;打开时说明段落默认收起、面板按组折叠,节点标签只给最高电压那一档、线路标签默认整个不显示(面板上勾「显示」再按「显示范围」展开),面板上有 PATH_DC_LINE 的「标记定位」下拉(自动 / 一直跟随 / 写死坐标三档)。「通道回线各自显示」这一档从数据层核对(勾上之后 3669 条回线都带上了自己的 `lineLabelPosition`),再放大三步后勾「与线路平行」,画出来的标签 transform 里要出现 `rotate(...)`。这一轮又补了「数据映射」下拉(raw / lngLat / fit / scale)与「节点 / geojson 比例」滑块:切到 fit 后 320 个站点都要落进 GeoJSON 画出来的范围、读数里给出换算比例;「经纬度 → 屏幕」「屏幕 → 经纬度」两个按钮来回换算一致(屏幕读数保留 1 位小数,允许 0.01° 的回差);在画布上挪一下鼠标,面板上就要出现屏幕像素与经纬度两行读数。
|
|
112
|
-
- `selftest-perf.html`(
|
|
112
|
+
- `selftest-perf.html`(183 条):大数据量与画质档位的性能自测 —— 20000 点 / 8000 线的建场景与整份重排耗时、500 次拾取、逐个改 500 个节点对 `view.batch()`、四档画质与 `'auto'` 的自动升降;前面几轮补过 8 条交互期细线的断言(`getStats()` 给出 `antialias` / `software` / `thinLines` 三个读数;交互期 1px 实线整批走 `gl.LINES` 快路径、停手回到四边形展开;`interactionThinLines: false`、线宽 8px、整批带虚线这三种情况下都不走快路径,改回细实线又走);上一轮补过 7 条线路标签候选复用的断言(复用态与整层重排逐字一致,改线路内容 / 挪节点 / 相机变化之后缓存都作废)与 1 条连续更新的耗时 DIAG(2 万点 / 1 万线,中位 0.30 ms / 次);**这一轮再补 3 条标签避让盒的断言**(4 万点 + 4 万条长线放大后连续整层重排:第 2 轮起一次布局都不读、同一轮里没有哪条标签被量第二次)与 1 条量测读数 DIAG(逐轮 1427 / 0 / 0 / 0 / 0 次,旧写法 2662 / 0 … 次、第 1 轮重复量 1235 条;**上一轮把「没有哪条标签被量第二次」从按 DOM 节点去重改成按标签身份(`labelKind` # `labelIndex` # `labelLabel`)去重** —— 标签节点是池化复用的,按节点去重会把「同一格换了条标签」误报成重复量,旧代码实测第 1 轮 1235 条、新代码 0 条;**这一轮再补 3 条线路批次视口裁剪的断言**(20000 点 / 8000 线放大到 `scale: 10`:`lineInstances` 8000 → 27、相机挪回来回到 8000、来回一趟整幅像素差 0 / 241164)与 1 条读数 DIAG。这一轮再补 2 条点视口网格的断言(网格可用时枚举顺序与整份筛出来的下标序列一致、回调返回 `false` 最多只访问一个)。这一轮再补 19 条点聚合断言(归一化、1.2 万点建组与统计、原始数据不变、`visibleOnly` 排除成员、拾取 `type=cluster`、点击语义、`maxZoom` 展开、关闭恢复),页面每段之间让出一帧,边跑边出结果。这一轮另补 7 条热力图缓存断言:`styleKey` / `weightKey` 的语义、只改 `density.opacity` 不重采不重烤、换色带与定向改要素值会重烤。;本轮新增 1 条相机运动帧断言:人为拉长帧间隔并连续改相机,记录到的每次 `gl.viewport` 都必须等于画布全尺寸,防止运动期降采样重新引入模糊;本轮再补 1 条像素级断言:在同一交互状态下把细线专用片元管线与四边形画法逐像素对拍,线像素数量与整幅颜色差都必须在容差内,防止新管线把整批线画丢、画黑或换色。
|
|
113
113
|
- `selftest-scale.html`(125 条:119 PASS + 6 DIAG):大数据交互这一轮的自测 —— 2000 点 / 2000 线、筛选开着:`batch()` 里改 200 个节点只发 1 条 `onFilter` 与 1 条 `onDataChange`(对照组逐个调用是 200 条);改字段 / 改坐标 / 改线路之后「增量补写的隐藏集」与「从零建一个同样条件的视图」逐位一致;点挪到被筛掉的坐标上,按 `coordinate` 联动的线路当场隐藏、挪回去又出来;线段网格的步进登记与多层粗格(沿线查得到、线不经过的角落不进候选、跨层只返回一次、2 万条长线密集时小范围查询的候选远少于总条数、整图估计值与总条数同量级);点 / 线 / 空白的拾取;搜索文本缓存的命中与「改过字段之后按新值搜得到、旧值搜不到」。最底下几条 DIAG 是耗时读数(`Date.now()`),无头浏览器里虚拟时钟不往前走、显示 0.0 ms,带界面的浏览器里才是真实数字。;这一轮又补了 8 条:三万点的发散渲染(超过 2 万点走分批铺开)铺完之后装饰 400 个一条不少、挪一条带装饰的线的端点只动那一条线的装饰且落在新路径中点上、贴图符号进的是贴图批次、选中的节点抬到最上层、整份重排之后装饰与动点不翻倍,以及「节点自己配的涟漪数得清」(这一条顺带抓出了发散渲染期间涟漪补不齐的 bug)。这一轮再补一条 `createPointGrid.estimate` 与 `query` 同口径、带 `stopAfter` 提前封顶的断言(不建候选数组也能判断值不值得走网格);本轮又补了删除型筛选的 10 条回归:同步初始删除先于 `ready`、无 id 线路按下标删、级联线路与空通道一起清掉、单条 `filterRemove` / 单次 `onFilter`、历史一步、撤销不再次删除、重做不补发第二条事件、运行时 `setFilter` 与 `cascade: false` 对照,另校验载荷里的 `removedPoints` / `removedLineItems` 与剩余数据。
|
|
114
114
|
- `bench-filter.html`:基准页(不是自测,只会打印读数)—— 20000 点 / 20000 线、`cascade: 'binding'` 的筛选开着(`filterWorker: false`),量「连换 10 次筛选条件」「`batch()` 里改 500 个节点(筛选开着 / 关掉)」「单个 `updatePoint`」「默认全字段 `search` 跑两次」这几档,每档先热身一遍再计时(`Date.now()`)。标题从 `bench running` 变成 `bench done` 表示跑完;别用 `--virtual-time-budget` 打开它 —— 虚拟时钟下同步脚本里的 `Date.now()` 不往前走,量出来全是 0。
|
|
115
115
|
- `bench-scale.html`:百万级基准页(同样只打印读数)—— 点 / 线规模与视口走 URL 参数,`window.__bench.run(options)` 量生成数据、建场景与发散渲染、平移 / 缩放 / 放大 / 拾取、`refresh` / `updatePoint` / `updateOptions`、内存账与销毁回收;另有 `prepare`(只建场景)、`__bench.op(name)`(单步)与 `__bench.micro(count)`(原语微基准)。配 `scripts/bench-scale.mjs` 用(CDP 驱动,`--points=1000000 --gpu --port=93xx`,另有 `--profile` 抓 CPU profile、`--heap` 抓堆快照);跑全部自测页用 `scripts/run-selftests.mjs`(`node scripts/run-selftests.mjs [页面名…]`,无头 Chrome + 真时间),两个脚本都要先起 dev server。
|
|
116
116
|
- `selftest-labels.html`(165 条):标签拖动与编辑、HTML 语义、高密度画布、导出分项多选的专项自测 —— 按住标签拖 90 px 之后标签真的挪了那么多、同时画布不平移,`labelDrag` 的 `start` / `move` / `end` 三态与 `offset` / `delta` 对得上,偏移进得了 `getLabelStates()`、再拖动节点时偏移照旧叠在节点位移上,`resetLabel` 之后回到默认锚点;`updateLabel` 改内容 / 字号 / 颜色(`style` 是增量合并,只改字号不动颜色)、`resetLabels` 一次清干净;点一下标签发 `labelClick` 并把这一条交给面板;线路标签的拖动与恢复默认同一条路;导出侧验「选什么导什么」(选场站只出场站、选通道自动带上拐点、不认识的分项记一条提示),`exportData({ scope: 'selection' })` 验场站 + 通道 / 只出线路 / 没选中时的 `ok: false` 三个分支。HTML 专项验全局 / 逐条 `true` / `false`、模板与 `field` / 默认名称转义、`updateLabel` 双向切换、线路标签富文本、点击载荷保留 `html` 与原标记、`getLabels()` 报运行时偏移、只改样式不替换 HTML 子节点、导出 → 回灌保持逐条语义,以及 120 条同内容标签首轮布局读取 ≤ 4 次的缓存断言。高密度专项用 1100 条纯文本验缺省 `auto` 只留一张画布、混入一条 `html: true` 仍保留那一条 DOM、`labelRender: 'dom'` 回退逐条 DOM 并清空画布、切回 `auto` 回收上一轮 div、SVG 导出包含画布标签与缩放只推一次;新增断言还验 1100 条同文案只调用 `measureText` ≤ 2 次、同一任务连续 5 次缩放只重绘 1 ~ 2 次、显式 `canvas` 在优化档遵守 `maxCount: 200`,以及 6 条线路标签在优化档全部进画布且 DOM 为 0。另验 GeoJSON 区域文字同一相机重复刷新 0 次 `style` 写入、只改字号复用同一 DOM 节点、离屏时改样式再回到视野仍能恢复显示、只改锚点位置也会重新投影,以及同一帧 12 次相机变化只做 1 次重投影。设备像素专项把 `devicePixelRatio` 临时设为 1.25,验标签落点与拖动期整层位移都落在物理像素网格上,并确认逐条标签不再各自声明 `will-change`。
|
|
117
117
|
- `selftest-actions.html`:事件判读与只读接口的专项自测 —— 左右键、拖动、框选、拐点与标签的 `action` 三件套(type / target / data)、`button` / `modifiers` / `moved` / `double`、Shift 框选(含没框到东西的空框)与 Ctrl 取反在 `selectChange.action.type` 上的分辨、`pickAt` 的客户端坐标与只读性、`getFeaturesInRect`(含宽高为负的矩形)与 `getChannelsInView`、`addPoints` / `addPoint` / `addLines` / `removeLines` 的返回与 `dataChange` 类型;最后一段把第 12 步 `step-12-events.js` 挂起来跑一遍(判读读数、`probeAt` / `probeRect`、两条走廊)。页面底部流式打印 PASS / FAIL。
|
|
118
|
-
- `selftest-select.html`:选择与框选的专项自测(
|
|
118
|
+
- `selftest-select.html`:选择与框选的专项自测(84 条)—— 点选 / 悬停 / Ctrl 取反 / 多选开关的像素级状态样式与恢复,`selectFeatures` / `selectAt` / `deselect` / 通道写法 / 变更来源的接口断言(含 selectFeatures 实际变化时发 onSelect + onSelectChange、重复设同一选中集不重复通知、selectAt 每次命中发 onSelect 且集合变化时再发 onSelectChange),以及 SVG 状态贴图的预热与不同尺寸配方;这一轮补了 `selectionKinds: 'all'` 下 `selectInBox` 一次收下节点、独立线路与按 `channelSelectMode` 摊开的通道回线、`selectAt` 的节点 / 线路 / 通道 / 空白四路、多分组通道在 pickAt / hoverAt / 鼠标事件里的 `group` / `groups`,以及 SVG 导出 `currentStrokeColor` 的描边色还原和 `selectTrigger` 的单击、双击、多选取反 / 累加 / 取消。本轮再加 4 条:`onSelect` / `onSelectChange` 的单通道顶层上下文与 `items[]` 标量归属、真实点击 `onClick → onSelect → onSelectChange` 顺序、`selectFeatures(整条通道)` 与 `selectAt` 的通道载荷和 `onSelect → onSelectChange` 顺序(其中 `channelIndex` 同时校验为通道声明下标);另补 1 条多通道选中断言,确认只给 `channelContexts`、不重复摊平,且两条事件共享同一批 `items`。
|
|
119
119
|
- `selftest-demos.html`:演示外壳与 22 个页签(21 个功能 + 综合,按基础 / 交互 / 样式 / 数据 / 性能 / 综合六组排列)的自测(**347** 条)—— 逐个用 `?demo=<id>` 打开基础视图、标签交互、标签显示、鹰眼图配置、样式与规则、悬停与提示、通道与数据、编辑与保存、事件与方法、接口能力、批量复制与合并、拖拽落点、可视化连线、动态装饰器、批量新增、搜索与筛选、扩展能力、性能与质量、点聚合、动画效果、扩展图形与综合示例,画布与右栏读数都在、切页签会把 `?demo=` 写回地址栏、切走之后上一个实例被销毁(不留画布)、缺省页签是综合示例、页签总数与注册表一致、顶部六个分组按钮一次只展开一组且组名可见;鹰眼图配置页另验 `layers` 的自定义 `draw` 真被调用、`placement` 切到数据前 / 后时 `drawInfo.stage` 跟着变、框拖动触发 `onInteraction` 并带相机、尺寸切换走 `updateOptions` 重铺画布、DPR 2 下 `maxPixelRatio` 的 1× / 2× 备份尺寸、关掉扩展层与 `zoomMinimapBy` / `setMinimapZoom`,以及 `extentFollowCamera` 默认关闭 / 打开生效、缩放关闭时 `dragAction: 'box'` 仍可框选、`zoomable: 'follow'` 跟随主图且 `setMinimapZoom` 不抢控制权;批量复制 / 合并、通道拐点 / 分组两页另有假接口断言(Shift 框选、整份请求体、标准响应包与资源级结果、版本号 / opId、200 / 网络失败 / 500 / 409、原 opId 重试与幂等重放不推进版本、409 rebase、本地回滚);假后端严格契约另有一组直接断言(请求头与 body 不一致返回 400、baseVersion 非有限数字返回 400、同 opId 换 body 返回 409 `IDEMPOTENCY_CONFLICT` 且版本不前进),通道页还验了自定义格式按钮注册 → 导出 → 注销;扩展能力页另验自定义运算符 / 图元 / `view.use` 插件 / `defineExportFormat` / 实例命令的注册、执行、同名覆盖、卸载与业务 DTO 结构,并验同名重新注册图元当场换画笔;可视化连线页另验初始 `channels` 为空、正交折线拐点、预览箭头、取消不写数据,默认提交 / `addLines` / `addChannels` 三种落地,指定起点后一次点击即可 ready、两端示例点的显隐 / 尺寸 / 形状 / 分端颜色与 `below-points` 层级、`commitAddChannel` 直接传线路 / 通道对象(含对象 path / 颜色 / 线宽优先);动态装饰器页另验吸附方式只有「跟随 / 固定」、两档下位置都有「相对节点 / 固定位置 / 鼠标位置」三档,固定 + 相对节点只改 `anchor`,固定 / 跟随 + 鼠标位置分别映射为 `placement: pointer` + 对应 `anchor` 且不带 `at`,跟随 + 固定位置仍传 `at`;通道与数据页另验「线路分组」弹框:分组卡片与「场站对标标识」都在,行内「确认移入」只改本地草稿、组件数据不动,取消 / Esc / 遮罩关闭不写数据,「确定分组」只调用一次 `splitChannelGroups` 把整份 groups 一次落地。点聚合页另验配置控件、聚合点击只增聚合计数、`maxZoom` 展开与适应全部后恢复、半径 / 重建 / 关闭统计;接口能力页另验三种级联触发独立开关、通道 / 分组 / 拐点程序化选中、通道 `focusOn`、`lineFollowsNode` 联动,以及样式字段合并、节点 / 线路装饰 CRUD、通道线宽与线路对账、GeoJSON 背景投影、历史 / 复制 / 删除收尾、`once`、WebGL 层清理、注册表注销与初始化 options 读数;页面底部流式打印 PASS / FAIL。鹰眼图线路层另有 3 条断言:首帧只铺固定范围里的线路、同一份数据补画命中缓存、小图缩放只重铺一次且仍裁剪范围外线路。
|
|
120
120
|
- `selftest-effects.html`:线路 / 节点动画与贴图预热的专项自测(96 条,页面自带「帧泵」—— 无头环境里 `requestAnimationFrame` 几乎不推进,它把 rAF 收进 Map、手动推帧,动画类断言才测得动):纯函数组(归一化、三层「关」的语义、按弧长采样、拐点连续、相位打散)、引擎组(铺批次 / 关掉一层 / 恢复 / 逐要素覆盖 / `getLineEffects` 全程贴线)、像素组(画布上真的有一个红点在两帧之间挪了位置)、**涟漪跟手组**(开着动画拖节点:拖动前圆心在节点坐标上、拖动中跟着走且是就地改实例缓冲、松手后整份重建也对齐、再拖回原位跟着回来,另有一条像素级断言量绿圈质心落在新坐标上)、**预热组**(全排列条数、第二次命中缓存、`getSymbolCombos()` 的去重与线路侧那一条、照着它配的一份**零张新画布**、反向一条「没画过的组合会当场烤、画布 +1」)、提醒组(`effects.line` 里写 `show` 被点名);这一轮另补 4 条动点缓存断言(5000 条普通线 + 2 条动点线:每帧只遍历动画线路,路径换短 / 换长后总长跟着更新)。
|
|
121
121
|
- `selftest-data.html`:标准数据文档与渲染后端开关的自测(103 条)—— `normalizeDocument` 的标准写法与 JSON 安全(无效日期写 `null` 并告警、类实例不静默变成空对象)、整份文档直接当 `createGisView` 入参、`exportData({ format: 'full' })` 导出 → 重建 → 再导出逐字节相同、`importData` 的整份与分列、`renderer` 三档与能力探测、网格顶点缓存窗口,以及自定义导出格式的注册 / 上下文 DTO 映射 / 深拷贝 / 视口导出 / 注销后失效;断言全在接口层,不读像素。
|
|
122
122
|
- `check-sample-fields.mjs`:不是页面、是对账脚本(`node docs/steps/check-sample-fields.mjs`)—— 核对 `sample-document.json` 自己就是标准写法(`normalizeDocument` 后逐字节相同、0 条提醒)、`docs/sample-document字段详解.md` 里的读数与 `documentCounts()` 一致、以及它的「字段索引」与从文件里摘出来的字段路径集合一个不多一个不少(当前 154 个);改完样本数据跑 `--write` 重写那两段机器块(幂等)。
|
|
123
|
-
- `selftest-docs.html`:三个文档页 + 调试页的自测(134 条)—— 文档中心(`docs/index.html`)的清单 92 份 md(10 个根 + 82 份子文档,含新的 `接口协议.md`;树形侧栏按 `parent` / `groupLabel` 分成 6 组 `details`)、首屏 README 渲染、markdown 渲染器(表格 / 带语言代码块 / 两级列表 / 引用 / 行内代码 / md 链接 / 目录 / 首条摘要卡片 / 首段导语)、元信息里的阅读时长、索引页的子文档链接、换一份子文档、代码块复制按钮、跨文档搜索与深链、跨文档锚点链接(`文件.md#小节` 原地切文档并闪一下)、能直接跑的围栏才有的「在调试页跑」与元信息行的「这一份的调试页(N 段示例)」、390px 窄屏首屏 / 深链 / 横向溢出回归、阅读百分比与「回到顶部」的联动;调试演示页(`docs/playground/index.html`)的一个示例下拉(内置示例 + 「这一份文档」两组 optgroup)、`?doc=` 读文档抽示例、片段标注与来源子栏、顶层 await 运行、`channels` 快照、光标行列、草稿自动保存与恢复、`Ctrl+S` 运行、复制 / 清空输出、状态胶囊;API 速查页(`docs/api/index.html`)的 `__gisApi` 钩子、六张统计卡、
|
|
123
|
+
- `selftest-docs.html`:三个文档页 + 调试页的自测(134 条)—— 文档中心(`docs/index.html`)的清单 92 份 md(10 个根 + 82 份子文档,含新的 `接口协议.md`;树形侧栏按 `parent` / `groupLabel` 分成 6 组 `details`)、首屏 README 渲染、markdown 渲染器(表格 / 带语言代码块 / 两级列表 / 引用 / 行内代码 / md 链接 / 目录 / 首条摘要卡片 / 首段导语)、元信息里的阅读时长、索引页的子文档链接、换一份子文档、代码块复制按钮、跨文档搜索与深链、跨文档锚点链接(`文件.md#小节` 原地切文档并闪一下)、能直接跑的围栏才有的「在调试页跑」与元信息行的「这一份的调试页(N 段示例)」、390px 窄屏首屏 / 深链 / 横向溢出回归、阅读百分比与「回到顶部」的联动;调试演示页(`docs/playground/index.html`)的一个示例下拉(内置示例 + 「这一份文档」两组 optgroup)、`?doc=` 读文档抽示例、片段标注与来源子栏、顶层 await 运行、`channels` 快照、光标行列、草稿自动保存与恢复、`Ctrl+S` 运行、复制 / 清空输出、状态胶囊;API 速查页(`docs/api/index.html`)的 `__gisApi` 钩子、六张统计卡、265 张成员卡都带签名网格与示例、96 个 options 四列网格、搜索过滤与命中读数、`select(名字)` 高亮、类型开关能单独收起属性、左侧当前分组高亮与即时跳转、顶栏文档深链、每个分组的手册章节链接、深浅主题切换与阅读设置共用;图解页(`docs/guide/sample-document.html`)的 47 条数据列表、点线路 / 分组 / 标签状态的图上标注、`点击时居中` 与 `显示编号` 开关、清空标注;三个页面都跑在 iframe 里,要用 dev server 打开。
|
|
124
124
|
|
|
125
125
|
上一轮补上的两组断言:`selftest-features.html` 新增 8 条 —— 3 条验 `channelSelectMode` 在 Ctrl + 点击加点后的高亮范围(`line` / `group` / `channel`),5 条验分组 `channelWaypointDragMode: 'vertex'`(`{ x, y }` 偏移归一化并参与实际路由、同组同序号拐点按世界增量平移、相邻拐点与其它分组不动、只写 `waypointOffsets` 不改公共 `path`、`parallel_groups.group_route` 带刚性偏移)。`selftest-demos.html` 新增 1 条,验通道演示页可切到 `vertex`,且「准备两分组拐点」会拆成两组、每组两个拐点。
|
|
126
126
|
|