gis-view-webgl2 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +356 -2
- package/dist/gis-view.css +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.ts +7861 -0
- package/dist/index.js +1 -0
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +21 -0
- package/docs/GisView.md +52 -0
- package/docs/GisView/345/256/236/347/216/260/345/216/237/347/220/206.md +42 -0
- package/docs/ai/01-/350/265/267/346/272/220/344/270/216/350/277/231/344/273/275/346/261/207/346/200/273/347/232/204/347/224/261/346/235/245.md +29 -0
- package/docs/ai/02-/346/214/211/345/206/205/345/256/271/345/210/206/347/261/273-/350/277/231/344/270/200/350/267/257/351/203/275/345/234/250/346/224/271/344/273/200/344/271/210.md +66 -0
- package/docs/ai/03-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/270/200/357/274/211/357/274/232/351/200/211/345/236/213/351/200/232/351/201/223/344/270/216/346/240/267/345/274/217.md +178 -0
- package/docs/ai/04-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/272/214/357/274/211/357/274/232/344/272/244/344/272/222/346/200/247/350/203/275/344/270/216/346/225/260/346/215/256.md +140 -0
- package/docs/ai/05-/351/207/215/350/246/201/351/227/256/347/255/224/351/200/211/357/274/210/344/270/211/357/274/211/357/274/232/345/256/277/344/270/273/346/227/201/346/224/257/344/270/216/346/224/266/345/217/243.md +43 -0
- 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 +75 -0
- package/docs/api/index.html +472 -0
- package/docs/demos/BackendSyncDemo.vue +1155 -0
- package/docs/demos/BasicDemo.vue +203 -0
- package/docs/demos/BatchAddDemo.vue +309 -0
- package/docs/demos/ChannelDemo.vue +1159 -0
- package/docs/demos/CopyMergeDemo.vue +1399 -0
- package/docs/demos/DemoShell.vue +337 -0
- package/docs/demos/DragDropDemo.vue +1020 -0
- package/docs/demos/EffectsDemo.vue +410 -0
- package/docs/demos/EventDemo.vue +389 -0
- package/docs/demos/GisViewDemo.vue +8796 -0
- package/docs/demos/HistoryDemo.vue +230 -0
- package/docs/demos/HoverTooltipDemo.vue +397 -0
- package/docs/demos/InterfaceDemo.vue +309 -0
- package/docs/demos/LabelDemo.vue +581 -0
- package/docs/demos/LabelDisplayDemo.vue +234 -0
- package/docs/demos/PerfDemo.vue +283 -0
- package/docs/demos/SampleDemo.vue +283 -0
- package/docs/demos/StyleDemo.vue +184 -0
- package/docs/demos/api-index.js +419 -0
- package/docs/demos/demoBackground.js +119 -0
- package/docs/demos/demos.js +170 -0
- package/docs/demos/gisStyle.js +421 -0
- package/docs/demos/shared/demoData.js +107 -0
- package/docs/demos/shared/demoStyle.css +250 -0
- package/docs/demos/shared/fakeBackend.js +517 -0
- package/docs/format/01-/346/200/273/350/247/210/344/270/216/345/235/220/346/240/207/347/263/273.md +64 -0
- package/docs/format/02-/350/212/202/347/202/271/344/270/216/347/272/277/350/267/257.md +78 -0
- package/docs/format/03-/346/213/220/347/202/271/350/243/205/351/245/260/344/270/216/346/240/267/345/274/217.md +95 -0
- package/docs/format/04-/351/200/232/351/201/223/344/270/216/346/240/207/347/255/276.md +72 -0
- package/docs/format/05-/346/235/241/344/273/266/350/257/255/346/263/225.md +31 -0
- package/docs/format/06-/345/257/274/345/205/245/345/257/274/345/207/272/344/270/216/346/240/207/345/207/206/346/226/207/346/241/243.md +98 -0
- package/docs/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +109 -0
- package/docs/from0/01-/345/205/210/346/212/212/345/234/260/345/233/276/346/203/263/346/270/205/346/245/232.md +123 -0
- package/docs/from0/02-WebGL2/350/265/267/346/255/245.md +144 -0
- package/docs/from0/03-/347/224/273/347/254/254/344/270/200/346/211/271/347/202/271.md +169 -0
- package/docs/from0/04-/347/224/273/347/272/277.md +91 -0
- package/docs/from0/05-/347/233/270/346/234/272/344/270/216/344/272/244/344/272/222.md +201 -0
- package/docs/from0/06-/344/273/216/346/225/260/346/215/256/345/210/260/345/256/236/344/276/213-/350/247/243/346/236/220/345/261/202.md +248 -0
- package/docs/from0/07-/351/200/232/351/201/223/345/210/206/347/273/204/344/270/216/345/244/232/345/233/236/347/272/277.md +359 -0
- package/docs/from0/08-DOM/350/246/206/347/233/226/345/261/202-/346/240/207/347/255/276/344/270/216/346/217/220/347/244/272/346/241/206.md +148 -0
- package/docs/from0/09-/351/271/260/347/234/274/345/233/276.md +115 -0
- package/docs/from0/10-/347/255/233/351/200/211/350/247/204/345/210/231/344/270/216/346/220/234/347/264/242.md +135 -0
- package/docs/from0/11-/346/200/247/350/203/275-/345/215/201/344/270/207/347/272/247/346/225/260/346/215/256.md +76 -0
- package/docs/from0/12-/346/211/223/345/214/205/344/270/216/351/230/205/350/257/273.md +92 -0
- package/docs/from0/13-/344/273/216/351/233/266/346/220/255/344/270/200/346/225/264/345/245/227-/344/270/212.md +618 -0
- package/docs/from0/14-/344/273/216/351/233/266/346/220/255/344/270/200/346/225/264/345/245/227-/344/270/213.md +417 -0
- package/docs/from0/15-/351/231/204/345/275/225-/345/270/270/351/207/217/351/200/237/346/237/245/344/270/216/350/270/251/345/235/221/346/270/205/345/215/225.md +89 -0
- package/docs/guide/sample-document.html +705 -0
- package/docs/index.html +2289 -0
- package/docs/internals/01-/346/200/273/350/247/210/346/226/207/344/273/266/350/201/214/350/264/243/344/270/216/346/225/260/346/215/256/346/265/201.md +35 -0
- package/docs/internals/02-/344/273/216/345/210/233/345/273/272/345/210/260/347/254/254/344/270/200/345/270/247.md +44 -0
- package/docs/internals/03-/346/270/262/346/237/223/347/256/241/347/272/277.md +92 -0
- package/docs/internals/04-/347/233/270/346/234/272/345/235/220/346/240/207/344/270/216/345/203/217/347/264/240.md +54 -0
- package/docs/internals/05-/346/213/276/345/217/226.md +14 -0
- package/docs/internals/06-/351/200/232/351/201/223/351/252/250/346/236/266/344/270/216/345/244/232/345/233/236/347/272/277.md +24 -0
- package/docs/internals/07-/345/242/236/351/207/217/346/233/264/346/226/260.md +27 -0
- package/docs/internals/08-/350/231/232/347/272/277/347/272/277/345/236/213.md +19 -0
- package/docs/internals/09-/345/205/211/346/240/207/347/212/266/346/200/201/346/234/272.md +28 -0
- package/docs/internals/10-/350/247/204/345/210/231/346/235/241/344/273/266/344/270/216/347/212/266/346/200/201/346/240/267/345/274/217.md +13 -0
- package/docs/internals/11-/350/246/206/347/233/226/345/261/202/346/240/207/347/255/276/344/270/216/346/217/220/347/244/272.md +21 -0
- package/docs/internals/12-/347/255/233/351/200/211/344/270/216Worker.md +22 -0
- package/docs/internals/13-/351/271/260/347/234/274/345/233/276.md +16 -0
- package/docs/internals/14-/350/203/214/346/231/257/345/261/202.md +14 -0
- package/docs/internals/15-01-/351/200/220/350/275/256/350/256/260/345/275/225-/346/226/260/345/242/236/344/270/216/344/277/256/345/244/215.md +190 -0
- package/docs/internals/15-02-/351/200/220/350/275/256/350/256/260/345/275/225-/346/200/247/350/203/275/344/270/216/344/277/256/345/244/215-/344/270/212.md +574 -0
- package/docs/internals/15-03-/351/200/220/350/275/256/350/256/260/345/275/225-/346/200/247/350/203/275/344/270/216/344/277/256/345/244/215-/344/270/213.md +503 -0
- package/docs/internals/15-04-/351/200/220/350/275/256/350/256/260/345/275/225-/347/203/255/345/212/233/345/233/276/344/270/216/346/200/247/350/203/275.md +454 -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 +300 -0
- package/docs/internals/16-02-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/255.md +152 -0
- package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +134 -0
- package/docs/internals/17-/350/260/203/350/257/225/344/270/216/346/211/251/345/261/225/345/273/272/350/256/256.md +16 -0
- 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 +307 -0
- package/docs/manual/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +69 -0
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +288 -0
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +881 -0
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +263 -0
- 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 +74 -0
- package/docs/manual/06-/346/225/260/346/215/256/344/270/216/345/234/272/346/231/257/346/233/264/346/226/260.md +322 -0
- 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 +654 -0
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +211 -0
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +196 -0
- package/docs/manual/10-/351/200/232/351/201/223/347/274/226/350/276/221/344/270/216/346/226/260/345/242/236.md +317 -0
- package/docs/manual/11-/346/240/267/345/274/217/344/270/216/350/247/204/345/210/231.md +195 -0
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +284 -0
- package/docs/manual/13-/345/212/250/346/225/210/344/270/216/345/233/276/345/205/203.md +380 -0
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +475 -0
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +349 -0
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +197 -0
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +324 -0
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +767 -0
- 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 +98 -0
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +110 -0
- 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 +177 -0
- package/docs/mini/index.html +193 -0
- package/docs/mini/mini-map.js +751 -0
- package/docs/playground/index.html +713 -0
- package/docs/runnable-sample.js +104 -0
- package/docs/sample-document/345/255/227/346/256/265/350/257/246/350/247/243.md +548 -0
- package/docs/steps/README.md +145 -0
- package/docs/steps/api-source.mjs +322 -0
- package/docs/steps/bench-filter.html +66 -0
- package/docs/steps/bench-scale.html +404 -0
- package/docs/steps/check-doc-center.mjs +134 -0
- package/docs/steps/check-doc-coverage.mjs +102 -0
- package/docs/steps/check-doc-truth.mjs +124 -0
- package/docs/steps/check-sample-fields.mjs +146 -0
- package/docs/steps/data.js +71 -0
- package/docs/steps/doc-sources.mjs +51 -0
- package/docs/steps/gen-api-index.mjs +130 -0
- package/docs/steps/index.html +674 -0
- package/docs/steps/rounds/01-/347/254/2541-11/350/275/256.md +148 -0
- package/docs/steps/rounds/02-/347/254/25412-22/350/275/256.md +164 -0
- package/docs/steps/rounds/03-/347/254/25423-33/350/275/256.md +149 -0
- package/docs/steps/rounds/04-/347/254/25434-44/350/275/256.md +127 -0
- package/docs/steps/rounds/05-/347/254/25445-55/350/275/256.md +145 -0
- package/docs/steps/rounds/06-/347/254/25456-66/350/275/256.md +162 -0
- package/docs/steps/rounds/07-/347/254/25467-77/350/275/256.md +165 -0
- package/docs/steps/rounds/08-/347/254/25478-88/350/275/256.md +167 -0
- package/docs/steps/rounds/09-/347/254/25489-93/350/275/256.md +86 -0
- package/docs/steps/rounds/README.md +18 -0
- package/docs/steps/selftest-actions.html +510 -0
- package/docs/steps/selftest-data.html +431 -0
- package/docs/steps/selftest-demo.html +805 -0
- package/docs/steps/selftest-demos.html +1686 -0
- package/docs/steps/selftest-docs.html +653 -0
- package/docs/steps/selftest-effects.html +662 -0
- package/docs/steps/selftest-features.html +4365 -0
- package/docs/steps/selftest-full.html +854 -0
- package/docs/steps/selftest-labels.html +927 -0
- package/docs/steps/selftest-perf.html +1870 -0
- package/docs/steps/selftest-scale.html +707 -0
- package/docs/steps/selftest-select.html +562 -0
- package/docs/steps/selftest.html +154 -0
- package/docs/steps/step-01-canvas.js +136 -0
- package/docs/steps/step-02-points.js +210 -0
- package/docs/steps/step-03-lines.js +178 -0
- package/docs/steps/step-04-camera.js +148 -0
- package/docs/steps/step-05-pick.js +296 -0
- package/docs/steps/step-06-style.js +131 -0
- package/docs/steps/step-07-channels.js +608 -0
- package/docs/steps/step-08-labels.js +173 -0
- package/docs/steps/step-09-minimap.js +161 -0
- package/docs/steps/step-10-full.js +150 -0
- package/docs/steps/step-11-background.js +161 -0
- package/docs/steps/step-12-events.js +195 -0
- package/docs/steps/step-13-engineering.js +105 -0
- package/docs/steps/util.js +116 -0
- package/docs//344/273/2160/345/210/260/345/256/214/346/225/264/347/232/204/345/256/236/347/216/260.md +35 -0
- package/docs//346/216/245/345/217/243/345/215/217/350/256/256.md +496 -0
- package/docs//346/225/260/346/215/256/346/240/274/345/274/217.md +25 -0
- package/package.json +40 -3
package/README.md
CHANGED
|
@@ -1,3 +1,357 @@
|
|
|
1
|
-
#
|
|
1
|
+
# gis-view
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
不依赖任何图形库的 WebGL2 矢量视图:点、线、背景层(图片 / GeoJSON)与首屏落位(`initialView`)都通过 `createGisView(dom, options)` 创建,宽高默认跟随传入的 DOM 容器;跨项目复用的视图级逻辑用 `plugins` 挂载。
|
|
4
|
+
|
|
5
|
+
- 渲染层:原生 WebGL2(GLSL ES 3.00 + 实例化绘制),没有任何运行时依赖,也不需要 Vue。
|
|
6
|
+
- 交互层:拖拽平移、滚轮缩放、按关键字搜索并定位(节点 / 线路 / 通道)、点击 / 双击拾取、点拖拽(相连线路同步移动)、Shift 拖动框选与 Ctrl 点击累加多选(多选后整组同步移动;`selectionKinds` 可限定只选节点 / 线路 / 通道或任意组合)、复制只带选中要素、节点合并可指定保留端并改写最终信息与样式、线路拐点编辑与通道多回线,全部走配置与回调 —— **这些能力缺省全关**,要哪一项显式打开(`features` 预设 / 开关表,或 `interactive: true` / `clickable: true` / `draggable: true` 这类详细配置,见 `docs/GisView.md` 的「功能开关」)。
|
|
7
|
+
- 数据层:按属性筛选(点被过滤时绑在它身上的线路与通道一起消失,大数据量走 Web Worker)、按属性批量配色 / 换符号、节点标签(内容、贴边方式、HTML 定制、数量与显隐可配,十万级数据下有候选上限兜底)与点 / 线 / 通道的悬浮提示,筛选、规则与标签共用同一套条件语言;节点标签与线路标签分开配置、都支持 `auto`(智能隐藏)/ `always` / `hidden` 三种显隐策略,线路标签的位置沿线走(比例 / 百分比 / 绝对距离)、候选还会按屏幕小格摊开(通道里的并行回线不会把扫描名额占光),线路上还能按同样的位置写法挂图片或 SVG 装饰(`decorations`,通道回线可以用 `linePropsByMember` 逐条配;装饰缺省 `auto` —— 线路上没有拐点时按比例跟随(挂在中点就一直保持中点),有了拐点就跟拐点一样钉在线路上(位置不再沿路径挪动,线路几何一变就把锚点投回线身最近的点),写 `anchor: 'follow'` 强制一直跟随、`anchor: 'fixed'` + `at: [x, y]` 则固定在写死的坐标上;装饰与线路标签都能写 `parallel: true` 跟着线路方向转,通道里的每条回线也能各显示各的标签)。平移 / 缩放 / 拖拽期间两类标签都能各自配 `hideOnInteract`(`true` 或者「标签数超过 N 才收」),收起时只切一个 class、不写标签 DOM,停手自动补齐;线路标签另有 `followOnInteract`(默认开)—— 没收起时拖节点 / 拖拐点期间跟着线路走,关掉则停手后统一归位;通道回线的提示范围用 `tooltip.channelScope` 在整条通道 / 所属分组 / 单条回线之间切。
|
|
8
|
+
- 界面尽量不内置:控制面板、读数、图例都不生成,组件只提供画布与实例方法,样式完全由使用方决定(标签与提示框是覆盖层,默认外观可以整体改写);唯一可选的界面是内置鹰眼图——配 `minimap` 才挂一层小地图(位置、尺寸、节点 / 线路 / 视口框三份样式与交互都可配,`zoomable` 打开后小图自身也能整体放大缩小:空白处拖动可以平移小图、或直接框选让主图显示过去,每格倍率 `zoomStep` 单独配,通道还能只画一根回线;小图同样吃规则——`pointRules` / `lineRules` 决定每个节点 / 每条线的颜色、大小与层级,`levels` 只看某几层、`layered` 控制分层叠画、`pointZoomScale` / `lineZoomScale` 让节点与线路跟着小图倍数放大,不配自己的规则时自动沿用主图规则的颜色与层级),不配不产生任何 DOM。
|
|
9
|
+
- 数据切换与加载信号:`updateData(points, lines, channels)` 一次换完整个场景(也收 `{ points, lines, channels }`;接口列名不一样时先在调用方映射成这三列,见 demo 页签「接口数据」),场站 / 回线按批补用 `addPoints` / `addChannels`(批量通道只重排一次、只记一步历史),`onDataRendered` / `whenRendered` / `whenDataRendered` 给出「这一份数据已经画到屏幕上」的信号 —— loading 遮罩在这之后收,不会闪一帧旧画面。
|
|
10
|
+
- 整包只有一个组件:`gis-view`,只从根入口导出,不预留后续扩展。
|
|
11
|
+
|
|
12
|
+
## 开发
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install
|
|
16
|
+
npm run dev
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
打开终端提示的地址,页面就是 `docs/demos/GisViewDemo.vue`:下拉第一项是仓库根目录 `new.json` 的真实数据(2164 个站点 / 3669 条线路 / 2629 条通道,点位与走向直接照它渲染),也可以切到同在根目录的样例数据 `test.json`(320 个站点 / 449 条线路,按 `channel_id` 聚合成 107 条多回线通道),或切回 900 / 5000 / 10 万的合成数据;真实与样例数据的线色、线宽和站点符号照 `test.js` 的 `getGisStyle` / `getSymbolPath` 分档,转成组件的批量规则表(`docs/demos/gisStyle.js`),关掉「按属性批量配色 / 换符号」就能对比同一份数据的两套外观。演示点击、双击、悬停、拖拽节点、Shift 框选与 Ctrl 点击多选、拐点编辑(加过 / 拖过拐点的通道与线路会归到「新增拐点集合」,读数栏下面直接列出来,点「导出拐点集合」把 `getParallelGroups()` 的结果复制到剪贴板),两端锚在节点上的二 / 三 / 四 / 五回线通道(可切换偏移方式、通道宽度与内收距离,也可以勾「隐藏通道端点节点」看通道与回线一起消失),以及新增的筛选下拉框(电压、站点类型、名称关键字、联动方式与「线路也按电压过滤」)、按属性配色换符号开关(站点符号可选原 SVG 路径或内置方块,路径描边粗细可调)、按电压分层(`zIndex`,高电压等级压在上面)、节点形状(圆 / 正方形 / 圆角方形)、标签位置 / 内容 / HTML 渲染 / 数量 / 候选上限与悬浮提示开关;筛选读数会显示隐藏了多少点 / 线、这次判定走的是 Web Worker 还是主线程、耗时多少。右下角的鹰眼图现在由组件自带(`minimap`):demo 只给尺寸、线路显隐、回线根数、拖动动作、步进、三份样式,以及这一轮新增的规则配色 / 分层开关 / 只看某几层(`levels`)/ 节点与线路跟随小图放大(`pointZoomScale` / `lineZoomScale`)(「可缩放」打开后滚轮与 - / + 按钮按 `zoomStep` 整体放大缩小鹰眼图自身,Ctrl / ⌘ + 滚轮留给主图;空白处拖动按 `dragAction` 平移小图或直接框选,按住 Ctrl / ⌘ 是另一个动作),缩略图画的是筛选后仍可见的节点与线路(含通道回线,关掉「回线」一个通道只画中间那一根),在视口框上按下直接拖框、空白处长按后视角跟随、单击不再改动视角。通道面板下面还有「通道分组」与「定向更新」两行:前者把选中的通道按每组的回线数拆开或合并回去(读数里能看到每个分组几回线、有没有拐点),后者改一个节点 / 线路 / 通道就立刻重渲染相关的视图,演示 `splitChannelGroups` / `mergeChannelGroups` / `moveChannelLine` / `updatePoint` / `updateLine` / `updateChannel` 这几个接口(右键框里每条回线都能单独分配:拆成新分组、或并进第 n 组,两回线拆成两个分组各一条);界面(按钮、滑块、读数浮层)仍然全部写在 demo 里。这一轮把 demo 重新排了一遍:说明段落收进「展开说明」、配置按组折进可折叠面板,画布右上角多了「全屏」按钮(`requestFullscreen` + `resize()`);节点标签默认只标最高电压那一档、线路标签默认不显示(面板上各有一份「显示范围」),新增的「规则更新」面板可以把规则写成 JSON 再点一下重新渲染。这一轮再把面板收进「视图 / 样式 / 数据」三个分页(整块面板栈独立滚动、鹰眼图设置默认折叠),画布默认垫一层由 `docs/demos/demoBackground.js` 现生成的 GeoJSON 底图(街区 + 环线 + 经纬网,可切三档底图与透明度),并新增「初始视图与背景」面板:首屏不再铺满整屏,默认聚焦数据中心放大 3 倍,也能切「适应全部 / 2× / 3×」或自己填中心点与倍数;demo 里还内置了一份插件示例 `focusSelectionPlugin`(按 F 聚焦当前选中),演示 `plugins` 的挂载与随视图销毁。
|
|
20
|
+
通道面板这一轮又多了一块:分组按整条通道的车道错开,拆分 / 合并不会挪动任何一条回线(两回线拆成两组各一条时两根线一左一右分居骨架两侧,四回线拆成两组时四条线还停在原来的位置上),想把某一组拐到别的走廊上就用「加拐点分开分组 / 去掉分开的拐点」按组挪拐点(`addChannelGroupWaypoint` / `removeChannelGroupWaypoint`);右键点通道回线弹出交互框,`onChannelContextMenu` 里给出这条通道有几组、当前是哪一组、每组几条回线与可用交互(点空白处收到 `null` 收框);「新增通道」点两次节点、草稿线跟着光标走,第二个节点点完在框里选「新建通道 / 变成新分组 / 并入某一组」,也可以勾自动提交或直接调 `addChannel`。右键框这一轮又加了「线路分配」:当前分组的每条回线各有一个下拉(拆成新分组 / 并入第 n 组)+「分配」按钮,走 `moveChannelLine`,组数 / 回线数到上限会被拒绝并在读数里给出原因。这一轮右键框再补齐:分组列表点一下就是 `setCurrentChannelGroup`(顺带选中这一组的回线、当前组高亮),下面一排「选中该分组 / 加拐点 / 删拐点 / 拆成多组 / 合并回一组」走 `runChannelAction`(灰掉的按钮悬停能看到原因),Esc / × / 点空白处收框;这两件事都要显式开开关:`features: { drag: true, channelMenu: true }` 与 `waypointEditable: true`(开关缺省全关;通道右键框还要 `contextMenu` 开着,通常给 `features: 'view'` 再加 `channelMenu: true`)。
|
|
21
|
+
|
|
22
|
+
这一轮的 demo 拆成了「一个壳 + 多个标签页」:入口是 `docs/demos/DemoShell.vue` 配 `docs/demos/demos.js`(id / 标题 / 一句说明 / 懒加载的 `component`),切标签页会把 `?demo=<id>` 用 `replaceState` 写回地址栏,收藏一个链接就能直接打开某一页。页签各自只演示一件事(现在是十四个功能页 + 综合示例):`basic` 最小画布与拾取、`labels` 节点与线路标签(拖动 / 点击 / 右键 / 双击 / 批量加标签 API)、`style` 规则配色与符号、`channel` 通道与分组、`events` 事件日志、`copy-merge` 框选类型过滤(节点 / 线路 / 通道任意组合)、只复制选中要素、手动 `addPoint` 落点与自动副本二选一、指定保留来源 / 目标并改写最终信息与样式的合并、剪贴板、`batch-add` 多节点 / 线路 / 通道 / 分组 / 拐点的批量新增、`drag` 拖拽落点、`history` 历史与批量、`samples` 完整样本、`interface` 接口数据、`backend` 编辑与保存、`perf` 大数据与画质档位、`effects` 动画效果,`full` 保留原来那份全功能 demo(缺省页签);共用数据与样式抽在 `docs/demos/shared/demoData.js` / `docs/demos/shared/demoStyle.css`,新增自测页 `docs/steps/selftest-demos.html` 逐个页签挂载一遍。
|
|
23
|
+
|
|
24
|
+
源码本身就是 JS:`src/gis-view/*.js` 逐文件带中文注释,对外类型集中在 `src/gis-view/types.d.ts`(构建时复制成 `dist/index.d.ts`),不用先编译就能直接读。想照着从零写一遍看 [从 0 到完整的实现](docs/从0到完整的实现.md),里面有一份可以照抄的 mini 实现与验收清单。
|
|
25
|
+
|
|
26
|
+
想知道组件到底有哪些属性和方法:[GisView 文档](docs/GisView.md) 的「实例方法」一节把 223 个实例成员(220 个方法 + `el` / `canvas` / `gl`)与 85 个 `options` 键逐个列全;demo 的「视图 · 交互」页签里有同一份清单(`?demo=full`,可搜索、点一条就对当前视图求值);`node docs/steps/check-doc-coverage.mjs` 负责 10 项双向对账,改完源码或文档先跑 `node docs/steps/gen-api-index.mjs` 重生成 demo 读的那一份(三类查询的口径见 [docs/steps/README.md](docs/steps/README.md) 的「怎么查全部属性与方法」)。
|
|
27
|
+
|
|
28
|
+
## 构建 npm 包
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm run build:lib
|
|
32
|
+
npm pack
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`npm pack` 和 `npm publish` 会通过 `prepack` 钩子自动执行库构建,因此即使 `dist/` 中存在旧的应用构建产物,也会在打包前被替换为正确的库产物。
|
|
36
|
+
|
|
37
|
+
npm 压缩包包含 `dist/` 编译产物与 `README.md`、`docs/` 文档。JavaScript 在 Vite 压缩后会进行兼容型轻量混淆,关闭自保护、控制流平坦化等容易影响使用方构建工具的选项。混淆不等于加密,请勿在浏览器组件中存放密钥等敏感信息。
|
|
38
|
+
|
|
39
|
+
## 从 npm 安装
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install gis-view
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import { createGisView } from 'gis-view'
|
|
47
|
+
import 'gis-view/style.css'
|
|
48
|
+
|
|
49
|
+
const view = createGisView('#map', {
|
|
50
|
+
points: [
|
|
51
|
+
{ id: 'station-a', x: 116.397, y: 39.908, name: '望京换流站', selectStyle: { color: '#ffd166', size: 22 } },
|
|
52
|
+
{ id: 'station-b', x: 117.201, y: 39.084, name: '海河变电站' }
|
|
53
|
+
],
|
|
54
|
+
lines: [
|
|
55
|
+
// from / to 绑定点 id:拖拽点时线端点自动跟随
|
|
56
|
+
{ id: 'line-a-b', from: 'station-a', to: 'station-b', name: '京海双回线路' }
|
|
57
|
+
],
|
|
58
|
+
onDblClick: payload => console.log('双击', payload?.item)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
view.fitView().zoomIn()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
样式会随组件的 chunk 自动注入,通常不需要手动引入;特殊构建环境下可以显式引入:
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
import 'gis-view/style.css'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 交互与样式
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
const view = createGisView('#map', {
|
|
74
|
+
// 单击 / 双击命中要素与空白处都会回调,空白处载荷为 null
|
|
75
|
+
onClick: payload => console.log('单击', payload),
|
|
76
|
+
onDblClick: payload => console.log('双击', payload),
|
|
77
|
+
// 悬停 / 选中样式:命中后按字段覆盖要素自身样式
|
|
78
|
+
pointSelectStyle: { size: 22, color: '#ffd166' },
|
|
79
|
+
// sizeScale 是按倍数放大:在节点自己(含规则)定下来的尺寸上再乘一层,大小各异的节点不会被压成一个大小
|
|
80
|
+
pointHoverStyle: { sizeScale: 1.25, strokeColor: '#ffd166', strokeWidth: 2 },
|
|
81
|
+
lineSelectStyle: { width: 5, color: '#57d7ff' },
|
|
82
|
+
// 多选 / 框选样式:缺省与选中样式一致,单个要素可以用 multiSelectStyle / boxSelectStyle 覆盖
|
|
83
|
+
pointMultiSelectStyle: { size: 26, color: '#7ef0c0' },
|
|
84
|
+
pointBoxSelectStyle: { size: 26, color: '#ff9ff3' },
|
|
85
|
+
lineMultiSelectStyle: { width: 6, color: '#7ef0c0' },
|
|
86
|
+
// 整体放大与地图缩放联动:两组系数相乘,zoomScale 为 0 表示不跟随地图缩放
|
|
87
|
+
pointSizeScale: 1,
|
|
88
|
+
pointZoomScale: 0.5,
|
|
89
|
+
// 拖拽点:start / drag / end 三个阶段,载荷带最新坐标、本次移动的全部节点与跟随移动的线路
|
|
90
|
+
onPointDrag: payload => console.log(payload.phase, payload.coordinate, payload.points.length, payload.lines.length),
|
|
91
|
+
// Shift 拖动框选节点:松手时派发一次 onSelect,载荷带选框矩形与框内全部节点
|
|
92
|
+
boxSelect: true,
|
|
93
|
+
onSelect: payload => console.log(payload?.items.length, payload?.box),
|
|
94
|
+
// Ctrl 点击节点可以逐个累加多选,之后拖动其中任意一个节点,整组节点与相关线路同步移动
|
|
95
|
+
// 线路拐点:Ctrl + 点击添加、拖动改线、双击或 Delete 删除,载荷里的 line 是最新数据
|
|
96
|
+
waypointEditable: true,
|
|
97
|
+
// 拐点默认只对选中的线路显示,改 'always' 可以常显
|
|
98
|
+
waypointVisibility: 'selected',
|
|
99
|
+
// 加过拐点的通道 / 线路会进 getParallelGroups(),载荷里的 group_id 就是这个集合的组号
|
|
100
|
+
onWaypointChange: payload => console.log(payload.phase, payload.group_id, payload.line),
|
|
101
|
+
// 通道:回线照常写进 lines,lineIds 的顺序即回线顺序;它们共用一个骨架,偏移按宽度均分,拖动拐点整条通道一起折线
|
|
102
|
+
// 两端用 from / to 绑在节点上:两端必须有节点通道才显示,拖动节点两端跟着走
|
|
103
|
+
// 回线多的时候不用手写:createChannels([{ from, to, circuits, path }]) 直接按声明数组生成 lines 与 channels
|
|
104
|
+
channels: [{ id: 'corridor-1', lineIds: ['line-a-b', 'line-a-b-2'], from: 'station-a', to: 'station-b', path: [[116.7, 39.6]] }],
|
|
105
|
+
channelWidth: 0.14, // 通道宽度的世界单位写法:最外侧两条回线中心的间距(nodeWidth 为 0 时才用)
|
|
106
|
+
channelInset: 0.12, // offsetMode: 'inset' 时从切点向内收的比例(占通道宽度,最多 0.42)
|
|
107
|
+
channelNodeWidth: 1, // 通道宽度按两端节点尺寸取:1(默认)表示回线正好铺在节点两边、节点放大回线跟着变宽,0 表示退回上面的世界单位写法
|
|
108
|
+
channelTangentInset: null, // 两端切点往节点内收的 CSS 像素:null(默认)按回线线宽收,0 表示切点正好落在节点边上
|
|
109
|
+
// 通道分组:一条通道默认一组,拆分 / 合并都走实例方法 —— 新拆出来的分组不带拐点,
|
|
110
|
+
// 被并入的线路跟随目标分组的拐点(目标没有拐点就一条都不带),加点 / 拖点只作用于同一分组
|
|
111
|
+
// 偏移按整条通道的车道排:拆分 / 合并只换骨架、不挪回线,两回线拆成两组各一条时两根线一左一右
|
|
112
|
+
// view.splitChannelGroups('corridor-1', [['line-a-b'], ['line-a-b-2']])
|
|
113
|
+
// view.mergeChannelGroups('corridor-1', 'corridor-1::2', 'corridor-1')
|
|
114
|
+
// 按回线自由分配:把一条回线单独拆成新分组(两回线就是每组一条),或并进通道里的另一组
|
|
115
|
+
// view.moveChannelLine('corridor-1', 'line-a-b-2', { type: 'newGroup' })
|
|
116
|
+
// view.moveChannelLine('corridor-1', 'line-a-b-2', { type: 'joinGroup', groupId: 'corridor-1::2' })
|
|
117
|
+
// 把某一组拉走:拆分之后各组已经错开,想把某一组拐到别的走廊上就给第 2…n 组加一个拐点(第 1 组原地不动)
|
|
118
|
+
// view.addChannelGroupWaypoint('corridor-1', 'corridor-1::2')
|
|
119
|
+
// view.removeChannelGroupWaypoint('corridor-1', 'corridor-1::2')
|
|
120
|
+
// 右键通道回线:onChannelContextMenu 给出「几组 / 当前哪组 / 每组几条回线与可用交互」,点空白处收到 null,用它收框
|
|
121
|
+
// onChannelContextMenu: (context, event) => { if (context) view.setCurrentChannelGroup(context.channelId, context.groups[1].id) }
|
|
122
|
+
// view.runChannelAction('add-waypoint', { channelId: 'corridor-1', groupId: 'corridor-1::2' })
|
|
123
|
+
// 新增通道:点两个节点出草稿(onChannelDraft 给两个节点的信息与可选方案),或绕开交互直接加一条
|
|
124
|
+
// view.beginAddChannel({ autoCommit: true }); view.addChannel({ from: 'station-a', to: 'station-b' })
|
|
125
|
+
// 定向更新:改一个节点 / 线路 / 通道并重渲染,只重排受影响的那一小片
|
|
126
|
+
// view.updatePoint('station-a', { x: 116.42 }).updateLine('line-a-b', { width: 6 }).updateChannel('corridor-1', { width: 0.2 })
|
|
127
|
+
// 全局开关,也可以逐点 / 逐线用 clickable、draggable 覆盖
|
|
128
|
+
clickable: true,
|
|
129
|
+
draggable: true,
|
|
130
|
+
// 拖动节点时线路是否实时跟随:默认 true(拖动过程中端点一路跟着走);
|
|
131
|
+
// 关掉之后拖动期间线路原地不动,松手那一下才更新到新位置
|
|
132
|
+
liveDrag: true
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
状态样式按字段覆盖,`selectStyle: {}` 表示命中时不改变外观;不配样式就什么都不画(悬停仍然触发回调与提示、选中仍然进选中集合),要退回旧的「尺寸 / 宽度增幅加一圈强调环」就把 `defaultHighlight` 设为 `true`。框选与 Ctrl 点击得到同一份选中集合,用 `getSelections()` / `getSelectedIds()` 读回,也可以用 `select(id | id[])` 受控设置选中、`selectInBox(box)` 直接框选,多选之后拖动任意一个被选中的节点就是整组移动。拐点写在线路的 `path` 里,拖动或增删之后 `getData()` 与 `onWaypointChange` 的 `line` 都是最新数据;通道内的回线把拐点挂在共用骨架上,改一条就是改整条通道,`getWaypoints()` 读当前显示的拐点、`getChannels()` 读骨架 / 偏移 / 每条回线的折线;被加过 / 拖过拐点的通道与线路还会归到 `getParallelGroups()` 的「新增拐点集合」里,结构对齐 `test.json` 的 `parallel_groups`(一条通道一组、回线列在 `member_line_ids`、`group_route` 是它们共用的骨架),删掉最后一个拐点这条组就退出集合。拖拽点时,`from` / `to` 绑定的线与坐标相同的线都会跟着移动。 多选(Ctrl + 点击)与框选(Shift + 拖动)各有自己的样式:`pointMultiSelectStyle` / `pointBoxSelectStyle` / `lineMultiSelectStyle` 是整体配置,要素自己的 `multiSelectStyle` / `boxSelectStyle` 优先,两者都没配就回落到选中样式。节点尺寸与线宽各由两组独立系数控制:`pointSizeScale` / `lineWidthScale` 是与地图无关的整体放大,`pointZoomScale` / `lineZoomScale` 是地图缩放的联动指数(0 表示不跟随,基准是 `zoomBaseScale` 或首次适应视图时的比例),实际绘制尺寸是两者相乘,整体放大不会被联动再放大一次,拾取容差跟着一起放大。`size` 与 `sizeScale` 的关系是「尺寸 × 倍数」:`size` 是节点的宽度(正方形取边长,圆的直径按面积对齐正方形、等于宽度的 `2 / √π` 倍,同一个 `size` 下圆与方摆在一起不会一大一小),规则与状态样式里的 `sizeScale` 依次往这条链上乘,因此「电压越高越大 + 悬停统一放大 1.25 倍」这类组合可以各配各的。
|
|
137
|
+
|
|
138
|
+
回线数量多时用 `createChannels` 组装:它按 `circuits` 把一条通道声明展开成多条只绑两端节点的回线,并生成共用的通道对象(id、名称、配色都可以按序覆盖),返回的 `{ lines, channels }` 直接交给 `createGisView` / `updateOptions`:
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
import { createChannels, createGisView } from 'gis-view'
|
|
142
|
+
|
|
143
|
+
const { lines, channels } = createChannels(
|
|
144
|
+
[
|
|
145
|
+
{ id: 'corridor-1', from: 'station-a', to: 'station-b', circuits: 2, path: [[116.7, 39.6]] },
|
|
146
|
+
{ from: 'station-c', to: 'station-d', circuits: 5, width: 0.24, offsetMode: 'inset', inset: 0.05 }
|
|
147
|
+
],
|
|
148
|
+
{ lineWidth: 2 }
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
const view = createGisView('#map', { points, lines, channels })
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 筛选、批量规则、标签与提示
|
|
155
|
+
|
|
156
|
+
```js
|
|
157
|
+
const view = createGisView('#map', {
|
|
158
|
+
points,
|
|
159
|
+
lines,
|
|
160
|
+
// 批量规则:按 vn_kv 定颜色与大小,按 station_type 换符号(后命中的规则覆盖前面的,只补要素没写的字段)
|
|
161
|
+
pointRules: [
|
|
162
|
+
{ when: { vn_kv: 1050 }, style: { color: '#e0592e', size: 18 } },
|
|
163
|
+
{ when: { vn_kv: 525 }, style: { color: '#e0a92e', size: 15 } },
|
|
164
|
+
{ when: { station_type: 'wind' }, style: { symbol: '/wind.svg' } }
|
|
165
|
+
],
|
|
166
|
+
lineRules: [{ when: { vn_kv: { gte: 525 } }, style: { color: '#e0a92e', width: 1.8 } }],
|
|
167
|
+
// 节点标签:内容、位置、数量与显隐都可配
|
|
168
|
+
// 一个节点上多条标签:label 写成数组,每条各自有内容 / 位置 / 样式 / 显隐(id 与数据里的 labels、updateLabel / removeLabel 对上号)
|
|
169
|
+
// 节点标签:内容、位置、贴边方式、数量与显隐都可配;avoidOverlap 默认开着,会和已放下的标签重叠的直接不画
|
|
170
|
+
// anchor: 'edge'(默认)让间距从符号外沿量起,节点放大标签跟着往外让;html: true 时字符串按 HTML 渲染(模板取值始终转义)
|
|
171
|
+
// scanLimit 是每帧最多考察多少个视口内的节点(缺省按 maxCount × 8 估算、不低于 256,0 表示不限),十万级数据里它是帧率的关键开关
|
|
172
|
+
// draggable / editable:标签能拖走(偏移记在组件里,再移动节点时叠在节点位移上)、能点选(发 labelClick,改内容 / 字号 / 颜色由使用方调 updateLabel 落)
|
|
173
|
+
label: { content: '{name} · {vn_kv} kV', position: 'right', anchor: 'edge', gap: 8, maxCount: 32, avoidOverlap: true, overlapGap: 2, scanLimit: 256, draggable: true, editable: true, when: { vn_kv: { gte: 500 } } },
|
|
174
|
+
// 线路标签:和节点标签完全分开配;位置沿线走(0.5 = 中点),avoidOverlap 与节点标签共用避让网格
|
|
175
|
+
// 一条线上多条标签同理:lineLabel 也可以写数组,每条各自的位置 / 样式 / parallel
|
|
176
|
+
// scanLimit 限制的是「收下的候选数」,候选先按屏幕小格筛一遍,同一小片区域只留一条线;hideOnInteract 让交互期整层收起
|
|
177
|
+
lineLabel: { content: '{name}', position: 0.5, parallel: false, maxCount: 24, avoidOverlap: true, hideOnInteract: true, followOnInteract: true, draggable: true, editable: true, when: { vn_kv: { gte: 500 } } },
|
|
178
|
+
// 悬停提示:点 / 线 / 通道分别配置
|
|
179
|
+
tooltip: {
|
|
180
|
+
point: '{name}({station_type})\n电压等级 {vn_kv} kV',
|
|
181
|
+
line: '线路 {name} · {vn_kv} kV',
|
|
182
|
+
channel: '通道 {name} · 共 {circuits} 回'
|
|
183
|
+
},
|
|
184
|
+
// 筛选:节点被过滤时,两端节点没了的线路与通道回线一起消失
|
|
185
|
+
filter: { points: { vn_kv: { gte: 525 } } },
|
|
186
|
+
// 大数据下把判定放进 Web Worker:点 + 线超过 2 万才用,条件里带函数时自动退回主线程
|
|
187
|
+
filterWorker: { threshold: 20000 },
|
|
188
|
+
onFilter: result => console.log(result.hiddenPoints.length, result.visiblePoints, result.worker, result.durationMs)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
// 运行期改条件,或按属性整批换配色
|
|
192
|
+
view.setFilter({ points: { station_type: 'coal' }, lines: { vn_kv: { gte: 525 } }, cascade: 'binding' })
|
|
193
|
+
view.updateOptions({ pointRules: [...], label: { enabled: false } })
|
|
194
|
+
// 标签的拖动与编辑:选中一条、改内容 / 字号 / 颜色,或者恢复默认
|
|
195
|
+
view.on('labelClick', payload => view.updateLabel(payload.kind, payload.id, { content: '新名字', style: { fontSize: '16px', color: '#e056a0' } }))
|
|
196
|
+
view.resetLabel('point', 'p-1') // 这一条的偏移 + 覆盖一起清掉;resetLabels() 是全部恢复默认
|
|
197
|
+
// 多条标签:数据里写 labels(导出再导入即回显),或者用接口加 / 改 / 删
|
|
198
|
+
view.addLabel('point', 'p-1', { content: '备注', position: 'bottom' }) // id 自动 note1 / note2…
|
|
199
|
+
view.updateLabel('point', 'p-1', { labelId: 'volt', content: '220 kV' }) // 只改 volt 那一条
|
|
200
|
+
view.getLabels('point', 'p-1') // 这个节点上现在有哪些标签(每条带 source:配置 / 数据 / 运行时加的,自建「标签列表」面板用它)
|
|
201
|
+
view.setLabelStates(view.getLabelStates()) // 持久化结果整份灌回来(含运行时加的标签与拖出来的偏移)
|
|
202
|
+
|
|
203
|
+
// 点标签的载荷:label 是这一条标签自己(id / content / source / 合并后的位置与样式),parent 是它挂在哪条要素
|
|
204
|
+
// 线路在通道里时 parent.channel 给 { id, groupId, index, slot, item } ——「点的是哪条通道的哪一回线」直接读
|
|
205
|
+
view.on('labelClick', payload => console.log(payload.label.id, payload.label.content, payload.parent.channel?.groupId))
|
|
206
|
+
// 右键默认把浏览器的系统菜单收掉(onContextMenu / onChannelContextMenu 照常发);想留系统菜单写 false
|
|
207
|
+
view.updateOptions({ preventContextMenu: false })
|
|
208
|
+
|
|
209
|
+
// 导出带 labels 列(缺省 'auto' 只给有标签要说的记录带):记录原样喂回 points / lines 就能回显
|
|
210
|
+
const rows = view.exportData({ parts: ['station'], labels: true }).points
|
|
211
|
+
view.clearFilter()
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
搜索与相机定位(搜索只找不藏,筛选才负责隐藏):
|
|
215
|
+
|
|
216
|
+
```js
|
|
217
|
+
// 搜索:scope 可以是 points / lines / channels,通道会连成员回线一起搜
|
|
218
|
+
const result = view.search({ keyword: '望京', scope: 'channels', limit: 50 })
|
|
219
|
+
view.focusOn(result.hits[0]) // 命中、要素 id、{ type, index }、坐标或矩形都能直接定位
|
|
220
|
+
|
|
221
|
+
// 相机:zoomTo 直接给比例,zoomToBounds 把一段世界坐标范围适应到视图内
|
|
222
|
+
view.zoomTo(12).zoomToBounds({ minX: 116, minY: 39, maxX: 117, maxY: 40 }, 60)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
条件三种写法可以混用:函数、显式条件(`{ field, op, value, not }`)与简写映射(`{ vn_kv: { gte: 525 } }`,数组表示「并且」);运算符有 `eq`、`ne`、`gt`、`gte`、`lt`、`lte`、`in`、`nin`、`contains`、`startsWith`、`endsWith`、`between`、`exists`、`regex`,字段路径支持 `props.vnkv` 这类嵌套取值。`createConditionMatcher(condition)` 可以单独拿去过滤业务数组。筛选的 `cascade` 决定节点被过滤后线路怎么联动(`'binding'` 按 `from` / `to`、`'coordinate'` 按端点坐标、`'both'` 两者都算、`false` 不联动)。
|
|
226
|
+
|
|
227
|
+
## 组件文档与示例
|
|
228
|
+
|
|
229
|
+
- [GisView 文档](docs/GisView.md):用法与参考全在这一份 —— 安装引入、最小示例、坐标与相机、数据字段、options 全表、线型 `lineType` / `dash`、条件语言、光标 `cursorStyle`、交互、标签与装饰、筛选搜索、通道与多回线、鹰眼图、实例方法与回调全表、导出图片、标准数据文档、渲染后端(WebGPU 开关)与大数据开关,末尾附常见问题与类型索引
|
|
230
|
+
- [从 0 到完整的实现](docs/从0到完整的实现.md):按「先画一个点、再画一条线」的顺序把整库重写一遍,含 WebGL2 原理、代码与踩坑记录
|
|
231
|
+
- [从 0 手写 gis-view:分步 demo](docs/steps/README.md):十一个递增的文件(`docs/steps/step-01-canvas.js` ~ `step-11-background.js`),从一块空画布一路长到 `createGisView`,最后一步补上背景层与初始视图;`docs/steps/index.html` 可切步骤对照读数与事件日志,新增通道的每一次点击(起点 / 终点)、连续新增、落地 / 取消 / 移除、第 11 步的「切换底图 / 切换初始视图」都能在页面上直接试
|
|
232
|
+
- [GisView 实现原理](docs/GisView实现原理.md):只讲「怎么实现」—— 文件职责与数据流、从 `createGisView` 到第一帧、渲染管线与着色器、相机与像素、拾取、通道骨架与增量更新、虚线图案表、光标状态机、规则与状态样式、覆盖层、筛选与搜索 Worker、鹰眼图、性能改动与还能怎么优化(第 16 章现在是 22 条方案,逐条标收益 / 成本;10 万点 / 1 万线的底数表之后另有两张改前 / 改后的实测表,16.13 / 16.17 / 16.18 是上一轮落地的,16.14 / 16.15 / 16.16 与 16.20 里的搜索这一件是这一轮落地的),第 18 章是各轮优化的完整实测与 WebGPU 取舍
|
|
233
|
+
- [数据格式](docs/数据格式.md):喂进来的节点 / 线路 / 通道 / 标签 / 拐点分组逐字段怎么填、条件语法与运算符表、五套导出格式(`view` / `new` / `test` / `input` / `full` —— 后一种是「接收 == 导出」的标准数据文档)与常用选项、事件载荷速查、仓库里的示例数据怎么用 —— 自建面板与后端约定数据时先看这一份
|
|
234
|
+
- [sample-document 字段详解](docs/sample-document字段详解.md):仓库根目录 `sample-document.json` 的逐字段讲解 —— 先回答 `labelStates`(标签的运行时状态)、`channels[].groups[].path`(分组骨架的中间拐点)、`lines[].path` 的拐点(整条折线,首尾是端点)三个高频问题,再按文件顺序把八列(`format` / `version` / `points` / `lines` / `channels` / `labelStates` / `view` / `filter`)逐条讲一遍,末尾附 154 个字段路径的自动对账;图形化的交互版见 [docs/guide/sample-document.html](docs/guide/sample-document.html),所有 markdown 都能在文档中心 [docs/index.html](docs/index.html) 里读
|
|
235
|
+
- [AI 对话汇总](docs/AI对话汇总.md):本机跟本组件有关的 39 个 AI 会话按内容整理(选型 / 通道分组拐点 / 交互 / 样式 / 性能 / 数据接口 / 文档工程 / 旁支集成),每条给出日期、用户要了什么与落点;第五节「逐会话问答」把 39 个会话 194 轮的问题原文与回答要点都写了出来
|
|
236
|
+
|
|
237
|
+
## 说明
|
|
238
|
+
|
|
239
|
+
- 没有 Vue 组件形态:`createGisView` 接收 DOM 元素或选择器,在容器内插入 `.gis-view` 根节点与 canvas,`destroy()` 时只移除自己插入的节点。
|
|
240
|
+
- 点支持圆形、正方形(`radius` 给圆角,0 就是直角方形)、SVG 路径与图片符号,前两种由着色器按距离场直接画,缩放与整体放大都不会糊,可单独配置大小、颜色、描边、透明度与阴影;`size` 是节点的宽度,圆按面积对齐正方形(直径 = 宽度 × `2 / √π`),同一个 `size` 下圆与方的视觉重量一致;线支持颜色、粗细、透明度、端点、折线 `path` 与阴影。
|
|
241
|
+
- 背景层支持底色、图片(`bounds` / `fit` / `opacity` / `shadow`)与 GeoJSON(填充色、填充透明度、描边色、线宽、点颜色与大小、整体透明度、阴影)。
|
|
242
|
+
- 通道与多回线:`channels` 把若干条线路组织成并行敷设的走廊,回线几何由共用骨架按偏移推导,二 / 三 / 四 / 五回线都能等距排开;偏移方式可选最外侧切点均分(`tangent`)或从切点再向内收一段距离(`inset`),拖拐点整条通道同步折线;一条通道默认一个分组,可以拆成多组(每组至少一条回线,新拆出来的组不带拐点)再合并回去(被并入的线路跟随目标分组的拐点,目标没有拐点就一条都不带),加点 / 拖点只作用于同一分组,见 docs/GisView.md 的「通道分组」。通道两端可以用 `from` / `to` 绑在节点上:两端都有节点才显示,拖动节点两端跟着走,节点没了通道与回线一起消失;两端的切点还会默认往节点里收一个线宽(`channelTangentInset`,`0` 表示正好落在节点边上),线不会从节点边缘探出去。
|
|
243
|
+
- 筛选只改表现不改数据:条件里出现的字段会被投影成扁平数组(`fields` 由条件语言静态提取),`getVisibleData()` 给使用方拿过滤后的数据、`listPoints({ visibleOnly: true })` 给可见节点快照,`onFilter` 回报隐藏下标、可见数量、是否用了 Worker 与耗时;通道两端锚在节点上,端点被过滤时整条通道与回线一起消失。
|
|
244
|
+
- 大数据量交互不吃亏:点的拾取与框选走随数据范围重建的均匀网格索引,线也走随线段重建的网格索引,每次只比较光标附近的候选,耗时与总量基本脱钩,读数可以直接看 `getStats().pickMs`;悬停、选中、拖拽都只做增量解析与增量上传,拐点默认只显示选中线路的,10 万点 / 10 万线的交互量与要素总量基本无关。
|
|
245
|
+
- 10 万级交互的专项优化:框选的选中集合用 `Set` 做差集(避免平方级比较),整组拖动先按实例槽位排序再按「连续槽位段」合并上传实例数据(十万次 `bufferSubData` 变成几十次,贴图符号点也走自己的槽位表,符号没换就地改位置)、拖动大组时圆头批次改为松手后分摊补齐(拖动期间一个字节都不传),按住拖动 / 滚轮缩放期间自动切轻量渲染(跳过阴影、强调环与高亮重算,停手 160 ms 后补一帧全质量);标签图层在交互期只做一次整体 `transform`、停手 180 ms 后才真实排版,拖点只挪动过的那几个标签;框选抬起时候选下标先按数值排序、差值去重键用数值、圆 / 方符号解析有热缓存、可见点缓存按需作废。demo 里的统计面板按最小间隔重画,避免每帧写响应式数据。这一轮又补了三处:初始化按到视图中心的距离发散渲染(10 万点首屏 1 帧内先出中心一片、3 帧内铺满)、整组拖动在 2048 个节点以上改成就地改坐标并复用事件载荷(10 万点整组拖动每帧 28 ms → 11 ms)、标签在连续平移里每 220 ms 补一次真实排版并把位置取整到像素(拖动中标签不再越拖越空,静止时文字不糊)。
|
|
246
|
+
- 整组拖动的增量补丁这一轮补齐到每个实例批次:10 万点 / 10 万线整屏框选后连续拖动,每帧补丁 1391 ms → 339 ms(圆头那部分 8 帧合计 9.8 s 在拖动期间归零,松手后 4 帧内分摊补完);规则条件的运算符结果按值记忆,`vn_kv >= 500` 这类分档条件在 10 万级数据上从逐要素求值变成查表。
|
|
247
|
+
- 这一轮补上三处:拖动节点的线路跟随做成开关(`liveDrag: false` 时拖动期间只画节点、线路与通道原地不动,松手那一下一次算完并上传);节点标签在缩放 / 悬停 / 选中时按相机重投影、间距恒定取「符号半径 + `gap`」(不再被地图缩放一路拉出画布,也不会有「鹰眼图缩放几次主视图标签就不见了」);`sizeScale` 让悬停 / 选中样式按倍数放大(大小各异的节点各自按比例变大,规则里配的倍数不会被顶掉)。
|
|
248
|
+
- 这一轮(鹰眼图与缩放联动):小图开了「可缩放」之后,空白处拖动可以直接平移小图内容、或拉出矩形框选让主图显示过去(`dragAction` 与 Ctrl / ⌘ 各管一个动作,平移过内容就不再被自动拽回视口框),每一格放大 / 缩小的倍率由 `zoomStep` 单独配(`zoomInStep` / `zoomOutStep` 还能分别覆盖),`channelCircuits: false` 时一个通道只画偏移最靠中间的那一根回线(多回线挤在小图上只会糊成一片);缩放联动的两组系数分工写明:`pointSizeScale` / `lineWidthScale` 是与地图无关的常数倍数,`pointZoomScale` / `lineZoomScale` 是跟随地图缩放的指数(0 不跟随、1 同倍率),节点尺寸一变,按节点尺寸取宽的通道回线位置跟着走;只有一条回线的通道不再计算回线偏移(回线就是骨架),缩放时的宽度重算与实例重传整块跳过,真实数据里这类通道占大多数。
|
|
249
|
+
- 这一轮(通道分组、鹰眼图规则与定向更新):一条通道不再只能是「一整组」——`splitChannelGroups` 把它拆成多个分组、`mergeChannelGroups` 再并回来,每组回线数自己定、最少一条,拆分出来的新分组不带走拐点、合并进来的线路跟随目标分组的拐点(目标没有拐点就一条都不带),加点 / 拖点只作用于同一分组,分组结果写回 `channels[].groups` 并参与 `getParallelGroups` 的组号;再细到单条回线的 `moveChannelLine` 把某一条回线单独拆成新分组或并进别的分组(两回线拆成两个分组各一条,demo 的右键框里每条回线都有一个「拆成新分组 / 并入第 n 组」的下拉 + 分配按钮,组数 / 回线数到上限时拒绝并给出原因);鹰眼图补上了规则(`pointRules` / `lineRules` 的颜色、半径 / 线宽、形状与层级,不配自己的规则时沿用主图规则的颜色与层级,主图的像素尺寸不会搬过来),配合 `levels` 只看某几层、`layered` 控制分层叠画、`pointZoomScale` / `lineZoomScale` 让节点与线路跟着小图倍数放大,底图按层级分箱、按「层级 + 颜色 + 线宽 + 透明度」分桶,绘制次数不随规则数量膨胀;定向更新补上 `updatePoint` / `updateLine` / `updateChannel`:只重解析受影响的那一小片(节点带着绑在它身上的线与锚定通道、线路只重排自己、通道按成员表比对后决定补槽位还是整份重解析),其余要素与缓存一个字节都不动,鹰眼图与标签覆盖层跟着重画。
|
|
250
|
+
- 发散渲染与导出:2 万点以上默认从视图中心向外分帧铺开(`progressiveInit`,铺完前 `isLayoutReady()` 返回 `false`,标签与拾取只看已经画出去的那部分),首屏一帧内先出中心附近的一片;`exportImage({ scale, background, download })` 把画布内容与标签合成一张图导出(返回 data URL,`download: true` 直接下载)。
|
|
251
|
+
- 这一轮(指针事件、连续新增与移除新增):鼠标的每个动作都有自己的回调 —— 悬停 `onHover`、左键单击 `onClick`、双击 `onDblClick`,外加通用的 `onPointerDown` / `onPointerUp`(左 / 中 / 右键按下与抬起)与 `onContextMenu`(每一次右键),后三者发同一种 `GisViewPointerPayload`:命中的要素(`type` / `index` / `item`,通道回线还带 `channelId` / `groupId` / `channelIndex`)、按钮(`button` / `buttons`)与光标位置(`world` / `x` / `y` / `clientX` / `clientY`),空白处 `hit` 为 false,要自己弹菜单就在回调里 `preventDefault()`;新增通道补上「每点一次都回调」的 `onChannelDraftClick`(`step` 为 1 是拿到起点、2 是拿到终点,载荷与 `onChannelDraft` 一致)与连续新增:`beginAddChannel({ continuous: true })` 落地之后草稿原地重开(发 `phase: 'restart'`),接着点下一对节点就能一直加,`continuous: 'chain'` 再以上一段的终点当下一段起点,`continueAddChannel()` 手动开下一段、`cancelAddChannel()` 退出草稿;新增错了用 `removeLine(lineId)` 把刚落地的那条回线摘掉(通道声明一起改,通道空了整条删掉),整条通道走 `removeChannel(channelId)`,两者都会把选中下标跟着往前挪。
|
|
252
|
+
- 性能实测与 WebGPU 的结论见 [GisView 实现原理](docs/GisView实现原理.md) 第 18 章:10 万点 / 10 万线的连续平移,标签全开最初只有 0.8 FPS、关掉标签 10.3 FPS;加上标签候选上限(`label.scanLimit`)回到 36.5 / 48.9 FPS,再把标签图层做成「交互期整体变换 + 停手后重排」,平移 / 缩放 / 拖点分别到 57.9 / 57.9 / 55.0 FPS(与关掉标签同档),整屏框选约 9.9 万个节点从 97 ms 降到 52 ms;整组拖动的增量补丁与圆头欠账见效之后,10 万选中连续拖动每帧 1391 ms → 339 ms;引擎每帧渲染函数耗时一直只有 0.05 ~ 0.2 ms。瓶颈在 DOM 覆盖层与 JS 侧,换 WebGPU 省不到这笔开销,本轮不迁移;这一轮把它收成了 `renderer` 开关(`auto` / `webgl2` / `webgpu`)与 `getRendererInfo()` 读数,真要迁移的路线写在文档里。
|
|
253
|
+
- 搜索与定位和筛选分开:`search()` 只找不藏(可限定 `points` / `lines` / `channels`,通道连回线一起搜并给出 `memberIndex` / `memberLineIndex`,见 `docs/GisView.md`),**但不再自己动视角** —— 搜到之后要不要聚焦、要不要选中由使用方决定(`GisViewDemo` 的搜索面板就是一个「搜到就聚焦 + 选中」的勾选框);`focusOn(target, options)` 定位到命中 / id / 坐标 / 矩形(给数组就是一批整体居中,`{ center: true }` 只居中,`select: true` 顺带选中),相机另有 `zoomTo(scale, origin?)` 与 `zoomToBounds(bounds, padding?)`。
|
|
254
|
+
- 预览能力:鹰眼图是唯一内置的界面(配 `minimap` 才有,见 `docs/GisView.md` 的「鹰眼图」;底图先铺线、再把节点整层盖上去,节点层级高于线路;小图自己也能套规则:`pointRules` / `lineRules` 给每个节点 / 每条线定颜色、大小与层级,`levels` 只画某几层、`layered` 分层叠画、`pointZoomScale` / `lineZoomScale` 跟着小图倍数放大,没配自己的规则就用主图的颜色与层级),要更自由的实现仍然交给使用方——`getBounds()` 给全量范围、`getViewRect()` 给当前视口矩形,在小画布上按比例画出来并回写 `setCamera()`,需要缩放与框选放大时用 `project` / `unproject` 在两端换算即可。
|
|
255
|
+
- 这一轮(demo 整理、标签显隐规则、全屏与规则更新):demo 页面重新排成「左边画布、右边配置」两列,说明段落默认收起、配置按组折进可折叠面板,画布右上角的「全屏」按钮走 `requestFullscreen()`(进出都等布局稳定后 `view.resize()`,按 Esc 退出也认);节点标签默认只给电压最高的那一档打(阈值取当前数据里最高的 `vn_kv`,new.json / test.json 都是 1050kV),面板上的「显示范围」还能切 ≥525kV 与全部;线路标签默认整个不显示,勾上「显示」之后再按同一套范围展开,打开 demo 不再满屏标签。新增「规则更新」面板:`pointRules` / `lineRules` 可以在文本框里直接改 JSON(或选面板配置 / 单色 / 高电压突出三份预设),点「更新规则并重渲染」把新规则表交给 `updateOptions`,读数里给出规则条数与整份重排耗时 —— 自定义规则生效期间面板上的样式开关不再抢着下发规则。
|
|
256
|
+
- 这一轮(demo 分页与底图、初始视图、插件与三处性能优化):`initialView` 决定首次加载的相机落位(`{ mode, center, scale, zoom, bounds, padding }`,不配仍是适应数据范围),实例方法 `applyInitialView()` 在换数据 / 改配置之后重新套用同一份落位,见 `docs/GisView.md` 的「初始视图」;`plugins: [view => hook]` 是插件挂载点 —— 初始化按数组顺序执行,返回的 `destroy` 在视图销毁时倒序回调,单个插件抛错只走 `onError`,demo 里的 F 键聚焦就是一份完整示例;性能上 `drawGrid` 复用顶点缓冲(范围 / 步长 / 颜色不变就跳过重建)、`onStats` 按新增的 `statsInterval`(默认 250 ms)节流、悬停拾取合并到一帧一次。`docs/steps` 新增第 11 步 `step-11-background.js` 演示背景层 + 初始视图,步骤页工具条上多了「切换底图 / 切换初始视图」两个按钮。
|
|
257
|
+
- 这一轮(背景图与鹰眼图背景):背景层的图片来源扩到「URL / dataURL / `<img>` / `<canvas>` / `<svg>` 元素 / 只给 SVG 路径的 `d` 字符串」(路径会先量外框再包成 SVG,可用 `pathStyle` 上色),摆放除了 `bounds` + `fit`(`contain` / `cover` / `stretch` / `center`,`size` 给原尺寸)之外还能给 `corners` 四个角的世界坐标,按任意四边形做仿射 + 裁剪贴图;GeoJSON 补上 ECharts 风格的 `aspectScale`(默认 0.75,只压经度方向,相机与 `fitView` 仍用缩放前的 `rawBounds` 真实经纬度)、`nameProperty`、`label` 区域文字(开关 / 模板 / 字号 / 字重 / 颜色 / 偏移 / 透明度,位置取区域重心)与 `regions` / `itemStyle(params)` 逐区域配色,填充与描边的颜色、宽度都能逐区域覆盖,`getResolvedBackground()` / `getBackgroundLabels()` 把解析结果暴露出来给自建缩略图或图例用。鹰眼图背景默认继承主视图(`minimap.background: 'inherit'`:底色 / 图片 / GeoJSON 与区域文字一起缩着画,文字字号按比例缩放后夹在 5 ~ 12px),也可以给 `minimap.background` 单独配一份、用 `minimap.backgroundStyle` 单独定容器底色,或在主视图换背景时自动跟着重画;demo 的「背景来源」下拉把现生成 GeoJSON / 全省 GeoJSON(真实经纬度,`public/全省.geojson`)/ dataURL 图片 / 运行时 canvas / SVG 路径五种来源摆在一起对比,`docs/steps/step-11-background.js` 与三个自测页同步补了断言。
|
|
258
|
+
- 发布前请在 `package.json` 中更新包名、版本号和仓库信息。如果包名已被占用,可以使用 npm 作用域,例如 `@your-scope/gis-view`。
|
|
259
|
+
- 这一轮(坐标转换与鹰眼图范围):新增 `projection.js` 的坐标层 —— `createGeoProjection` 给出「经纬度 ↔ 画出来的世界坐标」(含 `aspectScale`:只压经度、绕整体 bbox 中心,`rawBounds` 给相机、`bounds` 给绘制),`createGeoMapper` 把数据搬到 GeoJSON 上(`mode: 'lngLat'` 完全贴合 / `'fit'` 等比铺进底图 / `'scale'` 按比例,`scale` / `scaleX` / `scaleY` 就是「节点与 geojson 的大小比例」,`mapPoint` / `mapLine` / `mapChannel` / `mapData` 都是复制一份再换坐标);实例方法补上 `getGeoProjection` / `lngLatToWorld` / `worldToLngLat` / `lngLatToScreen` / `screenToLngLat` / `getPointer` / `createGeoMapper` / `mapDataToGeoJson`,`onPointer` 与各类拾取载荷在配了 GeoJSON 底图时给出 `lngLat`。鹰眼图新增 `minimap.extent`(默认 `auto` = 可见数据范围 ∪ 底图范围,GeoJSON 取画出来的 `bounds`、图片取四角 quad):数据与底图不在一个坐标系时小图不再把底图缩成一个点,`data` 是旧行为、`background` 只看底图。demo 的「数据映射」下拉 + 「节点 / geojson 比例」滑块 + 「坐标换算」输入框把这几项摆在一起,`docs/steps/selftest-*.html` 同步补了断言。
|
|
260
|
+
- 这一轮(事件里的选中态、选中集读数与要素、导出数据分四份):`onClick` / `onContextMenu` / `onPointerDown` / `onPointerUp` 的载荷里直接带 `selected`(这一刻在不在选中集里)、`wasSelected`(`onClick` 给「点之前」的那一份,右键与按下 / 松开是「当下」)与 `selection` 读数 `{ ids, count, points, lines, channels }`(通道回线按通道算一个),左键分支与右键自建面板不必再比对 `getSelections()`;`getSelectionData()` 一份读数全给(`ids` / `count` / `pointCount` / `lineCount` / `channelCount`,要素本身 `points` / `lines` / `channels`(数据里的对象引用,通道是选中回线所属通道去重),加上 `items` / `pointIndexes` / `lineIndexes` 与 `bounds`),`onSelectChange` 与指针载荷共用同一份口径;导出数据按概念分四份 —— `exportStationData` / `exportPointData`(`new.json` 的 `station_data` 与 `test.json` 的 `point_data`)、`exportLineData`(`test.json` 的 `line_data`,通道回线带 `channel_id` / `channel_name` / `parallel_group_id` 与第几回)、`exportChannelData`(`new.json` 的 `channel_data`,回线折在 `line_data` 列里、路由在 `coords` 列里)、`exportParallelGroups`(`test.json` 的 `parallel_groups`,与通道数据分开取),另有 `exportData({ format: 'view' | 'new' | 'test' })` 一次按口径给整份,见 `docs/GisView.md` 的「导出数据」。
|
|
261
|
+
- 这一轮(标签拖动与编辑、导出分项多选):节点标签与线路标签各多了一对开关 —— `draggable` 让标签自己接住指针(按住挪到别处记成 `offset`,移动节点时偏移照旧叠在节点位移上,位移小于 3 px 算点选),`editable` 时点一下发一条 `labelClick` 把这一条交给使用方;改内容 / 字号 / 颜色走 `updateLabel(kind, ref, patch)`(`style` 是内联样式的增量合并,某项写 `null` 只摘掉这一项覆盖),恢复默认走 `resetLabel` / `resetLabels`,当前记着的逐条状态用 `getLabelStates()` 读出来(按 id 记,换数据不丢),改动都发 `labelChange` 给面板回显,见「标签的拖动与编辑」。导出多了**分项多选**:`exportData({ parts: ['station', 'channel'] })` 选什么导什么(没要的那几份连记录都不生成),选了通道 / 线路会自动带上拐点 —— 通道的路由与线路的折线都靠拐点说话;`exportSelectionData()` 把同一套分项接到「选中的这一撮」上(选中口径与 `exportSelection` 完全一致,出的却是业务表记录),见「导出数据」。
|
|
262
|
+
- 这一轮(大数据交互的性能、bug 排查与 demo 压测):三处「按总量付费」改成「按改动量付费」—— `batch()` 收口筛选(批处理里只记账,收尾统一跑一次 `applyFilter`,标签整层重排 / 提示框 / 鹰眼图也只在收尾各补一次)、筛选投影带缓存(`filter.js` 的 `createFilterProjectionCache`:数据与条件字段没变就复用,单条改动只补那一条的字段 / 坐标 / 两端绑定,点挪了位置还会把绑在它身上的线端点一起重解析,id 变了或数组换了一份才整份重建)、线段网格(`utils.js` 的 `createSegmentGrid`)改成按格线步进登记「真正穿过的格子」(长线只记一条包围盒,查询时先按盒子筛一遍)。实测:20000 点 / 20000 线的筛选开着改 500 个节点(基准页 `docs/steps/bench-filter.html`)从 4687 ~ 6382 ms 降到 152 ms(同一份数据不开筛选是 142 ms),10 万点 / 10 万线 500 次 `pick` 从 3530 ms 降到 164 ms(0.3 ms / 次),10 万级默认全字段 `search` 从每次 30 ms 上下降到首次 80 ms(建缓存)、之后 15 ms 上下。顺带修掉一处真 bug:节点更新时如果它绑着被筛选隐藏的线,补槽位必然失败,于是每次 `updatePoint` 都退化成整份重排 —— 现在只把真的画在场景里的线交给补丁路径。demo 的「开关 · 历史」页多一块**性能压测**(一个按钮量出「500 次逐条 `updatePoint` / 500 次 `batch()` / 500 次 `pick` / 5 次 `search`」,附 `getStats()` 读数,跑完数据回到原处),「数据 · 通道」页的导出样例改成一有数据变更就自动刷新。新增自测页 `docs/steps/selftest-scale.html`(26 条断言 + 4 条耗时 DIAG)与基准页 `docs/steps/bench-filter.html`(上面那些数字就是它量出来的),详见 `docs/GisView.md` 的「批量更新」与「大数据量」、`docs/GisView实现原理.md` 的 16.6 / 16.7。
|
|
263
|
+
- 这一轮(线路标签拖动飞走 / 鹰眼图之后标签消失 / 热力图观感 / 配置更新的快速通道):修掉两处真 bug —— 线路标签拖动时每一步都把「落点 + 本次偏移」的结果写回排版基准,分 5 步拖 50px 会被算成 10+20+30+40+50 = 150px(看着就是标签自己飞走),现在拖动期间只刷新文本,落点始终从锚点重算;两层标签同时被 `hideOnInteract` 收着时,停手的收尾计时器挂不上、图层再也回不来(「移动一次鹰眼图,线路标签全消失」就是这条),现在收尾无条件挂上,180 ms 内恢复。热力图:光晕的渐隐改成按符号自己的距离场走(圆符号与过去逐像素等价,方 / 圆角方形不再一律套一个圆点 —— 这是「有的圆有的棱形」那一半观感的来源),密度场的透明曲线指数 0.45 → 0.8(核外更透、热核更实、外缘不再发灰)。性能:`updateOptions` 的快速通道扩到纯标签 / 提示 / 根样式(这类补丁引擎一个字节都收不到,只刷覆盖层;小图配置变更顺带作废组件这一层的小图数据缓存)与状态样式 / `defaultHighlight`(只重解析在悬停 / 选中里的那几个要素)—— 10 万点 / 10 万线的合成数据里,`updateOptions({ label })` / `{ tooltip }` / `{ ariaLabel }` / `{ statsInterval }` / `{ initialView }` / `{ pointSelectStyle }` / `{ defaultHighlight }` / `{ selectionBoxStyle }` 从 82 ~ 120 ms 降到 0 ms(改 `pointStyle` 这种真重排仍是 116 ms 上下),8 个用例在「快速通道」与「强制整份重排」两条路上的截图逐像素相同;`selftest-labels.html` 新增「拖动逐步核对」段(分 5 步拖、合计 -50px,每一步的漂移必须为 0)。见 `docs/GisView.md` 的「标签的拖动与编辑」「线路标签」「鹰眼图」「热力图」「大数据量」与 `docs/GisView实现原理.md` 的 15.34。
|
|
264
|
+
- 这一轮(样式更新的快路径 / 面板收进弹层 / 常用读数与定位的实例方法):`updateOptions` 又多了一条快路 —— `pointStyle` / `lineStyle` / `pointRules` / `lineRules` 这些「只换外观、不动几何」的补丁由引擎里的 `isStyleOnlyPatch` 认出来,逐要素按新配置重解析后覆盖到实例槽位上(数据包围盒、背景、通道骨架与装饰锚点都不重算,只有贴图换批次 / 阴影条数 / 绘制层级 / 虚线图案变了才回退整份重排),10 万点 / 10 万线下一次 `pointStyle` 从 109 ~ 116 ms 降到 40 ~ 55 ms,两条路画出来的截图逐像素相同。上手成本这一轮主要花在「少写循环」上:实例上新增 `getPoint` / `getLine` / `getChannel`(按 id / 下标取一个要素)、`listPoints` / `listLines`(按 `indexes` / `inView` / `limit` / `withScreen` / `visibleOnly` 列快照)、`getElementBounds` 与 `focusOn`(一批要素的包围盒 + 把视角挪过去,`{ center: true }` 只居中、`select: true` 顺带选中)、`getViewState` / `getContextAt`(一次取全读数,自建状态栏与右键菜单各调一个就够),见 docs/GisView.md 的「实例方法」与「常用读数与定位」。demo 也从「一长串折叠面板」改成「一排按钮 + 弹层」:点按钮或右键按钮弹面板,Esc / 点别处 / 点画布都能收(点画布那一下不吞事件),切页签时按钮跟着换,画布上右键空白处弹出「画布菜单」(定位到选中 / 聚焦命中的那一个 / 适应视图 / 读一下选中集 / 导出选中的图 / 导出选中的数据 / 清空选中 / 切底图,每一行都是一个公开方法),并新增「实例方法速查」面板当场调一次新方法看返回值;`docs/steps/selftest-demo.html` 补了一批断言覆盖弹层开关与画布菜单。
|
|
265
|
+
- 这一轮(新加标签的交互 / 标签事件的所属父级 / 右键默认收系统菜单 / 数据格式里的标签):`addLabel` 加的标签与配置、数据里声明的走同一条路 —— `label` / `lineLabel` 上打开 `draggable` / `editable` 之后它照样能拖能点(逐条没写就跟着这一类的缺省走,写 `false` 单独关掉这一条),位置现给就行(节点标签是八向 `top` / `bottom` / `left` / `right` 与四个斜角加 `center`,线路标签是沿线 0 ~ 1 / 百分比 / 距离);「一开始就显示几条」把数组写进 `label` / `lineLabel`,或者把 `labels` 写进数据都行,两种写法都跟着数据走。`labelClick` / `labelDrag` 的载荷新增两份现成读数:`label`(这一条标签的 `id` / 当下文本 / 合并后的 `position` 与 `style` / `source` 是配置、数据还是运行时加的 / 运行时那一份的 `definition`)与 `parent`(`type` / `index` / `id`,线路在通道里时 `channel` 给 `{ id, groupId, index, slot, item }` —— 点的是哪条通道的哪一回线直接读);引擎为此补了 O(1) 的 `getLineChannel(index)`。右键默认把浏览器的系统菜单收掉(`preventContextMenu` 缺省 `true`:接住右键时一律 `preventDefault()`,点 / 线 / 空白 / 通道都一样),想留系统菜单写 `false`,关掉 `contextMenu` 开关也等于组件不接右键。demo 的「数据 · 通道」页新增两块标签格式样例(节点上的 `point_data[].labels` 与通道里的 `channel_data[].line_data[].labels`,`printDataShape()` 的完整结构也带上 `getLabels()` / `getLabelStates()`),「样式 · 标签」页的「加一条标签」多了位置下拉与「初始标签」(给前 3 个节点各写 4 条上下左右的 `labels`,一条 `label` 配置不写也画得出来)。点选 / 拖动的读数(这一条标签的 id / 内容 / 来源,挂在哪条要素 —— 线路在通道里时连通道 / 分组 / 第几回)在 demo 面板点一下就直接排出来,`getLabels()` 的每一条也补了同一口径的 `source`。
|
|
266
|
+
- 这一轮(指针事件的动作判读 / 只读查询与数据增补的实例方法 / 滚轮缩放的连续手感):画布上的每一条指针事件都多了一份 `action` 读数 —— `type` 说「这一次是什么操作」(按下时是意图 `dragPoint` / `dragWaypoint` / `boxSelect` / `pan` / `copy` / `channelDraft` / `contextMenu` / `click`,松开时是刚做完的那件事,点击与右键是真正发生的那一件:`select` / `clearSelection` / `toggleSelect` / `addWaypoint` / `removeWaypoint` / `selectWaypoint` / `contextMenu` / `dblClick` / `click`),`target` 说点在什么上面(`point` / `line` / `channel` / `empty`,通道回线单独给 `channel` 而不是混在 `line` 里),`moved` 说明按下之后有没有真的拖动、`double` 说明是不是双击里的第二下,`button`(`left` / `middle` / `right`)与 `modifiers`(`ctrl` / `meta` / `shift` / `alt`)不用再读 `originalEvent`,`data` 把 `target` 对应的那一份数据直接端过来(节点 / 线路就是数据里那个对象、通道回线给通道定义、空白给 `null`)—— 「右键点空白才弹菜单」「拖完的是节点还是拐点」「点的是通道的第几回」因此都是一次 `switch (payload.action.type)` 的事。标签事件同一套写法(`target: 'label'`、`data: { label, parent }`);选中集变更也带 `action`(`select` / `toggleSelect` / `boxSelect` / `clearSelection`),引擎的 `onSelect` 多给一个 `detail` 参数(载荷为 `null` 时靠它分辨这一下是框选还是 Ctrl 取反,`originalEvent` 与修饰键都在里面)。查询与增补补了一批实例方法:`pickAt(clientX, clientY)` 按鼠标事件坐标问「这儿是什么」(与指针载荷同一份结果再加 `target` / `lngLat`,只读、不改选中不动光标)、`getFeaturesInRect(rect)` 按画布矩形列出框里的节点与线路(与框选同一套判定,另给这块像素对应的 `worldRect`)、`getChannelsInView()` 数当前视口里的通道(同一通道的多回线只算一条,带 `lineIndexes` / `groupIds`),`addPoint` / `addPoints` / `addLines` 把现成对象追加到数据末尾(老数据下标一个都不动,返回 `{ ok, count, indexes, items }` 并发一条 `addPoints` / `addLines` 数据变更),`removeLines` 一次删多条线路(通道声明与分组跟着收拾、删空的通道整条走)。性能做在「每次按下少跑一趟」上:拖拽起手直接复用按下那一次的拾取结果(`resolveDragIndex`),省掉一次点网格查询与一次拐点扫描;滚轮缩放从「一格一档」改成按 `deltaY` 的量连续缩放(100 像素 ↔ 1.12 倍,触控板的小步进因此是一路平滑的),`deltaMode` 为行 / 页的设备先折算成像素、单次再夹在 240 像素以内。可上手性:`docs/steps/` 新增第 12 步 `step-12-events.js`(一边点一边把 `action` 逐字段打在页面上,工具栏的「框内有什么」调 `getFeaturesInRect`),新增自测页 `docs/steps/selftest-actions.html`(67 条断言逐条覆盖上面这些读数),事件演示页(`?demo=events`)也补了一栏「动作判读」与 `pickAt` / `getFeaturesInRect` 两个方法按钮,`docs/GisView.md` 新增「action:一次操作的判读」一节与五个实例方法的说明。
|
|
267
|
+
- 这一轮(左右键事件的参数与判读 / 程序化悬停 / 批量打补丁与删除选中):指针载荷的 `action` 把「按的是哪个键、按着哪些修饰键、按了多久、是不是长按」一次给全 —— `button`(`left` / `middle` / `right`,悬停与没有按键的路径是 `none`)与 `modifiers` 每条都有,`left` / `right` 是左键 / 右键的一眼判定(中键两边都是 `false`),松开与点击载荷另有 `press`(**按下时的意图**,与那一刻 `onPointerDown` 的 `action.type` 同一份)、`duration`(按了多久,毫秒)与 `longPress`(超过 500ms 且中间没拖动过 —— 触摸 / 触控笔按它出菜单);**中键按住就是临时平移**(`action.button === 'middle'`、`action.type` 是 `pan`,按住拖动平移、松开结束,不经过 `onClick`、也不改选中);悬停载荷(`GisViewHoverPayload`)带 `action.type: 'hover'` / `target` / `button: 'none'` 与光标位置(`x` / `y` / `world`;光标正好停在最近一次指针事件的位置上时另有 `clientX` / `clientY` / `originalEvent`,程序化悬停没有这一份)。方法侧补四条路:`hoverAt(clientX, clientY)` / `clearHover()` / `getHover()`(程序化悬停:让指针「停」到某个位置,高亮、光标与 `onHover` 一起更新、读数与 `onHover` 同一份 —— 外部列表 / 表格 hover 到某一行时联动高亮图上的要素用它,`docs/steps/` 第 12 步工具栏的「程序化悬停」按钮就是这么接的),`getSelectionBounds()`(选中集的世界包围盒,没选中或数据换过是 `null`),`patchPoints(entries, patch?)` / `patchLines(entries, patch?)`(批量打补丁:`[{ id: 'a', name: 'A' }, { index: 3, x: 10 }]` 一次改几条,或 `['a', 'b']` 配一份公共补丁盖一批,认不出来的进 `skipped`,只记一条历史、只发一条 `updatePoints` / `updateLines`),与 `removeSelected(options?)`(删掉当前选中的那一批:节点连同绑在它们身上的线路、剩下的选中回线一起删,删完清空选中,返回点 / 线的 ids 与下标、连带删掉的通道数 —— 工具栏「删除选中」就是它,一次调用只落一条历史、只发一条 `removeSelected` 数据变更)。性能与交互:`selectionInfo()` 按选中集引用缓存(悬停与每条指针载荷都要这份读数)、`onSelectChange` 的去重从拼字符串签名换成 Set 比对、批量补丁与删除选中各只落一条历史与一条数据变更。**`updatePoints(points)` / `updateLines(lines)` 的整份替换语义保持原样** —— 批量补丁因此另起名 `patchPoints` / `patchLines`,两套各管一件事(老接口换整份数据、新接口只动点到的这几条)。
|
|
268
|
+
- 这一轮(数据格式成文、筛选简写与 `optionChange`、几个 demo 的交互修正):新增 [数据格式](docs/数据格式.md) —— 节点 / 线路 / 通道 / 标签 / 拐点分组逐字段一张表(`x` / `y` 必填、`id` 是引用键、图上回线也是普通线路)、条件语法与运算符表、四套导出格式(`view` / `new` / `test` / 新增的 `input` 回灌口径:`exportData({ format: 'input' })` 给当前数据副本,`updateData` + `updateOptions` 直接喂回去就回显)、事件载荷速查(指针 / 命中、选中集、标签、读数)与示例数据说明。筛选补上简写:`setFilter({ vn_kv: { gte: 525 } })` 等价于 `{ points: { vn_kv: { gte: 525 } } }`(顶层出现 `points` / `lines` / `cascade` / `worker` 任一键才按完整格式解释),完整格式里多写的顶层键走 `onWarning` 点名提醒、不再静默失效;`normalizeFilter` / `FILTER_FORM_KEYS` 一并导出,交给组件之前可以先自查同一套口径。配置变更多了一条统一出口 `onOptionChange`(`updateOptions()` 每改一次发一条:`keys` / `replace` / `changed`(含 `style` / `presentation` 两个快路径归类)/ 合并后的 `options`),外部面板跟组件状态同步不必自己再包一层 `updateOptions`。实例方法补 `isSelected(ref)`(id / 下标 / `{ type, index }` 问「这个要素在不在选中集里」)与 `getLineChannel(ref)`(这条回线属于哪条通道、哪一组:`{ lineIndex, channelId, channelIndex, channel, groupId, group }`)。读数侧:`getStats()` 新增 `idle`,静止画面按需渲染时报「空闲 + 最近一帧耗时」而不是 0.3 fps 这种假帧率,帧率窗口空闲超过 200ms 后断开重算;`emitStats` 的节流初值改成 `-Infinity`,首帧一定报一次(页面打开 200ms 内画完的那一帧原来会被节流掉,静止之后又不再画帧,面板读数就一直空着)。demo:`PerfDemo` 的电压筛选原来传的是扁平条件(库只认 `points` / `lines`,条件被静默忽略,看着就是筛选不生效),改用 `points` 完整格式并抽出 `voltageFilterInput()`;`EventDemo` 的总线订阅补拆 `{ payload, event }`(右键动作读数不再是「—」)、标签载荷认 `{ label, parent }`、方法栏补选中集 / 可见数据 / 导出读数;`LabelDemo` 不再让「点选标签」改写批量操作的目标 labelId(批量改 / 删原来会落到 `default` 上)、三个「加一条」按钮各自记住新建的 labelId、`cyclePosition` / `toggleHidden` 回写命中读数并新增 `labelStatesText` 汇总;`ChannelDemo` 的分组操作一律按 `channelId`(拆分后 `getChannels()[0].id` 是分组 id,第二次「每回线拆一组」原来会空转)、导出读数区分输入侧通道分组与视图侧拐点分组;`StyleDemo` / `BasicDemo` 的读数行与 `GisViewDemo` 的初始读数修正(`onReady` 在 `createGisView` 返回之前触发,依赖实例的读数必须放到创建之后)。回归:仓库 9 个自测页全部通过、7 个 demo 的点击 / 滚轮 / 框选 / 读数 / 控制台逐页跑通(`PerfDemo` 2000 点筛 `≥1050 kV` 得 500 点 / 99 线)。另外把内置鹰眼图的容器改成按需挂载:没配 `minimap`(或 `enabled: false`,以及功能开关把 `minimap` 关掉)时一个节点都不进 DOM,运行时再配上会当场补挂上,`destroy()` 之后宿主里不留东西 —— 与文档里「不配则不产生任何 DOM」的承诺一致(此前无论配不配都会先挂一层隐藏容器)。
|
|
269
|
+
- 这一轮(数据格式补全拐点 / 标记 / 偏移与颜色样式、`input` 口径带 `labelStates`、拖拽落点新演示、逐要素细粒度方法与选中高亮的修正):[数据格式](docs/数据格式.md) 补上缺的几节 —— 「拐点」把三处写法(线路 `path` 的中间顶点、通道 `channels[].path` 骨架、分组的 `groups[].path`)与读写接口(`getWaypoints` / `addWaypoint` / `removeWaypoint` / `setLineWaypoints` / `setChannelPath` / `addChannelGroupWaypoint`)以及导出形状(`parallel_groups` 的 `group_route` 含两端、`input` 折在 `path` 里)摆进一张表,「标记 / 装饰」把 `lines[].decorations` 逐字段写清(`position` 的四种写法、`anchor` 的 auto / follow / fixed、`at` 优先、`parallel`、`size` / `color` / `strokeColor` / `strokeWidth` / `opacity` / `shadow`),再补一节「偏移与颜色样式」说明「标签 `offset` 是像素、标记 `position` 是沿线、`at` 是世界坐标」与「全局 → 规则 → 要素自身 → 状态 → 热力图」的覆盖顺序;标签一节补上「编辑结果不在数据对象里」这件事:新增 `options.labelStates` 与 `updateOptions({ labelStates })` 回灌,`exportData({ format: 'input' })` 的结果多一列 `labelStates`(与 `getLabelStates()` / `setLabelStates()` 同一份形状,`labels: false` 时不带,`counts.labelStates` 报条数)—— 数据支持的格式与导出的格式因此对上,「存档 → 回灌」两步就能完整回显。逐要素改法补一批实例方法(19 个):`setPointSize` / `setPointSymbol` / `setPointColor` / `movePoint` / `setLineWidth` / `setLineColor` / `setLineWaypoints` / `getLineDecorations` / `setLineDecorations` / `addLineDecoration` / `updateLineDecoration` / `removeLineDecoration` / `setChannelWidth` / `setChannelNodeWidth` / `setChannelInset` / `setChannelTangentInset` / `setChannelLineWidth` / `setChannelColors` / `setChannelPath` —— 一个方法只改一样东西(大小 / 图标 / 颜色 / 拐点 / 标记 / 通道宽度与配色),走的还是定向更新那条路:只重排受影响的一小片、只发一条数据变更、只落一步历史,要素没找到只记一条 warning 不抛错;通道那两个「成员回线 + 通道声明」一起改的(线宽 / 配色)包在一个 `batch` 里,只发一条数据变更。新增演示「拖拽落点」(`docs/demos/DragDropDemo.vue`,页签 `?demo=drag`):左侧看板 7 个图标直接拖到画布上 —— `dragstart` 写自定义 MIME + `text/plain` 兜底,`drop` 用 `getContextAt` 一次取全(世界坐标 / 命中要素 / 选中集)先出草稿(这一步数据一个字节不动),「确认添加」才按类型调 `addPoint` / `addLines` / `createChannels + addLines + updateOptions` / `addWaypoint` / `addLineDecoration` / `addLabel`,「取消添加」或 Esc 丢掉草稿,两条路都进事件日志(`dataChange` / `waypointChange` / `labelChange` / `selectChange` 都订阅了);落点正好压在节点上会拉出两端重合的退化线,这里按 6px / 相机比例拦一条提示;面板上的「微调最后一条」把上面那批细粒度方法摆成按钮,「存档 → 重建」用 `exportData({ format: 'input' })` + `labelStates` 走完整回灌。修正 `StyleDemo` 的「选中高亮」完全没效果:它把样式对象传给了布尔开关 `defaultHighlight`(`=== true` 才算开),现在按「有样式对象就用它、没有才退回内置强调」,`create.js` 的 `warnOptionValues` 也会对这类值提醒一句。类型侧 `types.d.ts` 补 `GisViewOptions.labelStates`、`GisViewExportDataResult.labelStates` 与这 19 个方法的签名;自测页 `selftest-demos.html` 的页签断言改成 8 个并新增两条拖放断言(拖进去先出草稿、确认才落地)。回归:`npx tsc --noEmit`、9 个自测页 720 条断言、`t35` / `t50` / `t54` / `t55` / `t58` 全部通过。
|
|
270
|
+
- 这一轮(三份完整样本数据、演示页「完整样本」与数据格式的一处澄清):仓库根目录新增 `sample-labels.json` / `sample-waypoints.json` / `sample-channels.json` —— 三份都是与 `exportData({ format: 'input' })` 同构的完整样本(`points` + `lines` + `channels` + `labelStates` 四列,拿来就能当 `createGisView` 的入参),把标签(八向位置、沿线的比例 / 百分比 / 距离、旧式 `lineLabel*` 写法、`field` / `html` / `className` / 内联样式与像素偏移)、拐点(`lines[].path` 的中间顶点与通道 / 分组骨架各一份)、分组(`channels[].groups` 各带自己的骨架拐点)、标记(`decorations` 的比例 / 百分比 / 距离 / 世界坐标四种定位与圆 / 方 / SVG / 图片四种符号,外加 `parallel` / `opacity` / `shadow`)、偏移(标签 `offset`、标记 `position` / `at`)与颜色样式(逐点 `symbol` / `strokeColor`、逐线 `color` / `width` / `lineType`、通道的逐回配色与线宽 / `nodeWidth` / `offsetMode` / `inset` / `tangentInset`)摆齐,`labelStates` 里还带着拖过的偏移、改过的内容与样式、运行时加的标签。新增演示页「完整样本」(`docs/demos/SampleDemo.vue`,页签 `?demo=samples`):三份切着看,「与文件对一遍」用 `exportData({ format: 'input' })` 与文件逐字节对账,「按导出重建」演示完整的存档 → 重建回显,读数把拐点 / 标记 / 标签状态 / 画布上的标签数量摊开。文档侧 [数据格式](docs/数据格式.md) 的「拐点」一节补上最容易踩的一条:**线路的 `path` 是整条折线,首尾两个顶点是端点**(绑了 `from` / `to` 时被节点坐标替换),只写中间那几个点会被首尾替换掉、直接退化成两端直连 —— 通道 / 分组骨架反过来只写中间拐点,两种写法各配一段可以直接抄的 JSON;「示例数据」一节列出三份样本与各自包含的字段。回归:`npx tsc --noEmit`、`npm run build`、`npm run build:lib` 通过,9 个自测页 725 条断言全绿(`selftest-demos.html` 的页签断言改成 9 个并新增 3 条样本断言),9 个演示页逐页点按 0 warning / 0 error(`full` 那条「未知配置项 qualty」是它故意留的配置提醒),新增的样本校验脚本对三份 JSON 断言 25 条(条数 / 拐点 / 标记 / 标签状态 / `parallel_groups` 含两端 / 导出回灌逐字节一致)全过。
|
|
271
|
+
- 这一轮(两处演示修正):「标签交互」的「加一条」按钮每点一次都写一批新标签 —— id 按序号写 `备注1` / `备注2`…,内容与 `offset` 跟着序号走,并写上 `avoidOverlap: false`;之前几次加出来的是同名同位置的两条,被标签避让整条丢掉,看着像「只有第一次加得上」。「拖拽落点」的草稿标记改成只画看板上的那个图标(原来圆点后面还跟着文字),落到画布上的图形本身不受影响。[GisView](docs/GisView.md) 的标签一节补上「加出来的标签也要过避让」这一段和两个绕开的写法。
|
|
272
|
+
- 这一轮(文档逐项对账与第四份「全字段齐全」样本):[GisView](docs/GisView.md) 新增「参数速查(逐个方法)」一节 —— 把 `GisViewInstance` 的每个成员按「配置与数据 / 相机与视图 / 坐标、拾取与读数 / 选中、复制与合并 / 拐点与通道 / 细粒度改一个要素 / 增删数据 / 标签 / 导出 / 筛选 / 事件、开关与历史」十一组逐个列全,参数一律写成「名字: 类型 = 默认值」(不写默认值的就是必填),返回值只标类型(`this` = 实例本身、可以接着链式调),并补上此前只在实例里、文档缺签名的 `select(id)` / `getLineChannel(ref)` / `getChannelsInView()`;末尾「属性」一节说明 `el` / `canvas` / `gl` 各是什么、什么时候是 `null`。[数据格式](docs/数据格式.md) 的节点 / 线路 / 标记 / 通道与分组 / 标签五张字段表都补上「默认值」列,逐字段写清默认值、取值范围、生效条件与回落到哪个 `options`(「无」「空数组」「跟随某项」分开写,通道那几个全局选项也标了各自的默认值)。新增 `docs/steps/check-doc-coverage.mjs`:从 `src/gis-view/create.js` 的实例对象与 `src/gis-view/types.d.ts` 的接口提成员与字段,再和文档对账 —— 实例成员 185 个、数据字段 212 个,缺一个就 exit 1(`node docs/steps/check-doc-coverage.mjs`,现在 0 缺失)。样本侧新增第四份 `sample-full.json`(同样是 `exportData({ format: 'input' })` 的同构形态):10 点 / 22 线 / 3 通道摆在同一份里 —— 5 条线路各带 3 ~ 4 个拐点(全部线路共 26 个拐点、通道骨架再 24 个)、同一节点最多挂 4 条标签(8 条线路也各带 2 条以上)、通道 `fc1` 6 回拆成 3 组且每组各带 2 个骨架拐点(`fc2` 用 `nodeWidth: 1.6` + `inset`、`fc3` 用负的 `tangentInset`)、23 个标记与 6 条 `labelStates`(拖过偏移 / 改过内容与样式 / 运行时加的 `added: true` / 隐藏的 `hidden: true`);演示页「完整样本」多一枚「全字段齐全」芯片(页签 `?demo=samples`),自测页 `selftest-demos.html` 补一条断言(22 条线 / 拐点 ≥ 30 / 标记 23 / 画布上标签 ≥ 20)。顺手修掉「事件」一节里一处没转义的 `|`(`count(name) || count('*')` 那一行会让表格多出一列)。回归:`npx tsc --noEmit`、`npm run build`、`npm run build:lib` 通过,9 个自测页 728 条断言全绿,9 个演示页逐页点按 0 warning / 0 error(`full` 那条「未知配置项 qualty」是它故意留的配置提醒),样本校验脚本对四份 JSON 断言 33 条(条数 / 拐点 / 标记 / 标签状态 / `parallel_groups` 含两端 / 导出回灌逐字节一致)全过。
|
|
273
|
+
- 这一轮(拖拽落点预览 / 最近要素查询与三处性能优化):拖拽落点的演示把「拖到一半就能看见落点图标」做实了 —— 组件新增 `setDropPreview` / `getDropPreview` / `clearDropPreview`:拖拽期间把**准备落地的那一份字段**交给组件,组件按与真实要素完全相同的样式链(规则 -> 全局样式 -> 这一份字段)在地图上画出图标(`point` / `waypoint` / `decoration` 走节点链,`line` / `channel` 走线路链,`label` 只画落点光环),读数里的 `styleSize` 与落地后 `getPointStyle().size` 是同一个数 —— 「预览里多大、落地后就多大」不再靠两边各写一套;`docs/demos/DragDropDemo.vue` 删掉演示自己那套 DOM 假标记与手写的最近点 / 最近线扫描,`dragover` 合并到下一帧(rAF + 32 ms 兜底、同一个指针位置跳过)调一次 `setDropPreview`,「微调最后一条」的按钮改成 `pointSizeOf()`(数据里没写尺寸就读 `getPointStyle()`)。查询侧新增 `nearestPointsAt`(点网格)与 `nearestLineAt`(线段网格,一次给出 `position` 沿线比例,加拐点 / 挂装饰直接接 `addWaypoint` / `addLineDecoration`),`getContextAt` 多了 `{ selection: false, lngLat: false }` 轻量档(dragover / 自绘十字线这类每帧来的场合用)。性能侧:符号预烘焙在按住交互与页面隐藏时让路(当前档位缺贴图的那一批仍可插队,停手后接着烤)、线段查询复用 `segmentRefs` 与按线路缓存的总长度。可上手性:[GisView](docs/GisView.md) 的「常用读数与定位」之后新增「拖拽落点预览」一节(含可直接抄的 dragover / drop 两段),「坐标、拾取与读数」参数表补上这六个方法与 `getPointStyle`;实例成员 191 个、数据字段 212 个,`node docs/steps/check-doc-coverage.mjs` 0 缺失。回归:`npx tsc --noEmit`、`npm run build` 通过,`selftest-demos.html` 53 条断言全绿(新增 4 条拖放断言:拖拽期间就出预览 / 预览读数进面板 / 落地与预览的尺寸符号一致 / 确认之后预览收掉)。
|
|
274
|
+
- 这一轮(拖拽只出现点、按属性落地 / 按区域取数与标签排版读写分离 / 可上手性):拖拽演示不再跟着浏览器默认的那一个拖影方块 —— `dragstart` 里把 `dataTransfer.setDragImage()` 指向一个 1×1 的透明节点之后,光标下只剩组件画出来的落点图标;松手也不再直接写数据,落点先按属性留在图上(改名字 / 电压等级 / 图标时预览立刻重铺),点「按属性新增」才落地、点「取消新增」只把预览收掉。性能侧回答「把整个地图网格拆开、分区域显示值不值」:**渲染侧不值** —— 2 万点与 10 万点的 `drawCalls` 都是 2、`frameMs` 0.1 ~ 0.8 ms,把视口放大到只剩 1 个点也不掉(绘制按实例化一次提交,视口外的实例由 GPU 裁掉);**按区域取数值**,但值在「每次交互要重算的簿记」上:引擎新增 `forEachPointInView(rect, visit)`(零分配、回调返回 `false` 提前结束、与 `getVisiblePoints` 同一份可见判定),节点标签的两趟扫描改走它(放大看一小片时不再先拿整份 `getVisiblePoints()` 再筛,候选配额也只数视口里的点),标签排版里「量尺寸」与「摘节点」也分开 —— `dropLabel` 的 `remove()` 会让布局作废,夹在量尺寸中间等于这一批里每条标签各触发一次重排,现在攒到一批量完再统一回收,实测 2 万点全在视口里、标签上限 200 时一次 `updatePoint` 从 ~55 ms 降到 ~17 ms。文档侧:[GisView](docs/GisView.md) 的「大数据量」补了这两条实测结论与 `forEachPointInView` 的用法,「拖拽落点预览」补了「拖拽期间不要默认拖影 / 落地前先按属性预览」的写法,[实现原理](docs/GisView实现原理.md) 的 18 章补「第八轮」与「网格拆分 / 分区域显示值不值得」两节,`docs/steps/selftest-perf.html` 增一条标签不变量断言与一条读数。
|
|
275
|
+
- 这一轮(拖拽收尾不再丢点 / 优化方案逐条列全):拖拽演示修掉一处真 bug —— 浏览器在 `drop` 之后还会给源元素补一条 `dragend`,原来那一条会把刚落下的预览收掉,「松手先显示在地图上」实际只剩一眨眼;现在改成「有草稿就把预览按原字段摆回落点,没有才收掉」(`settleTransientPreview()`,`dragleave` 与 `dragend` 都走它),中途拖了别的项进来又没落上时点会回到草稿自己的位置上,真正收掉预览的只有「按属性新增 / 取消新增」;`docs/steps/selftest-demos.html` 补两条断言(58 条全绿)。优化方案(只列方案、这轮不动代码):拿 10 万点 / 1 万线在真时间下重测主路径,把 [实现原理](docs/GisView实现原理.md) 第 16 章从 12 条补到 **22 条**(16.13 ~ 16.22,逐条标收益 / 成本 / 风险 / 怎么验证)—— 最值钱的三条:`findPointIndex` / `findLineIndex` 仍在做 `points.findIndex` 线性扫描(CPU profile 里占单次 `updatePoint` 的 ~62%,而组件里已有按数据版本维护的 `pointIdMap` 没用上,10 万点下单次 0.40 ms 可望降到 O(1));单条 `updatePoint` 会同步整层重排标签(不带标签 0.40 ms / 带标签 4.61 ms,包在 `batch()` 里 0.41 ms,可合并到下一帧或做增量排版);`refresh()` 是整份重建(10 万点 16.3 ms、10 万点 + 1 万线 41.0 ms,而显示类补丁 0.04 ms)。另有 `addPoint` 逐个 16.4 ms/次(`addPoints(2000 条)` 一次 19.6 ms)、`removePoints` 的平方级去重、标签量测缓存、网格顶点随平移重建、Worker 化的下一批候选等;「网格拆分 / 分区域显示」与「WebGPU」两条已经在前面量过并明确不做,也在第 16 章开头点了名。
|
|
276
|
+
- 这一轮(大数据渲染与交互的深度优化 / 与 deck.gl、G6 的对照):把 [实现原理](docs/GisView实现原理.md) 第 16 章列的方案里最值钱的三条真正做了(16.13 / 16.17 / 16.18 已标成已完成)—— 交互侧给「按 id 找要素」补上一张按数据版本维护的定位表(`locateById()`:表在就 O(1) 查、查不到直接判「这个 id 不在数据里」,表不在先扫描、同一数据版本里扫够 8 次才建表;只改坐标 / 样式、不动 id 与下标的动作不推版本,`addPoints` / `removePoints` / `updateData` / 撤销重做这些结构改动才推,另有「命中时对一眼身份」「条数对不上不算完整表」两道保险),10 万点下最坏情况(id 在数组末尾)单次 `updatePoint` 0.435 → 0.047 ms、查一个不存在的 id 0.41 → 0.005 ms、数字当索引 0.479 → 0.005 ms、`patchPoints` 一次改 500 条 73.0 → 2.2 ms、`removePoints` 一次删 1000 条(平方级去重换成 `Set`)432 → 32 ms;渲染侧把标签重排从「每轮重写 DOM + 重新量尺寸」改成脏检查与复用(`overlay.js`:写之前先比签名、`transform` 没变不赋值、量过的盒子换算成层内排版像素缓存、候选节点按槽位停着、留下的才进 DOM),可见标签集合与改前逐字节一致,带标签的单条更新 5.12 → 0.75 ms。回归:真时间下 `selftest-labels.html` 90 条、`selftest-actions.html` 96 条、`selftest-full.html` 68 条、`selftest-demos.html` 58 条断言全绿(0 失败),`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 192 个实例成员 / 212 个数据字段全命中。对照侧另出一份《与 deck.gl / G6 的对照》(后来删掉了):一张「三家分别是什么」的表,两节「deck.gl 强我们弱 / 我们强 deck.gl 弱」与「G6 强我们弱 / 我们强 G6 弱」,末尾一张按「值不值得补」排的「我们还缺什么」表(瓦片底图与图层种类看场景,二进制数据通路、数据准备进 Worker、增量追加值得,布局算法 / WebGPU / GPU 拾取 / 多视图暂不值得)。
|
|
277
|
+
- 这一轮(把对照文档里「值得补」的补上 / 实现原理里中收益的也做掉):对着《与 deck.gl / G6 的对照》§4(这份对照后来删掉了)收了四条,同时做掉 [实现原理](docs/GisView实现原理.md) 第 16 章里的四条 —— ① **增量追加**:引擎认得出「数据末尾就多了这几条」(`appendPoints`),只解析这几条、只往现成的实例批次里补这几条(实例缓冲按容量记账,够就 `bufferSubData` 只传新段;形状符号插在圆点那一段末尾、贴图符号追加到现成那一组;包围盒只扩不缩、背景与背景文字跟着新范围重算),`addPoint` / `addPoints` 因此不再整份重建 —— 10 万点下逐个 `addPoint` 16.4 → 1.0 ms(第一次 4.6 ms 是整份实例缓冲重传一次),场景结构做不到等价时(新点不在最上面那一层 / 贴图没有现成的组 / 通道或线路按 id 认领了它)自己回落整份重建,画出来与整份重建逐像素相同;② **标签层**多了一道「输入指纹」判据,改动的字段与标签画出来的东西无关时整层重排整个跳过(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)时同步回落 —— 10 万点 / 10 万线连着搜 6 次占主线程 43.3 → 0.1 ms;④ `refresh({ reflow: false })` 只重画一帧、跳过整份重解析(10 万点 / 1 万线 41.0 → 0.1 ms,缺省行为不变)。二进制数据通路(16.21)与 GeoJSON 三角化 / 导出 PNG 评估后仍留作路线,理由记在 [实现原理](docs/GisView实现原理.md) 第 16 章的 16.21 / 16.22。
|
|
278
|
+
- 这一轮(线路 / 节点的动画效果、贴图符号预热、继续提性能、完善 md 与 step):新增 `src/gis-view/effect.js`(纯数据层:两份缺省值与键表、归一化、按弧长采样)+ 引擎里两条绘制通路 —— **线路动点**(图标按**弧长**跑:位置由「距起点的弧长」换算、拐点上连着走、朝向取所在那一段;铺进既有节点批次,每帧只改位置且按槽位攒段上传、一帧上限 4000 个、视野外的线整条跳过)与**节点涟漪**(ECharts 的 `effectScatter` 那一类:独立一遍实例化绘制,半径 / 相位 / 周期在建实例时定死,每帧只传一个 `u_time`,实例只铺视口内的、拖动 / 缩放期间不重建、`limit` 兜底)。配置两层:全局 `options.effects`(`line` / `point` / `enabled` / `respectReducedMotion`)与逐要素 `lines[].effect` / `points[].effect`(`true` 用全局那一份、对象逐字段覆盖、`false` 关掉),**三层「关」是四件事**;`effects: false` 一处关掉所有动画,页签切到后台 / 系统要求减少动态效果 / 视野里既没有动点也没有涟漪时一帧都不多画。接口:`setEffects` / `getEffects` / `getLineEffects` 与 `getStats()` 的 `lineEffectCount` / `pointEffectCount` / `animating`。预热补上 `options.prebake`(`symbols` / `pointColors` / `lineColors` / `sizes`,别名 `images` / `colors`):把「会出现的符号 × 颜色 × 尺寸」在第一帧之前按与绘制同一条解析路径烤好(实现原理 16.5 因此标成已完成),`getPrebake()` / `runPrebake()` 是读数与手动入口,不配就与从前一样按需烤。demo 的「样式 · 标签」页签新增**「动画效果与预热」**面板:全局两个开关 + 图标 / 同时几个 / 一趟几秒 / 涟漪半径 / 同时几圈,三个「逐条覆盖」按钮把 `effect: { … }` / `true` / `false` 写进前几条要素(走 `batch()`,一次改动只发一条 `onDataChange`),`prebake` 那一份从演示当时正在用的规则表里当场收出来(综合示例:19 个符号 × 13 个点色 × 7 个尺寸 + 12 个线色,`runPrebake()` 1741 条组合 2.3 ~ 2.5 ms)。自测新增 `docs/steps/selftest-effects.html`(63 条断言,页面自带「帧泵」),十个自测页全绿。
|
|
279
|
+
- 这一轮(提前预知有哪些符号 / 颜色、动画单独一个 demo、拖完之后动画留在原地、十万节点为什么卡):① 「提前预知」这条先量后改 —— 同一份 2463 节点的真实数据(无头 Chrome + SwiftShader)实测:不配预热 26 条组合 / 55 张画布 / 首帧 29 ms,**照规则表做全排列收出 1741 条 / 1173 张 / 首帧 963 ms**(多烤 21 倍画布);所以 `prebake` 补上**逐组合写法**(`combos`,`PREBAKE_COMBO_KEYS` / `normalizePrebakeCombo`)与「先收后配」的那一半:新增 `getSymbolCombos()`(解析时用 `noteSymbolUsage` 记账,收出这份数据真正会用到的「符号 + 颜色 + 尺寸 + 描边」,线路侧是「线色 × 动画图标」),`updateOptions({ prebake: { combos: view.getSymbolCombos().combos } })` 就是一条一张、逐条命中(自测盯着「零张新画布」),`getPrebake()` 读数也多一段 `combos`;顺手修掉一处重复光栅化(同一份数据按需 76 → 55 张、全排列 2343 → 1173 张)。② 新增 `docs/demos/EffectsDemo.vue`(`?demo=effects`,页签 9 → 10):全局一套色 / 逐条各自一个色 / 内联 SVG 自带颜色三种摆在面板上,读数用 `getEffects()` / `getLineEffects()` / `getPointEffects()`,预热那块四个按钮(精确 / 全排列 / 再烤 / 清掉)。③ 修掉「开了动画的节点拖完之后动画留在原地」:根因是相机与配置都没动、`syncRipples` 的脏检查签名不变,现在拖动期间 `patchRipplePositions()` 就地把圆心挪过去、`rippleGeometryVersion` 进签名、松手后整份重建补齐,并新增读数 `getPointEffects()`(自测里有像素级断言)。④ 十万节点「卡」的那一半是**填充率**(`frameMs` 常年 0.1 ms,光栅化与浏览器合成不在里面):`quality.js` 的自动档补上第二路信号 —— `sample(frameMs, gapMs)` 取两者中更大的那一路(`FRAME_GAP_FACTOR = 2`,掉档看最近 8 帧、连续两次超标、冷静 250ms),引擎侧 `frameGapSignal()` 只在交互 / 动画连续画帧时喂;`getQuality()` 多一项 `gapMs`,`fast` 档 0.75× 分辨率实测 156 → 92 ms/帧。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 实例成员 200 / 数据字段 215 全部命中、`npm run build` 通过、十个自测页 839 条断言全绿(`selftest-effects.html` 79 条含像素级与画布计数断言、`selftest-perf.html` 47 条含整帧间隔两条)。
|
|
280
|
+
- 这一轮(十万级 demo 的放大 / 移动卡顿:细长三角形 × MSAA 与两个渲染开关):先量后改 —— 微基准(无头 Chrome + ANGLE / AMD 集显、898×518 画布、十二万段同一份实例数据、单帧用 `readPixels` 强制同步读回)里每条线展开成四边形 + MSAA 4× 是 192 ms/帧,换成 `gl.LINES` 112 ms/帧,关掉 MSAA 之后分别是 38 / 23 ms;连发 10 帧只量 JS + 命令提交 0.01 ms(空页面 rAF 7.2 ms、只 clear 7.0 ms)—— 贵的既不是条数也不是 JS,是「细长三角形 × MSAA × 相互重叠」。于是新增**交互期细线快路径** `interactionThinLines`(缺省开:可见线段全部细到 3px 以内、且整批没有虚线时,拖动 / 缩放期间整批改用 `THIN_LINE_VERTEX_SHADER` + `gl.LINES` 画 1 设备像素的实线,停手回到四边形展开;两种画法共用同一张 `lineBuffer`,`lineThinVao` 只是它的第二种读法,所以位置不跳,悬停 / 选中也不受影响),门槛账本(最大半宽 / 是否全实线)在 `layoutScene` / `rewriteLineSlots` / `patchLineSlots` 三处标脏、交互期用到时现算,悬停加粗再变回细线也能降下去;`antialias` 同时进选项与 `pickEngineOptions`(软件光栅仍然自动关),`getStats()` 多出 `antialias` / `software` / `thinLines` 三个读数,`PerfDemo.vue` 面板上加了「交互期细线」与「MSAA」两个开关现场对比(MSAA 改不了运行时状态,改它走销毁重建)。实测(900×560 画布、10 万点 / 12 万线、`quality: 'high'`、同一页交替开关、两次独立运行共 6 轮):交互期帧耗时 p50 **194 ~ 208 ms → 132 ~ 139 ms**,同一 2.2 s 窗口的帧数 **13 → 19 ~ 24**,`thinLines` 命中率 0.94 ~ 0.96。一条更有用的结论:**先看总覆盖量,再谈条数** —— 同样 10 万点换成网格邻居线(19.9 万条、线短不叠)拖动 p50 只要 7 ms,而 PerfDemo 那份数据里「每 5 个点连一条到 0 号点」的 2 万条长线在圆心附近叠了十几层。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 实例成员 200 / 数据字段 217 全命中、十个自测页 847 条断言全绿(`selftest-perf.html` 47 → 55)。
|
|
281
|
+
- 这一轮(继续十万级交互与动画:非 MSAA 后备缓冲):上一轮把「细长三角形 × MSAA」拆开之后,剩下的账很直白 —— 同一份几何 MSAA 关掉快近 5 倍,那交互期干脆不带 MSAA 画。新增 `interactionNoMsaa`(缺省开):拖动 / 缩放 / 框选期间**与动画在跑的那些帧**(`render` 的判定是 `(interacting || animationRunning())`)整帧先画进一张不带 MSAA 的后备贴图(分辨率跟画布一致,尺寸变了当帧重建,建不出来就放弃这一档、这一帧照旧直接画),帧末用一个满屏四边形整幅贴回画布;停手 160ms(或动画停帧)后的那一帧照旧直接画进带 MSAA 的画布,所以**静止画面逐像素不变**(代价是这些活动帧的边缘暂时没有抗锯齿)。透明底 / 自定义底色的导出场景与本来就没开 MSAA 的画布不走这条。实现:`shaders.js` 的 `BLIT_VERTEX_SHADER` / `BLIT_FRAGMENT_SHADER` + `engine.js` 的 `ensureInteractionTarget()` / `blitInteractionTarget()`(后备贴图尺寸跟着画布走、不完整就放弃这一档);`getStats()` 多一个 `noMsaa` 读数、`updateOptions({ interactionNoMsaa })` 运行时可改,`PRESENTATION_ONLY_KEYS` 里与 `interactionThinLines` 同档(只影响下一帧怎么画,不碰实例数据)。真机实测(898×558 画布、10 万点 / 12 万线、`quality: 'high'`、无头 Chrome + ANGLE / AMD 集显):交互期单帧(`readPixels` 强制同步)**133 → 36.6 ms**,真实输入长跑(120 步、两次独立运行)拖动 p50 **130.3 → 16.0 / 21.8 ms**、缩放 p50 **239.5 → 16.7 / 16.6 ms**(p95 399 → 24.8 / 42.2 ms)、平移 p95 **129.1 / 100.5 → 17.3 / 17.2 ms**、动画帧 **212.4 / 212.0 → 53.1 / 53.0 ms/帧**(只开涟漪 187 → 48、只开线路动点 199 → 47,2.6 s 窗口帧数 12 ~ 15 → 49 ~ 57);同一交互状态下开 / 关后备缓冲各抓一帧整幅对拍:差异像素 6.4%、平均差 0.53 / 255、最大差 112(都在抗锯齿边缘);交互期把画布尺寸改掉后备贴图当帧重建(898×558 → 698×418,改完仍走后备缓冲)。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 实例成员 200 / 数据字段 218 全命中、十个自测页全绿(`selftest-perf.html` 55 → 64 条:读数、交互期开 / 停手回、开关关掉 / 改回来、交互期改尺寸仍生效、动画帧同样走后备缓冲,外加「同一交互状态 MSAA 帧与后备缓冲帧差异 < 20% 像素且平均差 < 4」的像素级核对),`PerfDemo.vue` 面板上「交互期细线 / MSAA」旁边加了第三个开关现场对比。
|
|
282
|
+
|
|
283
|
+
- 这一轮(6 条需求:demo 标签的位置与「随便操作就消失」、点击拖动时线路先变细再变粗、大数据下的接口选中、`centerOn`、`selectFeatures`、`getSelectionData`):① **标签**从两处一起收 —— `engine.js` 新增 `ensureChannelGeometry()`,让**读几何**的接口(`lineGeometryData` / `getChannels` / `forEachLine` / `forEachLineInView` / `getLineGeometry` / `listLines`)进门先按当前相机比例把通道回线的折线对一遍(渲染那条路每帧都会对,但标签排版 / 鹰眼图 / 通道范围在两帧之间读,拿到的是旧折线 —— ChannelDemo 里飘在节点群外、离线路老远的「丙通道 · 第 4 回」就是这么来的,几何一刷新又整片跳走);`overlay.js` 把标签的**落地次序**统一成「上一轮显示过的节点标签 → 上一轮显示过的线路标签 → 新出现的线路标签 → 线路补位候选 → 新出现的节点标签」(候选照旧按定义表收,`placeLabelGroups` 只入队、`flushLabelBatches()` 统一放),已经显示出来的标签不再被另一类这一轮的候选顺序顶掉。实测 `?demo=channel`(900×560 无头窗口)走完「悬停扫一遍 / 单击节点 / 拖动节点 / 点空白 / 放大三次」,画面上的 8 条线路标签一条没换;改动前同一页面拖动一次,线路标签从 5 条掉到 0 条。② **拖动时线路先细后粗**:`THIN_LINE_MAX_HALF_WIDTH` 从「CSS 像素 1.5」改成「**设备像素** 0.6」(判定式与顶点着色器对齐:`Math.max(halfWidth * boost, 0.35) * pixelRatio <= 0.6`),1.4px 的线不再落进 1 设备像素的 `gl.LINES` 快路径 —— 有 MSAA 时旁边像素本来就占五成覆盖率,没有 MSAA 时只画满中间那个像素,一拖动 / 一松手就是「先变细再变粗」;同时给没有 MSAA 的那一遍补上边缘覆盖率(线段着色器新增 `u_edgeExpand` / `u_edgeAA`,四边形两侧各外扩 0.5 设备像素、片元按覆盖率渐隐,引擎侧 `LINE_EDGE_AA_EXPAND = 0.5` 与 `lineEdgeAA`)。门槛实测(dpr = 1):1.4px 否 / 1.2 是 / 1.0 是 / 0.8 是 / 虚线 1.0 否 / 6px 否。③ **大数据下的接口选中**:`findFeatureKey` 改走 `locateById` 的 id 表、`isSelected` 与 `selectFeatures` 的去重改走按 `selections` 引用缓存的键集合、通道写法一次解析成回线键。十万点 / 十二万线实测:`select(1000 个 id)` 414.2 ms → 首轮 5.8 ms(表热 0.3 ms)、十万选中下 `isSelected` 问 1000 次 394.7 ms → 11.8 ms(小选中集 270.1 → 0.5 ms)、`getSelectionData()` 5.5 ms、`select(整条通道)` 3.0 ms(再按 `lineIds` 挑回线 0.02 ms)、`focusOn(id, { center: true })` 1000 次 3.3 ms。④⑤⑥ 三个新方法(**后续本轮已收口**:`centerOn` 并进 `focusOn({ center: true })`,`getSelectionData()` 把三份读数合并成一份):`centerOn(target, y?)`(两个数 / `[x, y]` / `{ x, y }` / id / `{ type, index }` / 通道写法 / 一段范围 / `{ lngLat }`,认不出来什么都不做、比例不动)、`selectFeatures(refs, options)`(id、下标、`{ type, index }`、`{ item }`、拾取载荷、搜索命中、通道写法混着写;`mode` 是 `replace` / `add` / `toggle` / `remove`,落地后补一条 `cause: 'api'` 的 `selectChange`)、`getSelectionData()`(ids / count 与点线通道读数、要素本身三个数组、下标清单、items 与包围盒 —— 单击、多选、框选、接口选中都是同一份 state)。文档与类型同步:`docs/GisView.md` 新增「接口选中(select / selectFeatures)与读回选中」一节、方法表与参数速查补三处,`types.d.ts` 补一组类型,`selftest-features.html` 233 → **255** 条断言。
|
|
284
|
+
|
|
285
|
+
- 这一轮(3 条需求:`centerOn` 与 `focusOn` 重复、`focusOn` 要能一批整体居中并顺带选中、检查多余的方法与配置属性):① **`centerOn` 并进 `focusOn`** —— 实例上的 `centerOn` 删掉,能力变成 `focusOn(target, { center: true })`:只挪中心、比例一点不动(两个数 / `[x, y]` / `{ x, y }` / id / `{ type, index }` / 通道写法 / 一段范围 / `{ lngLat }` 照旧全认);不带 `center` 时仍按范围适配比例(`zoomToBounds`),零面积目标先由 `expandBoundsForMaxZoom` 撑出范围(`maxZoom` / `padding`,缺省 40);解析只剩 `resolveFocusTargetBounds` 一处,认不出来什么都不做。② **一批整体居中 + 顺带选中** —— 目标给数组就逐个解析成世界包围盒、再 `unionBounds` 合成一份(搜到三个节点,就是这三个点的中心当屏幕中心);`options.select`(`true` 就是这批目标)复用接口选中那条路 `applySelectionKeys(resolveSelectionKeys(...), { mode })`,`mode: 'add' / 'toggle' / 'remove'` 都成立,落地后补一条 `cause: 'api'` 的 `selectChange`;`{ center: true }` 只居中、`scale` 直接给比例。`search()` 不再动视角,`GisViewSearchOptions` 只剩 `keyword` / `scope` / `fields` / `limit` —— 搜到之后要不要聚焦、要不要选中交给使用方,`GisViewDemo` 的搜索面板加了「搜到就聚焦 + 选中」的勾选框(勾上才 `focusOn(result.hits, { select: true, padding: 60 })`)。③ **多余的方法删掉,配置项审计后保留** —— 实例删掉 12 个重复成员:`centerOn`、`locate` / `locateSelection`(并进 `focusOn`)、`clearSelection`(`select(null)`)、`getSelection` / `getSelectedId` / `getSelectedIds`(`getSelections()`)、`getSelectionInfo` / `getSelectedItems` / `getSelectionBounds`(三份读数并进 `getSelectionData()`:`ids` / `count` / `pointCount` / `lineCount` / `channelCount` / `points` / `lines` / `channels` / `items` / `pointIndexes` / `lineIndexes` / `bounds`)、`getVisibleLines` 与实例 `getVisiblePoints`(`listLines` / `listPoints({ visibleOnly: true })`;引擎侧 `engine.getVisiblePoints()` 是标签层的缓存快照,保留)。配置项逐项过了一遍:`pickEngineOptions` 的白名单 `KNOWN_OPTION_KEYS` 每个键都仍被消费(`fitOnInit` 只做 `applyInitialView` 的门控),**没有删配置**。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 实例成员 **191** / 数据字段 218 全部命中;十个自测页 CDP 真时间全绿(`selftest-features` 259、`selftest-scale` 38 等,见 `docs/steps/README.md`);demo 手点:`?demo=events` 27 个按钮零报错,`?demo=full` 搜索勾选后缩放 10.2 → 64.9 px/单位并选中 4 个、不勾则视角不动,ops 面板 9 个按钮零报错(`focusOn(选中, { maxZoom: 8 })` 后比例正好 8)。
|
|
286
|
+
- 这一轮(`GisViewDemo` 的节点配色按 `getGisStyle` 走 / 真实数据换了新导出):① **节点描边色改成与同电压的线路同色** —— 节点一直是白底加一圈电压色描边(`getGisStyle` 的颜色在节点上指的就是描边色),但描边色原先另配了一套搭配(`DC_NODE_STROKE_COLORS` / `AC_NODE_STROKE_COLORS`:橙 / 琥珀 / 青绿 / 蓝 / 紫 / 灰蓝),跟同电压的线色对不上,看着「不像」`getGisStyle`。现在 `buildGisNodeRules` 直接取分档表里的 `color`:交流 ≥1000 / ≥200 黑、≥500 红、≥110 蓝、≥35 绿、其余 `#408080`,直流按 1000 / 700 / 600 / 450 分五色,缺 `vn_kv` 的兜底也改成 `getGisStyle({}).color` —— `test.js` 写的就是 `itemStyle: { color: '#fff', borderColor: getGisStyle(item).color }`。站点图标里的线条(`currentStrokeColor`)与鹰眼图的小图配色都跟着这同一份色走。② **自测跟着新数据同步** —— 仓库里的 `new.json` 换成新导出之后(7368 站点 / 9230 通道 / 12811 回线,channel 里自带 5 条 `dc_line`),`selftest-demo` 里 3 条「默认数据集里没有 dc_line / 按钮切到样例数据」的旧断言不再成立,改成按数据算出来的条数写(按钮「定位直流线路(5 条)」直接定位第一条、不切数据集),demo 里的说明与注释同步。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过;`selftest-demo` 110 / `selftest-demos` 60 / `selftest-features` 259 / `selftest-full` 68 全绿;无头 Chrome 像素核对:`listLines()` 里 12811 条线按 13 个电压分组颜色全部与 `getGisStyle` 一致(含 5 条 dc_line 的 `#FF3200`),节点逐档量到黑 / 红 / 蓝 / 绿 / `#408080` 的描边与 30 / 18 / 12 / 6 px 的尺寸。
|
|
287
|
+
|
|
288
|
+
- 这一轮(2 条需求:继续优化大数据性能并按场景把 WebGPU 做成开关 / 统一接收与导出格式并给一份包含全部数据的 JSON):① **标准数据文档**(`src/gis-view/document.js`)—— 顶层固定八列(`format` / `version` / `points` / `lines` / `channels` / `labelStates` / `view` / `filter`),节点 / 线路 / 通道 / 拐点(`path` 中间点)/ 标记(`decorations`)/ 标签(`labels`)与标签的运行时状态、相机、筛选全在里面;`normalizeDocument()` 做深拷贝 + JSON 安全(函数 / DOM / 画布 / `Map` / 循环引用跳过并记提醒,SVG → outerHTML、图片 → `{ type: 'image', src }`、`NaN` → null)+ 标准写法(坐标一律 `[x, y]`、`labels` / `decorations` 一律数组、通道一律展开 `groups`)+ 键序保留。整份文档可以直接当 `createGisView` 的入参(`view.camera` → `initialView`),`exportData({ format: 'full' })` 出的就是文档本身(没有 `ok` / `counts` / `warnings`),新增实例方法 `importData(input, options)` 整份 / 分列灌回(顶层出哪列应用哪列,返回 `{ ok, reason, applied, counts, warnings }`);`overlay.js` 的标签定位在要素被筛选隐藏时回落到原始数据,带筛选的文档回灌不丢标签状态。仓库根目录新增 `sample-document.json`(`scripts/build-sample-document.mjs` 从 `sample-full.json` 生成:10 点 / 22 线 / 3 通道 / 39 拐点 / 23 标记 / 44 标签 / 6 条标签状态),演示页「完整样本」多一枚「标准文档(整份)」芯片:与文件逐字节对账、导出 → 重建 → 再导出逐字节相同。② **WebGPU 做成开关**(`src/gis-view/renderer.js`):`renderer: 'auto' | 'webgl2' | 'webgpu'`(布尔值 `true` / `false` 等价于 `'webgpu'` / `'webgl2'`)加一份只碰 `navigator.gpu` 的能力探测(结果缓存、不创建画布)与实例方法 `getRendererInfo()`;当前绘制后端仍是 WebGL2(第 18 章的结论不变:十万级下 WebGPU 省不到填充率与 DOM 覆盖层的开销),`'auto'` 后台探一次不打扰使用方、`'webgl2'` 连探测都不跑、`'webgpu'` 明确报「已回退 WebGL2 + 原因」。③ **网格顶点缓存窗口**:`drawGrid` 原来每帧重建顶点并 `bufferData`,现在按「步长对齐的缓存窗口」(`GRID_WINDOW_PAD` 0.25)复用,真时间实测小步平移 40 帧只有 1 次上传(改前每帧一次)、大跳变 59 次,画面逐像素一致(16.19 已完成)。验证:新增 `docs/steps/selftest-data.html` 41 条断言全绿,十个老自测页真时间逐页全绿,`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 实例成员 193 / 数据字段 219 全部命中。
|
|
289
|
+
|
|
290
|
+
- 这一轮(2 条需求:EventDemo 的合并 / 复制事件弹框 / ChannelDemo 的分组右键交互框):① **合并 / 复制弹框并解释「拖到一起鼠标为什么变」**(`docs/demos/EventDemo.vue`)—— 「拖到一起鼠标为什么会变」现在在面板上看得见:新增的光标读数直接读 `canvas.style.cursor`(事件触发 + 250ms 轮询),拖一个节点压到另一个节点上松手进「待确认合并」、Alt 点击出复制预览、`beginCopyPoint()` 等选点时都走 `cursorStyle.copy` 这一档(按住 Ctrl / ⌘ 还能用 `cursorStyle.ctrl.copy` 再换一份),读数下面写明引擎是在 `updateCursor()` 里切的档;合并 / 复制的中间态各挂一条确认条(与 `DragDropDemo` 的落点草稿同一套写法):`onPointMerge` 的 `pending`、`onPointCopy` 的 `start` / `move` 载荷存进 `shallowRef`,条上的按钮就是载荷给的裁决入口 —— 合并调 `payload.confirm()` / `payload.cancel()`,复制调 `payload.commit()` / `payload.cancel()`(Esc / 右键 / 再点一下也能收)。② **`copy.autoCommit` 真正接到交互入口**(`src/gis-view/engine.js`)—— 原先只有 `beginCopyPoint({ autoCommit: false })` 这条 API 路能停在预览态,Alt 点击与「先点按钮再选节点」两条交互路恒落地;现在 `beginPointCopy` / `insertCopyPreview` 都跟着 `settings.copy.autoCommit` 走,`copy: { autoCommit: false }` 时松开 Alt 的预览停在原地,等确认条上的 `payload.commit()` 或 `commitPointCopy()`(EventDemo 演示的就是这一条)。③ **分组右键交互框**(`docs/demos/ChannelDemo.vue`,参考 teap-gis 的 `ParallelGroupEditorDialog`)—— 显式开 `features: { drag: true, channelMenu: true }` 与 `waypointEditable: true` 之后右键通道回线出框(贴指针位置、按 `getStats()` 的 CSS 尺寸夹取,Esc / × / 点空白处收框),三段式:分组列表点一下 = `setCurrentChannelGroup`(顺带选中这一组的回线、当前组高亮)、每条回线一个下拉(拆成新分组 / 并入第 n 组)+「分配」= `moveChannelLine`(被拒的原因写进日志)、下面一排按钮 = `runChannelAction` 的五个动作(`enabled` / `reason` 由引擎算好,灰按钮的 `title` 就是原因)。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 实例成员 193 / 数据字段 219 全部命中;`selftest-demos.html` 新增第 4 节(合并 / 复制确认条 8 条)与第 5 节(右键交互框 7 条)断言、`selftest-features.html` 新增 5 条 `copy.autoCommit: false` 断言全绿(CDP 真时间:`selftest-demos` 74 通过 / `selftest-features` 264 通过);`selftest-demos` 里「标签交互」那条删除断言(删完标签数 13 → 14)在未改动的 HEAD 上同样失败,是既有问题,与本轮无关。
|
|
291
|
+
|
|
292
|
+
- 这一轮(5 条需求:选中抬层 / 直流标签 svg 图标 / Delete 删除(带确认框、开关、快捷键与事件)/ 通道选中模式 / 复制改为松开左键才结束):① **选中抬层**复核是引擎原有能力(`selectionOnTop` + `raisedPointIndexes` / `raisedLineIndexes`),没有代码改动。② **直流标签 svg**:`docs/demos/gisStyle.js` 的 `dcLineDecorationSymbol` 去掉 `tint`(白底被逐实例染成线路色 → 实心色块),装饰图标恢复成「白底 + 彩色图案」。③ **删除**:`engine.js` 新增 `normalizeRemoveOptions`(`enabled` / `confirm` / `scope`),`features.js` 补第 18 个开关 `remove`,`events.js` 补 `remove` 事件;`create.js` 抽出 `removeRefs` 并新增 `buildRemovePlan` / `removeImpactOfPoint` / `removeLineIdsOfScope` / `executeRemove` / `settleRemove` / `requestRemove` 与实例方法 `requestRemove` / `getRemoveRequest` / `resolveRemove` / `cancelRemove` —— Delete 与接口先出一份载荷(删什么 / 连带什么 / 「只删线路 / 整个分组 / 整条通道」三档范围 / counts 读数),`remove.confirm` 三档裁决(缺省停在 pending 由使用方弹框),一次删除只落一条历史、只发一条 `removeSelected`,确认时数据跟载荷对不上就整条作废;开关关着退回老行为(只删选中的拐点),`shortcuts.remove` 单独可配。④ **通道选中模式**:`channelSelectMode`(`'line'` 缺省 / `'group'` / `'channel'`)与引擎的 `expandChannelSelection`,点击 / Ctrl 点击按模式摊开与整批取反,`updateOptions` 只重画一帧。⑤ **复制落地口径**:松开左键才落地(`handlePointerUp` + `via: 'shortcut'` + `autoCommit`),松开 Alt 不再落地,`EventDemo.vue` 的确认条改成落地之后(`phase: 'end'`)才弹。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 实例成员 197 / 数据字段 221 全命中;`selftest-features.html` 284 条断言全绿,`selftest-demos.html` 80 PASS(唯一 FAIL 是 LabelDemo 既有的标签计数问题,与本轮无关)。
|
|
293
|
+
|
|
294
|
+
- 这一轮(2 条需求:拖动 / 选中时的抬层被电压等级与原有层级限制、GisViewDemo 拖动节点时描边「一动一动」):① **抬层**补了三处漏账 —— `engine.js` 的 `syncRaiseSets()` 原来只重算集合、没标 `lineBatchDirty`,而线路批次里的先后就是画上去的先后(之前只有「有状态样式」那条路标过脏),于是「选中线路 / 通道不抬层、取消选中也不还原」;拖动中的整组(`markDragMoved` → `setRaiseExtraPoints`)只在 `layoutScene()` 里被消费,拖动却走「就地改坐标、不重排」的快路径,实测拖动中 `snapshotScene().raised` 一直是空的 —— 同层拖拽与拖到更高电压等级的节点上都不抬层;矢量导出 `snapshotScene()` 也没按抬层次序输出线路。现在:抬层线路集合一变就标脏重铺;`applyDragCoordinate()` 在起步那一帧先补线路几何(线路跟随与线路标签都吃这份几何,顺序反了这两样会停一拍)、再按新抬层集合整份重排一次(10 万点实测约 21 ms,之后每一帧仍是 0.2 ms 的槽位补丁);导出按同一份次序输出。② **描边一动一动**(GisViewDemo.vue 拖动节点):根因是点精灵的方框(`gl_PointSize`)正好是形状的外接框 —— MSAA 那一遍按方框的采样覆盖率合成、光栅化又按「像素中心在方框内」判覆盖,方框边压住描边的那圈像素只在 MSAA(非交互)帧里被削掉,交互期走非 MSAA 后备缓冲(`interactionNoMsaa`)时没人削它,一拖一停就是「描边一厚一薄」;让方框每边多留 1 个设备像素(`shaders.js` 的 `POINT_EDGE_PAD`:顶点按 `size + 2` 定 `gl_PointSize`、片元用 `v_quadRatio` 把 `gl_PointCoord` 缩回形状自身尺度)就能同时躲开这两种切法,与线路的 `u_edgeAA` 是同一类问题。真机 MSAA 对拍:同一节点拖动帧与松手后的静止帧逐像素一致(差异 0 字节),改之前是 86 字节 / 最大差 35;10 万点满屏 10 → 11.8 ms/帧。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 197 / 221 全命中、`selftest-features.html` 284 → 293 条全绿(新增 9 条抬层 / 交互期画质断言),其余自测页照旧。
|
|
295
|
+
|
|
296
|
+
- 这一轮(1 条续报:节点图标「圆的方的都闪、放大缩小地图也闪」):① **贴图符号的方框吸附到「设备像素边界的栅格」上**(`src/gis-view/shaders.js` 的 `SPRITE_VERTEX_SHADER`)—— 上一轮把吸附从「中心」换到「边」只解决了一半:那一行仍然在**引擎像素**坐标里做(`floor(anchor - halfSize) + halfSize`),而引擎像素坐标的原点在**画布中心**(`gl_Position = pixel * 2 / u_resolution`)、画布中心又正好落在某个设备像素的中心上 —— 画布的设备分辨率不是偶数时(DPR 1.5、奇数宽度的画布),引擎坐标里的「整数边」就全压在**设备像素中心**上:MSAA 那一遍按采样点给半格覆盖率(最外圈描边淡一半),非 MSAA 那一遍(交互期,`interactionNoMsaa`)按「像素中心在方框里」判覆盖、整个像素丢掉 —— 拖动 / 缩放 / 平移一走,贴图图标的最外圈描边整行消失、停手又回来,跟图标的尺寸与圆方都无关。上一轮「只在奇数尺寸时露出来」是测试画布宽高都是偶数(318×238)带出来的读数:偶数画布 + 奇数尺寸才会踩上,奇数画布反过来、跟尺寸无关。修法:设备像素的边界在引擎坐标里是一张固定的栅格(分辨率是偶数时落在整数上、奇数时落在半整数上,就是「分辨率一半」的小数部分),`vec2 grid = fract(u_resolution * 0.5); vec2 anchor = floor(clip * 0.5 * u_resolution + u_offsetPx - halfSize - grid) + halfSize + grid;` —— 偶数分辨率退化成上一轮那一行、逐像素不变(318×238 画布上 20 / 21 / 22 px 三种尺寸 × 四个位置共 12 个图标与旧写法 **0 差异**),奇数分辨率才另走一档。真 GPU 对拍(`--use-angle=d3d11 --enable-gpu`、`antialias: true`):319×238 画布上 20 / 21 px 的贴图符号旧写法「平移帧 vs 静止帧」差 120 / 126 字节、本轮 0;DPR 1.5 的 449×357 画布上,中心吸附 / 上一轮那行 / 本轮分别是 298 / 255、373 / 255、0 / 0。② **交互期的装饰按规模跳过**(`engine.js` 的 `ORNAMENT_INTERACTION_LIMIT = 2000`)—— 装饰(点阴影 / 高亮环 / 高亮圆头 / 高亮线路 / 贴图强调)原来一律跟着 `!interacting` 被跳掉,选中节点的阴影、悬停那一圈光环在拖动 / 缩放期间整块消失、停手 160ms(`INTERACTION_IDLE_MS`)再回来;现在装饰实例总数超过 2000 的才跳过,十万级照旧让位给帧率。真机对拍同一场景(含一个带 `shadow` 的选中节点)「静止帧 vs 交互帧」逐像素 0 差异;改之前同一窗口差 2261 字节 / 最大差 55,差的就是整块阴影。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 197 / 221 全命中、`selftest-features.html` 297 条全绿 —— 本轮新增 4 条:奇数设备像素画布上的「画布宽是奇数 / 平移帧确实走交互期后备缓冲 / 平移帧与静止帧逐像素一致」3 条(第 3 条在上一轮那版公式下报 132 字节、本轮 0)与「拖动帧确实走交互期后备缓冲」1 条;顺带把「拖动帧与松手后的静止帧逐像素一致」那条的等待从 `settle`(150ms,短于 `INTERACTION_IDLE_MS` 的 160ms,两次读到的都还是交互期那一帧、比较恒为 0)换成 `settleMs(400)`,换完才真正比出东西;八页自测全绿(demos 81 / demo 110 / effects 79 / actions 96 / labels 90 / full 68 / scale 38)。
|
|
297
|
+
|
|
298
|
+
- 这一轮(2 条需求:进一步优化性能(大数据下的复杂交互与事件响应速度)/ 参考市面上的大型 GIS 组件):① **标签层的整层重排不再逐条读布局**(`src/gis-view/overlay.js`,本轮唯一改动的源码文件)—— 根因是 `parseLabelTransform` 的正则写错(`0px?` 实际是「`0p` + 可选的 `x`」,而写进去的是 `translate3d(x, y, 0) …`),这条正则**从来没匹配成功过**,`writeLabelTransform` 每次都把刚量好的盒子作废,于是每次重排都要替几百条候选各读一次 `getBoundingClientRect`(每读一次就是一次强制重排)。现在:正则修对、解析结果缓存在 `state.placement` 上;新增按「画上去的那一份签名」(kind / class / 文字 / 内联样式 / 字体代号)的尺寸缓存(LRU 8192,与节点身份解耦)与 `readLabelSize`(量一次,顺带记「量出来的位置 − 从 transform 算出来的位置」的 delta,旋转标签不记);`labelAvoidRect` 改成纯算术(尺寸取缓存、位置从节点自己写下的 transform 里解析),只有签名第一次出现才真量一次;一批里「还没量过、又要参与避让」的节点先全部挂进 DOM 再一趟读完(写读分离,一批只付一次强制重排)。② **一条候选的开销也不再跟定义表 / 候选窗口成正比**:要素自己不带标签输入时整份定义表共享同一张计划(WeakMap 按 defs 缓存,不再每条候选建一张 `byId` Map)、按「当前 labelId 的下标集合」先筛下标再拼 key、候选的状态与拖拽偏移一批只算一次、停靠上限 768 → 4096(停住的节点带着已画好的那一份,下一轮回来时连 class 与文本都不用写)。与 deck.gl / MapLibre 这一类的通用手法同一路数:写读分离、测量结果按内容签名缓存并与节点身份解耦、避让判定不读布局、热路径不拼字符串。实测(无头 Chrome + SwiftShader,10 万点 / 10 万线、54 条 DOM 标签):单次定向更新 `updatePoint` 73.8 ms → 3.5 ms(100 次合计 7383 → 349 ms)、建场景 453 → 257 ms;重标签场景(10 万点、标签上限 1000 / 候选上限 8000、59 条标签)连续五次定向更新 664 / 564 / 515 / 443 / 460 ms → 42 / 44 / 37 / 27 / 24 ms;「改一条标签样式 → 强制整层重排」的布局读数 832 次 / 轮、31 ms → 14 / 2 / 0 / 0 / 0 次、3.5 ms(稳定态一次布局都不读)。`pick` / `hoverAt` / `pointermove` / `select` / `setCamera` / `fitView` 都没有回退。几何一致性:247 条标签(9 种锚点 + 平行线路标签)「解析位置 vs 实测位置」maxDelta = 0。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 197 / 221 全命中、十二页自测 1015 条全绿(本轮新增 perf 2 条 + labels 4 条)。
|
|
299
|
+
|
|
300
|
+
- 这一轮(2 条需求:继续优化框选提交 / 查看更新通道、更新线路时能不能顺带更新线路分组,并确定「更新线路分组」方法有没有必要):① **框选抬起那一下(`pointerup`)不再做无用功**(`engine.js` + `create.js`):选中集整份换新之后抬层集合只比引用(没有拖动 / 合并这类额外抬层的要素时直接引用选中集本身,空集合三处共用 `EMPTY_INDEX_SET`),原来每次提交都要「清空暂存集 → 把 3.4 万个选中下标重填一遍 → 再逐项比对 3.4 万次」的两趟全省掉;`syncSelections` 从 `map` + `filter` + 多次分类扫描改成一趟循环(id 表、键集合、点 / 线 / 通道条数一起收好),按数组引用缓存在 `selectionInfoCache` / `selectKeyCache` 里,`selectionInfo()` / `selectChanged()` / `selectChange` 载荷直接取现成的;选中集的存活校验走 `isKeyAlive` 的键查表(不再为每个键造描述对象、再投影一次);抬层欠下的整份重排记在 `raiseLayoutPending` 上、由 `flushRaiseLayout` 在下一帧渲染之前补,`pointerup` 回调里不再同步排十万级场景;整份重排本身也少扫几趟十万条 —— 点 glyph 从对象池复用、圆点实例直写进批次数组(不再逐条造 `circleFromGlyph` 中间对象)、没有装饰 / 跑动动画 / 阴影 / 逐要素状态样式时各自的十万级空扫直接跳过(`hasLineDecorations` / `hasLineEffects` / `hasPointShadows`)、细线快路径的账本只在「线宽或虚线开关真的变了」时才标脏、`zIndex` 只有一层时不再重铺点顺序。实测(10 万点 / 10 万线、无头 Chrome + SwiftShader,同一个脚本):框住 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**;CPU profile 里 `syncRaiseSets` 6.2 → **0 ms**、`buildLineEffectGlyphs` 3.69 → **0 ms**、`buildLineDecorationGlyphs` 1.66 → **0.62 ms**、`thinLineBatchEligible` 2.2 → **0 ms**。② **更新通道 / 更新线路时顺带更新分组**(对着「修改.txt」第 2 条):复核结论是「两条都值得补,但不必单开一个更新分组的方法」—— 组件本来就暴露了 `updatePoint` / `updateLine` / `updateChannel` 三条定向更新与 `moveChannelLine` / `splitChannelGroups` / `mergeChannelGroups` / `getChannelGroups` / `setCurrentChannelGroup` 这一套分组方法,只差「改线路时顺手挪组」这个入口:现在 `updateLine(id, { groupId })`(或 `groupIndex`,可配 `channelId`)把这条回线挪到目标分组(`groupId: 'newGroup'` 单独拆一组),走的就是 `moveChannelLine` 那一套(组数 / 回线数上限与失败原因照旧,挪不成发一条 warning),这三个字段只是指令、不写进线路数据、也不出现在 `updateLine` 的载荷里;`updateChannel(id, { groups })`(或改 `lineIds` 换成员)写完把引擎算出的最终声明(分组 id + 拐点路径)写回数据副本,并补一条 `channelGroups` 变更。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 197 / 221 全命中、十二页自测 1015 条全绿(特征页 297 条)。
|
|
301
|
+
- 这一轮(样式快路径再切三刀:混合补丁、原样重发与热力图色带):面板上「拖一个滑块」常写成 `updateOptions({ pointStyle, lineStyle, pointHoverStyle, lineHoverStyle })`,之前只要有一个键不在样式表里就整份重建,现在「至少一项样式键 + 尺寸倍率 / 画法开关 / 状态样式 / 回调」这一类都留在快路径上(状态样式先只重解析在悬停 / 选中里的那几个);引擎另外记一份「这一轮重解析用的输入」,节点与线路两半各自比对 —— 原样重发只标一帧脏、只动一半时另一半的十万个要素一个都不碰;`heatmap`(色带 / 值域 / 目标)也进了这条快路径,密度场与光晕标脏、下一帧按新配置重来。圆 / 方的符号缓存从 8 条线性表换成 32 位整数哈希 + 单条热缓存(不再逐点拼缓存键,缺省符号共享同一个对象),只换样式时带装饰的线也不再逐条重算装饰锚点。10 万点 / 10 万线实测:`pointStyle.size` 41.6 → 16.8 ms、原样重发 48.2 → **0.1 ms**、混合补丁 120.6 → 35 ms、`lineStyle.width` 40.4 → 17 ms、换热力图色带 241.9 → 90.1 ms;6 万条带装饰的线换一次线宽 59.6 → 14.9 ms;建场与整份重排没有回退。`docs/steps/selftest-perf.html` 新增 10 条断言,十二个自测页全绿(`selftest-features` / `selftest-demos` 各有既有失败),`npx tsc --noEmit` 0 错。
|
|
302
|
+
- 这一轮(继续优化:定向更新的线路标签候选复用):`updatePoint` / `patchPoints` 只改节点侧(补丁没动坐标 / id、筛选也没开)时,线路标签层接着用上一轮收好的候选 —— 相机视口、画布尺寸与「上一轮显示过的下标集合」逐项核对(`overlay.js` 的 `lineCandidateCache`,标签定义对象就是缓存键),十万条线的视口扫描整个省掉;改线路内容 / 挪节点 / 换 id / 筛选联动时按老样子整层重扫。10 万点 / 10 万条长线 + 两层标签(DOM 75 条):单次 `updatePoint` p50 **4.7 → 1.7 ms**(只配节点标签时 1.8 → 1.6 ms,没有回退),CPU profile 采样 531 → 226、`forEachLineInView` 从热点清单消失。
|
|
303
|
+
- 这一轮(继续优化:超长线段换更粗的一层网格):线段网格对「一层里铺不下的长线」原来只记一条包围盒、查询按盒子筛 —— 长线密布时几乎等于全量重算。现在换成更粗的一层网格(每层格子边长 ×4,粗层也只登记线段真正穿过的格子,跨层 stamp 去重),候选从「包围盒相交」变成「几何上真的经过」。10 万条随机长线(4000×2500)实测:一次查询的候选 11101 → **2210** 条、查询自身 0.14 → 0.02 ms,`pick` p50 / p95 / 平均 **0.9 / 6.2 / 1.90 → 0.3 / 1.7 / 0.53 ms**;代价写在明处:极限数据集建网格 16.3 → 41.6 ms、内存 +26 MB,十万条短线那档不变(建表 9.6 → 8.6 ms、`pick` p95 0.1 ms)。
|
|
304
|
+
- 这一轮(继续优化:标签避让盒不再逐条读布局):标签避让盒本该是纯算术(尺寸按内容签名缓存、位置写在 `transform` 里),但 `parseLabelTranslate` 只认「两段都是百分比」,最常用的默认右锚点 `translate(0, -50%)` 解析不出来 —— 尺寸命中缓存、盒子又作废的标签每次重排都要退回真量一次布局(每条一次强制重排)。现在位移的每一段都认(百分比按自身尺寸折算、像素 / 无单位的 0 是绝对位移),盒子全走算术。10 万点 + 10 万条长线、两层标签实测:放大后一次整层重排的标签量测 **7255 次 / 616 ms → 18 次 / 0.1 ms**,连续 4 次 `updateOptions` **166 / 190 / 216 / 200 ms → 13 / 12 / 10 / 15 ms**,之后 100 次定向更新 `updatePoint` **p50 177 → 6.1 ms**、平均 187 → 8.7 ms;同一轮里被量第二次的标签(1235 条)清零,画出来的标签逐字不变。
|
|
305
|
+
- 这一轮(继续优化:发散渲染的批次接着写,前缀不再重排、不再整份重传):`progressiveInit` 铺批次时每一批都把整个前缀重排一遍、整份重传一次 —— glyph 池 / 圆点与贴图拆分 / `circleRuns` 每批从零重建,实例数组按条数重新分配再整份 `bufferData`,线路批次也跟着每批重建(其实它只由解析结果与像素比决定)。现在每批只排新铺到的一段(`buildFrom` / `buildCursor`,`buildMask` 只往后 OR),前缀的拆分结果与池子下标按累加接着写,上传换成 `bufferSubData` 只传新增段(容量在开始前按预计的最终条数占好),排批另配一条 12 ms 的预算让帧数只减不增。实测初始化期间的上传:10 万点 + 10 万线 **71.14 → 36.97 MB**、30 万 + 30 万 **213.43 → 108.72 MB**、2 万点 **3.24 → 2.92 MB**;建场景同步耗时与 `ready` 墙钟不变(省的是每帧重复的布局与流量)。同步把 `selftest-perf` 里「没有哪条标签被量第二次」那条断言从按 DOM 节点去重改成按标签身份去重 —— 标签节点是池化复用的,按节点去重会误报(实测 81 条假重复),旧写法实测 1235 条、新写法 0 条。
|
|
306
|
+
- 这一轮(2 条需求:继续优化性能 + demo 一条「电压等级高的线在低的线之上」的规则):① **线路批次的视口裁剪**(`engine.js`):线路批次一直是一次实例化铺完全部线路,放大之后视口之外的线每帧照样过一遍顶点变换 —— 现在铺批前按「视口 + 线宽余量(max(最粗半宽 × 宽度倍率 × 热力光晕, 4px) + 2px)+ 25% 富余」逐段试交(`segmentHitsRect`,slab 法;只看包围盒对又长又斜的线只能筛掉六成),裁掉的线槽位记 -1,拖拽补丁见到 -1 先问一句「进视野了吗」(进了退回整份重排),相机停手时 `syncLineCull` 只在「跑出安全矩形 / 面积缩到一半」时重铺(其余帧只花几次比较)。10 万点 + 10 万线实测:放大 12 倍时实例 100000 → 21600、空闲重画 118 → 99 ms、平移 129 → 103 ms;整份都在视野里时不裁(377 ~ 392 ms 是噪声)。无头 SwiftShader 的每帧成本主要是填充率,实例少五倍不等于帧快五倍 —— 放大后的墙钟收益 15% ~ 20%,如实记着。② **线路 `zIndex`**(`line.js`):要素自身 > 规则 > `lineStyle` > 0,`resolveLineLayers()` 计数排序后按层铺批(同层内仍按数据顺序,没人写就整块跳过),`updateLine` 改层号走整层重排;demo 的电压配色规则每档写上 `zIndex`(取该档起始电压、缺 `vn_kv` 的兜底档 0),面板新增「线路按电压分层(高电压线路在上)」勾选框(默认开,跟着「按属性批量配色」一起灰)。`docs/steps/selftest-perf.html` 新增 3 条裁剪断言(8000 → 27 → 8000、像素差 0)、`selftest-features.html` 新增 4 条层序断言(交叉处像素:默认蓝 → 规则红 → `updateLine` 蓝 → 写回 0 红),`npx tsc --noEmit` 0 错。
|
|
307
|
+
|
|
308
|
+
- 这一轮(4 条需求:节点拖拽实时开关 / demo 增加放大缩小限制 / 继续优化文本标签防重叠 / 完善 demo 与 md 并梳理交互):① **拖拽的实时开关**:引擎侧本就有 `liveDrag`(缺省 `true`;`false` = 拖动期间线路与通道原地不动、松开指针后一次算完),这一轮把它摆到 demo 上 —— `GisViewDemo.vue` 的「交互开关与快捷键」面板加了「线路实时跟随」开关与两档读数,`docs/GisView.md` 的「拖拽点」一节补了面板位置与两档的差别。② **demo 的缩放限制**:`GisViewDemo.vue` 的「初始视图与背景」面板加了 `minScale` / `maxScale` 两个输入框与「以当前比例设限(÷5 ~ ×5)/ 试一下下限 / 试一下上限 / 解除限制」四个按钮 + 读数(当前比例 · 限制区间,等于缺省时明说「不限制」);`docs/GisView.md` 新增「缩放范围(minScale / maxScale)」一节:滚轮 / 双击 / `setCamera` / `zoomTo` / `zoomBy` 与初始视图都被夹在这两条线之间,`updateOptions({ minScale, maxScale })` 只改配置、不动当前相机。③ **标签防重叠的第二条路**(`src/gis-view/overlay.js`,本轮唯一改动的源码文件):节点标签撞上之后**换锚点**再试(新选项 `anchorFallback`,缺省 4:先换对侧、再按左 / 上 / 下…绕一圈,找不到空位才不画;换向只改 class 与 transform、内容与尺寸都不重量),线路标签撞上之后**沿线滑动**(新选项 `slideFallback`,缺省左右各 2 步、左右交替,落点只取视口里的 —— 滑出视口的落点下一轮会被候选筛选丢掉、看起来就是闪一下没了,滑过去的位置记进这条线的 meta、下一轮接着走不来回跳),底图的区域文字(`background.geoJsonStyle.label`)改成只占「软」网格 —— 标签先躲它、躲不开照画(数据标签优先于底图上写死的文字);`create.js` 的选项白名单与 `types.d.ts` 同步,`docs/GisView.md` 的节点 / 线路标签两节各补一段,`selftest-labels.html` 新增 11 条断言(换锚点 / 沿线滑动 / 关掉回旧行为 / 整片盖住照画 / 底图文字只算软冲突),`selftest-perf.html` 的稳态重排断言改成「后 3 轮一次布局都不读 + 换落点补画的新标签各只量一次(合计 59 次、随后回零)」(89 → 90)。④ **demo 交互规则梳理**(`GisViewDemo.vue`):右侧面板收成「页签(视图 · 交互 / 样式 · 标签 / 数据 · 通道 / 开关 · 历史)→ 一排按钮 → 弹层」,弹层 Teleport 到 body、点空白 / Esc 收掉,和节点 / 通道 / 画布三份右键框互斥(弹一份就收另一份,同一时刻只留一份),点画布那一下照旧落到画布上、不吞事件;`docs/GisView.md` 的「说明」一节重写 demo 那段。回归:`npx tsc --noEmit` 0 错、`npm run build:lib` 通过(`dist/index.js` 601.45 kB / `index.cjs` 459.34 kB / `gis-view.css` 4.73 kB)、十二页自测逐页 0 FAIL(共 1098 条:actions 96 / data 41 / demo 121 / demos 81 / docs 26 / effects 79 / features 301 / full 68 / labels 111 / perf 90 / scale 65 / selftest 19)、`node docs/steps/check-doc-coverage.mjs` 实例成员 197 / 数据字段 224 全命中。
|
|
309
|
+
|
|
310
|
+
- 这一轮(4 条需求:继续优化组件性能 / 提高组件后续的可扩展性 / 完善 md 和 demo 提高可读性 / 优化 demo 展现形式):① **百万级首屏分环不再调 `Math.hypot`**(`engine.js` 的 `startProgressiveBuild`):第一趟只算距离平方并记最大值(平方与距离同为单调,先后次序逐条不变),环号(含层号)只算一趟、记进 `Uint32Array ringIndexes` 供计数与回写两趟复用,环按「每环覆盖的面积一样大」均分(外层环不再越排越宽);无头 Chrome + ANGLE / D3D11、100 万点的建场景同步段 **317.6 ~ 342.3 ms → 297.0 ~ 300.0 ms**,填充 / 就绪不变(187.6 ~ 204.3 ms)。② **运行时插件 `view.use(plugin)`**(`create.js`):与 `options.plugins` 同一套写法(`view => hook`)与清理口径,调用即执行、返回**幂等**卸载函数(重复调用只清一次、实例销毁之后再调也安全),执行与卸载收进 `loadPlugin(plugin, warn)` 一处(非函数只提醒不抛错、`destroyed` 之后不再执行、初始化抛错走 `onError` 不打断别的插件),`pluginHooks` 从 hook 数组改成 `{ hook, alive }` 条目数组(销毁倒序回调前先落 `alive = false`),`types.d.ts` 补 `use(plugin): () => void`。③ **md 与 demo 的可读性**:`docs/GisView.md` 顶部新增分组目录、「插件」一节补 `view.use(plugin)` 小节(写法 + 示例 + 要点)、实例方法表补 `use` 一行;`docs/index.html` 的渲染器认 GitHub 口径的页内锚点(标题 `data-slug`,`[一节](#锚点)` 只滚动、不改地址栏)。④ **demo 展现形式**:`docs/demos/demos.js` + `DemoShell.vue` 把页签按「基础 / 交互 / 样式 / 数据 / 性能 / 综合」分组、每份演示的说明旁挂对应文档直链、`[` / `]` 前后翻页签(焦点在输入框里不抢键)、`localStorage` 记住上次看的演示;`GisViewDemo.vue` 的「实例方法速查」面板加 `use(插件)` / `卸载插件` 两个按钮与读数,`docs/GisView实现原理.md` 新增 15.50 一节记档(并把 16.33 的 ③ 标为已完成)。回归:`npx tsc --noEmit` 0 错、`npm run build:lib` 通过(`dist/index.js` 602.29 kB / `index.cjs` 459.89 kB / `gis-view.css` 4.73 kB)、十二页自测逐页 0 FAIL(共 1121 条:actions 96 / data 41 / demo 126 / demos 87 / docs 28 / effects 79 / features 311 / full 68 / labels 111 / perf 90 / scale 65 / selftest 19)、`node docs/steps/check-doc-coverage.mjs` 实例成员 198 / 数据字段 224 全命中。
|
|
311
|
+
|
|
312
|
+
- 这一轮(2 条需求:继续优化组件性能 / 标签显示增加优化开关):① **标签显示优化开关 `labelOptimize`**(顶层、缺省 `false`)—— 缺省档「强制显示」:不做避让、不按 `maxCount` 截断、不设 `scanLimit` / 扫描预算,线路标签也不做屏幕小格去重,视口里该出现的标签一条不少;显式 `labelOptimize: true` 才是「优化显示」,与从前逐字一致(避让 / 换落点 / 锚点回退 / 沿线滑动 / 数量与候选上限全生效)。它只管「画不画、画哪几条、画在哪儿」:`visibility` / `when` / 拖拽 / 编辑 / 样式与动画都不受影响;切换走 `updateOptions` 的标签快路径,引擎一帧都不重排(`create.js` 的 `KNOWN_OPTION_KEYS` 认得它、非布尔值会点名)。② **性能**:强制显示档连底图区域文字那张「软」网格都不建、不铺种子(`avoid=false` 时避让判定整段不走);两档实测(`scripts/bench-scale.mjs --label=true`,20000 点全在视口里、程序化连续改相机):优化档 57 条标签 DOM、平移 / 缩放 17.19 / 17.48 ms 一帧、首屏铺满 45.9 ms,强制显示档 20000 条、187.5 / 390.7 ms、483.2 ms —— 十万级数据显式开优化档,标签不能丢的小数据集就用缺省档。③ **demo 与文档**:`GisViewDemo.vue` 的节点标签面板加「标签优化」勾选框(默认打开,关掉当场能看到全部标签)与两档读数,`LabelDemo` / `ChannelDemo` / `StyleDemo` / `PerfDemo` / `EventDemo` / `DragDropDemo` 与 step-10 ~ 12 显式带上 `labelOptimize: true` 保持原口径;`docs/GisView.md` 新增「标签显示优化开关(labelOptimize)」一节、顶层配置表一行、三处字段表脚注与 FAQ / 大数据量两处改口,`docs/GisView实现原理.md` 记 15.51。
|
|
313
|
+
|
|
314
|
+
- 这一轮(3 条需求:interface 兼容层不进组件 / 继续优化性能 / 所有开关缺省全关):① **兼容层移出组件**:`src/gis-view/interface.js` 整份删掉 —— 组件只认 `points` / `lines` / `channels` 三列,「后端列名 → 三列」的映射连同切场景、分批通道、loading 收尾整套搬进演示侧 `docs/demos/InterfaceDemo.vue`(页签「接口数据」,`selftest-demos.html` 对着 `real.json` 有三条断言);用法见 `docs/GisView.md` 的「接口列名不一样:映射放在调用方」。② **性能再切三刀**:`create.js` 的 `applyData` 在没有筛选时跳过那趟 `applyFilter(null)`(百万点 `updateData` 稳态 181.2 → 169.8 ms);新增 `collectLineIds` / `uniqueLineId`,`addChannel` / `addChannels` 的 id 查重从「每加一条重扫全部 lines」改成「一次新增摊平一张 Set 表」(2 万点 / 20 万线连续 `addChannel` 约 200 ~ 290 ms,profile 里 `uniqueLineId` 4.8% → `collectLineIds` 3.3%);`engine.js` 的 `resolveFeatures` 两趟循环顺路把热力光晕记进三张表,画前那次整份 `refreshHeatGlow` 扫描取消(profile 里 2.0 ~ 2.9% 自耗时消失);`docs/steps/bench-scale.html` 新增 `updateData` / `updateDataSmall` / `addChannel` 三个 ops 与 `repeat:名字:次数` 前缀。③ **所有开关缺省全关**:`features.js` 的 `featureDefault` 推平成一律 `false`(`tooltip` / `label` / `lineLabel` / `minimap` 这几项「配了才有」的仍跟随详细配置),查看类的详细配置改成 `interactive: true`(pan / zoom / contextMenu)与 `clickable: true`(hover / select),`engine.js` 的 `pan` / `zoom` / `hover` / `select` / `contextMenu` 与 `showGrid` 引擎级缺省全改成关,预设 `'view'` 显式打开查看类那六个开关、`'static'` 与缺省一致(空表);自测页与 demo 里要用交互的地方全部显式打开,并补「`setFeatures` 没写的开关回到全关」的断言。回归:`npx tsc --noEmit` 0 错、`npm run build` / `npm run build:lib` 通过(`dist/index.js` 606.35 kB / `index.cjs` 462.95 kB / `gis-view.css` 4.73 kB)、十二页自测逐页 0 FAIL(共 1146 条:actions 96 / data 54 / demo 129 / demos 92 / docs 28 / effects 79 / features 311 / full 68 / labels 115 / perf 90 / scale 65 / selftest 19)、`node docs/steps/check-doc-coverage.mjs` 实例成员 202 / 数据字段 226 全命中。
|
|
315
|
+
- 这一轮(4 条问题:拐点数据怎么存 / 通道没写 `groups` 能不能渲染、怎么新建分组 / JSON 格式能不能标准化 / 导出数据长什么样,见「修改.txt」):① **拐点不用单开一个对象**(复核结论:不改)—— 折线的中间顶点就是拐点:线路写在 `lines[].path`(首尾是端点、中间才是拐点),通道与分组写在 `channels[].path` / `channels[].groups[].path`(只写中间,两端由 `from` / `to` 补齐);读数与统计早就有(`getWaypoints` / `getChannelGroups` / `documentCounts`),业务侧要「单独一份」就在导出里拿 `parallel_groups`(`{ group_id, member_line_ids, group_route }`,`group_route` 含两端、中间就是拐点)。② **没写 `groups` 的通道本来就照常渲染**:`channelGroupsOf`(create.js)与 `normalizeDocument`(document.js)是同一份兜底 —— 成员取 `lineIds`、骨架拐点取 `path`、组 id 用通道 id,短写法与 `groups: [{ id, lineIds, path }]` 读出来是同一条通道;要从这样一份数据里分一组出来,这一轮补了一个入口 `addChannelGroup(channelId, lineIds, options?)`(`engine.js` + `create.js`,`moveChannelLine` 的批量版):一次把几条回线挪成新的一组,缺省照抄来源组当前的骨架(拆出来那一组画面不会跳),`options.path` 自己给这一组的中间拐点、`path: []` 要一条直连,被挪空的分组整组消失、其余分组按分组 id 保留拐点与编辑标记,组数 / 回线数上限(`channelMaxGroups` / `channelGroupMaxLines`)与失败原因照旧,不属于这条通道的回线进 `skipped` 不影响其余条目,成功走 `syncChannelGroups`(一条历史 + 一条 `channelGroups` 变更,声明写回数据副本);一条一条挪仍用 `moveChannelLine`,整份重排用 `splitChannelGroups`。③ **标准化就是 `format: 'full'` 那一份标准文档**:接收与导出同一套八列(`format` / `version` / `points` / `lines` / `channels` / `labelStates` / `view` / `filter`),坐标一律 `[x, y]`、`labels` / `decorations` 一律数组、通道一律展开 `groups`,深拷贝 + JSON 安全 + 键序保留,`createGisView(dom, doc)` 与 `importData(doc)` 都吃它;业务列名(`station_data` / `line_data` 那一套)到三列的映射仍留在调用方(`docs/demos/InterfaceDemo.vue`)—— 上一轮才把兼容层搬出组件,这一轮不往回搬。④ **导出的五口径**见 `docs/数据格式.md` 的「导出数据」表:`'view'`(`points` / `lines` / `channels` / `parallelGroups`)、`'new'`(`station_data` + `channel_data` + 四至与母线清单)、`'test'`(`point_data` + `line_data` + `parallel_groups`)、`'input'`(与创建入参同构、回灌用)、`'full'`(标准文档)。⑤ **演示里露出来**(`docs/demos/ChannelDemo.vue`)—— 「分组」那一块多了勾选块:勾几条回线点「勾上的拆成一组」就走 `addChannelGroup(channelId, lineIds, { id?, path? })`,两项选项分别演示 `options.path`(照抄来源组当前的骨架 / 写 `[]` 直连)与 `options.id`(留空按 `通道 id::N` 排),读数报出新组、第几组与共几组,`docs/demos/demos.js` 的简介也跟着改一句。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 实例成员 209 / 数据字段 226 全命中、十二页自测 1191 条全绿(数据页新增 12 条:没写 `groups` 的通道渲染、短写法 == 长写法、`addChannelGroup` 的拆组 / 照抄骨架 / `path: []` 直连 / `skipped` / 已单独成组 / 找不到通道 / full 导出;演示页新增 7 条走面板勾选的 addChannelGroup 断言)。
|
|
316
|
+
|
|
317
|
+
- 这一轮(2 条需求:写一个「分组增删改 / 拐点增删改 / 场站改坐标 + 保存」的接口演示、方便定接口传参 / 新增「导出视口内数据」的方法):① **`exportViewportData(options?)`**(`create.js` + `types.d.ts`)—— 与 `exportData` 同一套口径与选项(`format` / `parts` / `labels` / `box`),范围换成当前视口:节点按中心落在 `getViewRect()` 里、线路按包围盒沾到视口、通道取「成员回线有一条在视口里」的那些(分组一起跟着走、不拆散);`margin` 是**屏幕 CSS 像素**外扩(按当前 scale 换算,贴着边的一圈也带上),`rect` 可以直接给一份世界矩形替掉视口,返回多带 `viewRect` / `margin` 两条读数。实现是把筛出来的下标交给同一套 `exportData`,通道那一侧传的是 **id**(`getParallelGroups` 按分组 id / 通道 id 认,传下标时 `parallelGroups` 会被整份滤掉 —— 自测里这个 0 → 1 就是这么发现的)。② **新演示「编辑与保存」(`docs/demos/BackendSyncDemo.vue`,`?demo=backend`)**:四类改动的接口请求体一份份摆在面板上 —— 场站位置 `PATCH /gis/points/{id}`(`movePoint`)、拐点 `PATCH /gis/lines/{id}/path` 与 `PATCH /gis/channels/{id}/groups/waypoints`(`addWaypoint` / `removeWaypoint` / `setLineWaypoints` / `setChannelPath`,给的是**整份** path 而不是 delta)、分组 `PATCH /gis/channels/{id}/groups`(`addChannelGroup` / `mergeChannelGroups` / `moveChannelLine`,body 一律是 `getChannelGroups()` 读回来的整份 `groups` 声明)、保存 `PUT /gis/documents`(`exportData({ format: 'full' })` 的八列标准文档);body 统一带 `baseVersion`(乐观锁)与 `opId`(幂等键),配上待提交队列、「提交整批」、勾选撞版本看 409 与「回灌服务端那一份」(`importData`),以及 `exportViewportData` 的视口读数与样例。文档:`docs/GisView.md` 新增「视口内导出」一节 + 五套口径 / 分项表 / 主表 / 参数速查四处各一行,`docs/数据格式.md` 的「导出数据」补一段,`docs/demos/demos.js` 把新演示挂到「数据」分组,`api-index.js` 重新生成(实例成员 209 → 210)。验证:`npx tsc --noEmit` 0 错、`npm run build` 通过、`node docs/steps/check-doc-coverage.mjs` 实例成员 210 / 数据字段 226 / options 82 全命中、十二页自测 1212 条全绿(数据页 66 → 73:视口筛、`viewRect` / `margin`、margin 外扩、`format: 'input'`、`parts`、`rect`、`format: 'full'`;演示页 105 → 119:新页签冒烟 2 条 + 12 条断言 —— 五条接口约定、场站改坐标、拐点增 / 改 / 删、分组增 / 删、整份保存、提交整批、409、回灌、视口导出)。
|
|
318
|
+
- 这一轮(2 条需求:优化鹰眼图的「可配置 / 可扩展 / 可编辑」、继续优化组件性能):① **鹰眼图三处收口**(`minimap.js` / `gis-view.css` / `types.d.ts`):容器可以加 `className` 与 `style`(`applyExternalStyle` 认中划线键、CSS 变量键与驼峰键,换配置时上一轮写过、这一轮没写的键会被清掉);`extent` 除 `'auto'` / `'data'` / `'background'` 三档预设外还能写死一份世界范围(`{ minX, minY, maxX, maxY }` 或 `[[minX, minY], [maxX, maxY]]`);新增 `draw` 钩子 —— 每帧在底图 / 线路 / 节点之后、`syncBox()` 之前拿到 `{ context, canvas, width, height, ratio, zoom, scale, bounds, extent, project, unproject, data, background }`(外面包 `save()` / `restore()`,`project` / `unproject` 与内置图层同一个投影)(同一轮还给视口框加了 `boxEditable` 的四角把手,已在下一轮按「去除鹰眼图的框可编辑」整块删掉 —— 两个字段、把手 DOM 与 `.gis-view__minimap-handle` 样式、`resize` 拖动模式、自测两条断言与 demo 开关一并去掉)。类型侧新增 `GisViewMinimapExtent` / `GisViewMinimapDrawData` / `GisViewMinimapDrawInfo`,`GisViewDemo.vue` 面板补「自定义层(指北针 + 中心十字)」开关。② **性能两条**:`exportImage` 的编码走新增的 `canvasToDataUrl()`(`toBlob` + `FileReader`,失败回落同步 `toDataURL`),实测 1280×800 30.7 → 4.8 ms(主线程那一段 4.3 ms)、4000×3000 80.0 → 61.2 ms(主线程 41.8 ms);热力光晕账本改成写实例时就地记(`writeCircle` / `writeLine` / `writeSpriteInstance`、追加点那一段循环与 `patchPointSlots` 的贴图分支按 `heat > 0 && opacity > 0` 置位,与解析那一趟同一口径),补丁路径不再整份重扫 `refreshHeatGlow`,顺手把样式快路径漏写回的 `point.heat` 补上(线路侧一直有)。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS(实例成员 212 / 数据字段 226 / options 键 27 / options 表 82)、十二页自测 1223 条 0 FAIL(`selftest-full` 68 → 73:类名、内联样式、写死 extent + draw 钩子、四角把手 4 个、拖角之后 scale 35.4200 → 22.4828 / 框宽 109.5 → 139.0)、`npm run build` 通过。
|
|
319
|
+
- 这一轮(2 条需求:去除鹰眼图的框可编辑 / 线路怎么设线形):① **整块删掉鹰眼图的框可编辑**:`minimap.js` 去掉 `boxEditable` / `boxHandleSize`、四角把手那一整段(`HANDLE_CORNERS` / `handles` / `syncHandles` / `handleAt` / `hideHandles` / `handleSize` / `viewportAspect` / `resizeBoxTo` / `MIN_RESIZE_BOX`)、`'resize'` 拖动模式与 `setDragCursor` / `syncHoverCursor` / 指针三处 / `applyOptions` 里的相关分支,`gis-view.css` 删 `.gis-view__minimap-handle` 两段,`types.d.ts` 删两个字段,demo 删「框可编辑」开关 / watch 条目 / hint 一句,`selftest-full.html` 删「四角出现把手」「拖角之后 scale / 框宽」两条断言(页内 73 → 71);`docs/GisView.md` 的 options 表、`draw` 行与鹰眼图说明段同步改口(写明已按需求去掉)。视口框只剩整体拖动 + `zoomable` 下的滚轮 / 框选。② **线形设置口径落成文档**:`solid` / `dashed` / `dotted` / `dashdot`(别名 `none` / `line` / `dash` / `dot` / `dash-dot` / `dash-dot-dot`)三条入口本来都支持 —— 逐条 `lineType` / `dash` / `dashOffset`(运行中 `updateLine` / `patchLines`)、规则 `lineRules`、全局 `lineStyle`(运行中 `updateLineStyle` / `updateOptions`);优先级 **状态样式 > 要素自身 > 规则 > `lineStyle` > 实线**,图案是 CSS 像素、同一套图案 + 相位共用 16 个槽位、虚线不补圆头、交互期细线快路径遇虚线退回常规批次。`docs/GisView.md`「线型」一节补「运行中改线型」小段、常见问题补「线路怎么设线形」,demo 的「线型」下拉提示补三种写法。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS(实例成员 212 / 数据字段 226 / options 键 27 / options 表 82)、十二页自测 1221 条 0 FAIL(`selftest-full` 73 → 71:删掉把手与拖角两条)、`npm run build` / `npm run build:lib` 通过(`index.cjs` 471.45 kB / `gis-view.css` 4.73 kB)。
|
|
320
|
+
- 这一轮(1 条问题:父元素 CSS `zoom` 会不会影响组件、要怎么改):复核结论是**已经自适应、不用改代码** —— 显示比例 `displayScale` = `getBoundingClientRect()` 的视觉尺寸 ÷ `clientWidth` 的布局尺寸,画布备份像素按「设备像素比 × 显示比例」开,线宽 / 节点尺寸 / 字号 / 指针换算 / DOM 标签 / 鹰眼图都按布局像素算,跟着旁边的 DOM 一起缩放;运行期改 `zoom` 由设备像素那一档 `ResizeObserver` 自动补一次重排(逐帧采样:第 2 帧起显示比例 / 备份像素 / 标签偏移 / 指针命中全部对齐),`transform: scale` 挂载时写死不用管、运行期改它要自己调一次 `view.resize()`(窗口 resize 也会带上)。口径写进 `docs/GisView.md` 的「宿主缩放」、常见问题与 `resize()` 方法表,链路与实测记在 `docs/GisView实现原理.md` 15.57,自测在 `selftest-full.html` 补两条断言。
|
|
321
|
+
|
|
322
|
+
- 这一轮(1 条问题:接口数据是先 createGisView 还是等回来再建 + demo 露出宿主缩放):① **先建实例、接口回来 `updateData`** —— 空实例第一次拿到数据时 `buildScene` 按 `initialView` 自动落一次位(容器 0×0 走 `fitPending` / `pendingFitBounds`,显示出来补做),无头 Chrome 实测空实例 + `updateData` 的首落位与带数据建实例逐位一致、总耗时也一致(5 万点 + 2 万线:10.1 + 58.7 ms 对 67.2 ms),`updateData` 还会在历史里记一步;真正的取舍是状态归属(相机 / 筛选 / 选中 / 标签 / 历史挂在实例上,等接口再建再重建会全丢)。演示加在 `docs/demos/InterfaceDemo.vue` 的「模拟接口(延迟 900ms)」(先清空 + loading → 回来 `updateData` → `whenDataRendered` 收 loading),口径写进 `docs/GisView.md`「动态换数据与加载完成」新小段与常见问题,`selftest-demos.html` 补两条断言(125 → 127)。② **demo 露出宿主缩放缓一把 + 修掉一处真 bug**:`GisViewDemo.vue`「视图 · 交互」面板新增「宿主缩放 · 父元素 zoom」滑杆(0.75 ~ 1.5,加在 `.demo-stage` 的 CSS `zoom` 上,读数给出 `displayScale` 与布局尺寸);补自测时发现把 zoom 调回去组件量不到 —— `engine.js` 的两档 `ResizeObserver` 原来挂在同一个实例上,同一实例对同一元素再 `observe` 一次是**改选项**、把内容盒那一档顶掉了(CSS `zoom` 下设备像素盒只在个别取整边界上变),改成内容盒 / 设备像素各挂一个实例(`dispose` 一并断开)后放大缩小都跟得上,`selftest-demo.html` 补三条断言(129 → 132)。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS、十二页自测 1228 条 0 FAIL、`npm run build` / `npm run build:lib` 通过。
|
|
323
|
+
|
|
324
|
+
- 这一轮(1 条问题:场站位置 / 拐点的改动是改完就传还是点保存一起传):① **结论是攒批(点保存一起传)** —— 拐点与分组给后端的是整份覆盖(`line.path` / `channel.group.path` / `channel.groups`),逐次发的是一串中间态;攒批让乐观锁 `baseVersion` 的冲突窗口最小,撤销 / 重做也不产生请求。要「改完就传」就自己加三条:同一目标**合并**(只留最后一份)、停手**延迟**(`phase === 'end'` 才是落地,`start` / `drag` / `move` 中间态一条都不发)、失败**回滚**(409 / 网络失败留着重试或 `importData` 回灌)。演示页「编辑与保存」(`?demo=backend`)新增「推送策略」面板:勾上「改完就传」按合并键替换队列里的旧请求、延迟 260 ms 模拟一次网络往返自动发,画布上拖节点 / 拖拐点走同一套。口径写进 `docs/GisView.md` 的新小段与常见问题。② **顺手修掉两处通道分组的真 bug**:`engine.js` 的 `buildWaypointPayload` 少给 `channelId`,画布 / 接口改通道回线的拐点后 `create.js` 的 `syncChannelGroupDeclaration` 被跳过 —— `getOptions().channels` 与 `exportData({ format: 'full' })` 里的 `groups[].path` 弹回旧骨架(探针实测三处一致:engine 0 → 1 / options 0 → 1 / doc 0 → 1);跟着暴露的 `setChannelPath` 对分过组的通道静默失效(顶层 `path` 被 `groups[].path` 盖住)也一并修了:认分组、写第一组的声明,要改别的组用 `updateChannel(id, { groups })` 整份覆盖。demo 的面板加 / 删拐点还去掉了重复入队(公开 API 的回调同一次调用里同步发出来)。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS、十二页自测 **1236 条 0 FAIL**(`selftest-demos` 127 → 135)、`npm run build` / `npm run build:lib` 通过。
|
|
325
|
+
|
|
326
|
+
- 这一轮(1 条问题:当前改动会不会影响性能、继续优化性能与 bug):① **有影响,落点只有一处,已经修掉** —— 上一轮给通道回线拐点补 `channelId` 之后,`onWaypointChange` 里那句一直没被走到的 `syncChannelGroupDeclaration` 才真正开始跑:它在拐点事件的**每一帧**(含拖动中间态)都要把这一组声明抄一遍、再往历史里塞一条中间态。改成只在**落地**(`add` / `remove` / `end`)写回:1200 帧的拖动实测 5000 回线 1.61 → 1.50 ms/帧、2000 回线 0.75 → 0.67 ms/帧,一次拖动(600 帧)留在历史里的中间态堆 10.95 MB → ~0。② **同一条热路径上再省一笔**:`applyLinePath` 原来给每条实线圆角线路把整条折线再抄一份当 `line.caps`(只读、没人改),改成与折线共用同一份顶点 —— 5000 回线 1.74 → 1.49 ms/帧(中位 −14%),2000 回线在噪声内。③ **顺手修掉一个真 bug**:`setChannelPath` 的「第一组」按引擎口径取第一个 `lineIds` 非空的组(引擎会把空的分组声明整组丢掉,写进去等于白写)。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS、十二页自测 **1238 条 0 FAIL**(`selftest-data` 73 → 75)、`npm run build` / `npm run build:lib` 通过。
|
|
327
|
+
|
|
328
|
+
- 这一轮(1 条需求:继续优化性能问题):① **量出一处真 bug** —— 通道里任一回线被选中时,`buildWaypointGlyphs` 要把整条通道的成员递归收进「拐点来源清单」,而每个成员进来又会再扫一遍整份成员表(`visited` 只挡「同一个下标不重复入账」、不挡重扫):5000 回线的通道一次展开就是 2500 万次 `push`,拖动时每帧一次,实测 **130 ms/帧**(改前整条路 141.4 ms)。修法是按分组 key 记下「这条通道已经整组展开过」,清单的内容与顺序一个字节都不变(导出图片逐字节比对一致),一次「选中整条通道」5000 回线 236 → 118.6 ms、3000 回线 145 → 101 ms。② **同一份来源清单与把手批次不再每帧重建**:来源清单只跟「选中集合版本 + 解析结果引用 + 显隐策略」有关(缓存复用),把手批次结构没变时只把 x / y 重新摆一遍再上传(符号 / 配色 / 尺寸都是一个字节都不动的解析结果),整份重排与导出显式传 `force`。③ **`commitChannel` 不再每帧整份重铺线路批次**(原来结尾一句 `lineBatchDirty = true`,让拖动每一帧都重传整批实例,63k 线场景是十几毫秒):改由紧随其后的 `patchLineSlots` 只补改动的那几条,补不了时它自己立脏兜底(6 个调用点逐一核对过)。④ **`applyLinePath` 与平行偏移改成就地写回**:线段对象 / 包围盒 / 顶点数组 / `state.paths[slot]` 长度没变就写回上一轮那份,新增 `offsetPolylineInto`(`offsetPolyline` 变成它的一行包装),拖动每帧不再新开几千组对象。⑤ **`pickLine` 不再给每个「更近的」候选抄一整组读数**(5000 回线一次悬停要抄几十万项,改成先记 slot、扫完只算一次)。5000 回线的一次通道拐点拖动 **130 ms/帧 → 0.96 ms/帧**,63k 线(3000 回线 + 60000 背景)拖动帧间隔 median 23.6 → 17.1 ms(满帧);`selftest-perf.html` 补 3 条断言(perf 103 → 106,含一条「整条通道 3000 条回线一条不落地跟着走」,防「省下来的时间是因为什么都没干」)。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS、十二页自测 **1241 条 0 FAIL**(perf 103 → 106)、`npm run build` / `npm run build:lib` 通过。
|
|
329
|
+
|
|
330
|
+
- 这一轮(4 条问题:组件支持线路方向吗 / 不写自定义 HTML 能不能改标签底色与字号、文字色 / 继续优化大数据下的方法调用与交互 / 检查是否存在 bug,见「问题.txt」):① **线路方向箭头**(新能力):新增 `lines[].direction` 与 `options.lineDirection`(写法与 `effects` 同构:`false` / `null` 关、不写跟随全局、`true` 用全局或缺省、对象逐字段覆盖,逐条的 `direction` 盖全局)—— 箭头按弧长摆、朝向跟着线路切向,「方向」就是 `path[0]`(`from` 那一端)→ 终点,`reverse` 指回上游;内置箭头与线路装饰共用同一条批次与槽位账(没人配时整份重排连那一趟扫描都不走),`updateLine(id, { direction })` 与 `updateOptions({ lineDirection })` 都即时生效;demo 的「线型、标记与尺寸」面板加了「线路方向箭头 / 箭头数量 / 指回起点」三件套。② **标签样式**:`label.style` / `lineLabel.style` 本来就是内联样式的增量合并(底色 / 文字色 / 字号不用写 HTML,逐条 `updateLabel` 再叠),这一轮把 demo 的节点 / 线路标签面板各加「底色 / 文字色 / 字号」三个下拉(留空 = 组件默认白底),并把口径写进文档。③ **性能与 bug**:`updateLine` 原来无条件把线路批次标脏,改一条线的颜色会让下一帧重铺视野里全部线路(六万条线实测 0.1 → 9 ~ 45 ms/帧)—— 现在只有几何(`path` / `start` / `end`)真的换了才重铺,样式改动只补这一条的槽位(实测稳在 0.1 ms);同一条路上补上三处漏掉的同步 —— `updateLine` 改 `effect` / `direction` 与装饰清单(`setLineDecorations` / `addLineDecoration` / `updateLineDecoration` / `removeLineDecoration`)从前要等下一次整份重建才看得见;顺手修掉 `applyResolvedStyles` 的 `refreshLine` 里 `thinLedgerStale` 写在赋值之后的顺序 bug(交互期细线账本从不作废)。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS、十二页自测 **1249 条 0 FAIL**(`selftest-effects` 79 → 82:`updateLine` 定向改 `effect` 立刻生效;`selftest-features` 311 → 316:方向箭头全局开 / 逐条关 / 整份摘、装饰清单定向改)、`npm run build` / `npm run build:lib` 通过(细节与实测见《实现原理》15.62)。
|
|
331
|
+
|
|
332
|
+
- 这一轮(2 条问题:继续优化大数据下的方法调用与交互 / 检查 bug,见「问题.txt」):① **`updateLine` 的几何与结构变更不再整份重排**:拐点增删让段数对不上时原来无条件 `lineBatchDirty = true`,账记在下一趟的整份重排上 —— 为改一条线,点 / 贴图 / 装饰锚点 / 标签候选全陪跑。新增 `scheduleLineRelayout(index)`(`lineRelayoutPending` 标记 + `lineRelayoutIndexes` 集合两笔账)与画之前那一趟的 `flushLineRelayout()`:写入只记账,帧末只 `flushLineBatch()` 重铺线路批次(同一帧里改多条线只重铺一次),结构变(线型 / 层级 / 阴影条数)走同一条路,只有动点图标换了或抬层欠账未清才回落整份重排。六万线实测:改路径(段数 1 → 2)×200 从 15.5 ms/次(合计 3101.9 ms)降到 0.013 ms/次(合计 2.6 ms)+ 帧末一次整批重铺 16.4 ms;改路径(段数不变)×200 是 0.023 ms/次 + 帧末 0.2 ms;`updateLine(id, { zIndex })` 从 5.92 ms/次 + 整份重排降到 2.3 ms 打补丁(层序表重算)+ 帧末一次整批重铺 8.7 ms。② **一处真 bug**:装饰锚点(方格 / 方向箭头)按路径算,重铺线路批次并不会让它挪窝 —— 快路径漏了 `refreshLineDecorations(...)`,改拐点后装饰停在旧拐角上(探针复现);现在 `flushLineRelayout` 补这一趟(条数对不上回落整份重排),selftest-features 的两条新断言(新拐角有紫像素、旧锚点没有)盯着它。③ 自测里「追加一个节点的代价明显低于一次整份重排」从「5 次平均 × 5」改成「**最快一次** × 5」:十二页连跑时平均值被 GC 与同页别的大场景从 0.2 ms 抬到 1.36 ms、比值 4.26 差一点误报,而快路径有没有生效看的是最快那一次的量级(回落重排时每次都在 3 ms 以上)。验证:`npx tsc --noEmit` 0 错、`node docs/steps/gen-api-index.mjs` 未变化、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS、十二页自测 **1257 条 0 FAIL**(`selftest-perf` 106 → 112、`selftest-features` 316 → 318)、`npm run build` / `npm run build:lib` 通过(细节与实测见《实现原理》15.63)。
|
|
333
|
+
- 这一轮(同一份问题.txt 再走一轮:继续优化大数据下的方法调用与交互 / 检查 bug):沿着上一条做掉的「只换样式」快路径又翻出**三处同类 bug** —— 渲染出来的东西缺省色取要素自己的颜色,而快路径只改了要素的颜色。① **线路的方向箭头与动点图标**:`patchLineSlots(..., restyle = true)` 跳过装饰重定位本身是对的(锚点确实不必动),但箭头 / 动点的颜色缺省取线色(`lineColorCss` / `parseColor(effect.color, line.color)`)—— 换全局线色之后画面上是「线换了色、箭头还是旧色」(探针:3.5 秒后箭头仍 `rgb(256,0,0)`);现在 `refreshLineDecorations` 的 `restyle` 早退改成逐条判断,动点另起 `refreshLineEffectGlyphs(indexes)` 就地换色(图片 / SVG 的颜色进烘焙键、批次分组也得换,返回 false 交回整份重排)。② **节点涟漪**:`parseColor(effect.color, point.color)` 同理,新增 `refreshRippleColor(effect, previous, next)` 在四处调用点推 `effectVersion`(解析全量路 / 窄路 / `updatePoint` / 复制预览,预览那一路顺手补上 `effect` 配置的同步)。③ 性能复核(10 万点 / 6 万线):`updateLine` 各类 0.011 ms/次、`patchPoints(500)` 5.3 ms、`updateOptions({ lineStyle: { color } })` 10.6 ms/次、`exportViewportData()` 81 ms、`setCamera` / `refresh` 20 次合计 0.2 ~ 0.4 ms —— 三处修复在「没有装饰 / 没有效果」时只是一次判断,没有拖慢任何热点;只剩的离群值(`removeLine` 61 ms 的整份重解析)记在 16.36 候选里。验证:十二页自测 **1261 条 0 FAIL**(`selftest-features` 318 → 321、`selftest-effects` 82 → 83)、`npx tsc --noEmit` 0 错、`check-doc-coverage` 10 项全 PASS、`npm run build` / `npm run build:lib` 通过(细节见《实现原理》15.64)。
|
|
334
|
+
|
|
335
|
+
- 这一轮(同一份问题.txt 的又一轮:继续优化大数据下的方法调用与交互 / 检查 bug):把上一轮记成候选的「删一条线整份重解析」做掉了 —— **数据补丁里节点那一半一个字节都没动时,整份重解析只跑线路那一半**。判定两步:`pointHalfDataUnchanged`(`patch.points` 缺省或与内部那一份**逐元素同引用**、隐藏集合内容没变、不在交互中、没有复制预览)+ `pointHalfStylesUnchanged`(基准样式 / 规则 / 效果 / 热力图按解析输入快照比、节点状态样式按补丁下发前的值比),两边都过才置 `skipPointResolve`,`resolveFeatures` 消费一次把点解析、id 表、层序、包围盒与阴影账整块跳过(上一轮那两份账记在 `pointHalfBounds` / `pointResolvedBakeBucket` 上)。同页交替 A/B(10 万点 / 6 万线,中位 / 最快):`updateOptions({ lines })` 58.6 / 53.8 → 45.7 / 40.8 ms、`updateOptions({ points })` 50.4 / 46.8 → 43.8 / 35.2 ms、`removeLine` 50.7 / 45.4 → 41.3 / 37.1 ms。三处 bug:① 上一轮那条窄路判样式类字段是「补丁里有没有这个键」,而组件每次都会把整份配置再交一遍(没配的状态样式写成 `null`)—— 判定恒真、一次都命中不了;② `pointHalfBounds` / `pointResolvedBakeBucket` 只有声明与读取、漏了写入,贴图档位那一关永远不成立;③ 自测抓出来的:`updatePoint` 会把新对象写回引擎内部那一份,身份比对看不出「解析结果没跟上」,`visible: false` 的退场被跳过(`selftest-scale` 的 `isPointVisible` 两条)—— 新增 `pointHalfOutOfSync` 一笔账(定向更新记、整份重解析清)。门槛还放宽了一处:发散渲染正跑着也照样跳(这一跳不动槽位表与实例缓冲),卡着 `layoutComplete` / `buildOrder` 时 8 次更新只有第 1 次命中。顺带两处线路侧微优化(`resolveLines` 的逐条配置包对象提到循环外、`resolveLinePath` 的逐条闭包改成模块级函数):隔离基准里六万条线最快 25.4 / 26.1 → 17.0 / 17.5 ms(放进流水线被排批次与实例写回盖住)。验证:`npx tsc --noEmit` 0 错、`check-doc-coverage` 10 项全 PASS、十二页自测 **1261 条 0 FAIL**(`selftest-scale` 中途 2 条失败 → 修完全过)、`npm run build` / `npm run build:lib` 通过(细节见《实现原理》15.65)。
|
|
336
|
+
- 这一轮(同一份问题.txt 的又一轮:删除也走增量 + 两处 bug):把《实现原理》16.16 末尾记下的「反向增量」做掉了 —— **`removePoints` 删除不再整份重建**:补丁只带数据与隐藏集合,引擎逐元素比对认出「新数组就是上一轮那批按序删掉几条」(`canRemoveResolve`),`applyRemovalResolve` 一趟就地过滤把跟着下标走的账全部平移(点侧 id 表 / 包围盒 / 最大半径 / 阴影账 / 符号账本 / 拾取网格,线侧三张槽位表 / `lineByIndex` / 脏下标 / 最大半线宽与线路包围盒),两个解析循环整段跳过,收尾与整份重解析共用 `finishFeatureResolve`。**线路批次整个留着**:绑在被删节点上的线路跟着一起删了、存活线路的几何一个字节没动,`keepLineBatch` 让「六万条线整份重铺」(剖析里 205 ~ 322 ms)直接归零;实例数组也复用(`prepareInstancesForLayout`,容量够就沿用、上传只传用到的那一段)。组件侧走一条窄补丁 `applyRemovals`(只发 `{ channels, hiddenPoints, hiddenLines, points, lines }`,整份配置原样再发会被窄路口推回整份重建)。实测(10 万点 / 6 万线):删 1 个节点回落整份重建中位 152.7 ms → 增量最快 20.2 / 中位 37.8 ms,一次删 1000 条 37.1 ms(回落那条约 240 ~ 340 ms);`selftest-perf.html` 同页 A/B(2 万点 / 2.4 万线)增量最快 7.80 ms vs 回落最快 24.1 ms。两处 bug:① **删除时中间那次隐藏集合补丁会把解析结果搅浑**(筛选结果按删除后的新下标算、却先打到旧下标的解析结果上,随后的增量路把它当成「上一轮那一份」继承 —— 症状是线路路径被截断 / 有的线一边有一边没有 / 级联隐藏的线又冒出来,自测的删除等价性断言抓出来的):组件侧 `refilterForRemoval` 在删除窗口里只更新本地账本、不发引擎,引擎侧加 `shiftIndexesThroughRemoval` 映射校验(隐藏集合必须等于旧集合按删除平移的结果),异步 Worker 回来得晚时也挡得住;② **`refresh()` 会跳过点解析**:那两条「这一半没动就跳过」按对象身份比对,数据被就地改过字段时看不出来 —— `applyOptions` 认 `forceResolve`,`refresh()` 缺省带 `{ forceResolve: true }`。自测 `selftest-perf.html` 新增 7 条删除等价性断言(删减后的快照 1.66 MB 与整份重建逐字段一致、被删点拾取不到、邻点按下标、隐藏集合与选中镜像平移、标签层重排、最快一次明显快于整份重建)。验证:`npx tsc --noEmit` 0 错、`check-doc-coverage` 全 PASS、十二页自测 **1268 条 0 FAIL**、`npm run build` / `npm run build:lib` 通过(细节见《实现原理》15.66 / 16.38)。
|
|
337
|
+
- 这一轮(1 条问题:filter 的 cascade 到底生效了没,见「问题.txt」):复核结论是**判定对、断在引擎** —— `evaluateFilter` 按 `from` / `to` 与端点坐标算 `hiddenLines` 一条不错,但被筛掉的节点在 `resolvePoints` 里整条跳过(不在 `resolvedPoints` / `pointByIndex`,id 表也查不到),`lineContext.resolveBoundPoint` 却只管可见节点:没自带 `path` 的绑定线拼不出顶点,通道则落进 `resolveChannels` 的「锚点缺失」分支、成员回线全部进 `channelHiddenMembers` 收起来 —— `GisViewDemo.vue` 的真实数据(`channel_data` 展开的回线)永远走这一条,表现就是「只要节点被隐藏,线路也跟着没了,`cascade` 写什么都不管用」。修法:`engine.js` 新增 `filteredAnchorIndexById`(被筛掉的节点 `id -> 数据下标`,键跟 `points` / `hiddenPointIndexes` 的引用绑定、取坐标时当场读数据,`updatePoint` 就地换对象也跟得上),`resolveBoundPoint` 在可见节点查不到时回落到它 —— 几何不跟着筛选断,`cascade` 成为「线 / 通道回线要不要一起收起来」的唯一开关:`false` 时照旧画、端点仍钉在原坐标上,`binding` / `coordinate` / `both` 与从前一样收起来;删掉 / `visible: false` 的节点仍旧按「没有节点」算(绑定线退回自身坐标、锚不住的通道整条不显示,与既有语义逐字一致)。自测 `selftest-scale.html` 新增第 3.5 组 6 条(`cascade: false` 时通道回线照旧画 / 端点仍钉在被筛掉的节点坐标上 / 通道骨架仍从它起算 / `binding` 收起来 / `coordinate` 收起来 / 换回 `false` 又出来;修前这一组 `listLines()` 恒为 0),72 → **78** 条;文档:GisView.md 的「筛选」、实现原理 15.71 与第 9 章的 cascade 说明、`从0到完整的实现.md` 的联动表、`src/gis-view/README.md` 一段。验证:`npx tsc --noEmit` 0 错、十三页自测共 **1331 条 0 FAIL**、`npm run build` 通过(细节见《实现原理》15.71)。
|
|
338
|
+
- 这一轮(「问题.txt」三条:features 与单个配置的重合 / 组件有没有重复的功能与方法 / SVG 第一次选中闪一下):① **两层开关的关系逐项对清**:`pan` / `zoom` / `contextMenu` ↔ `interactive`、`hover` / `select` ↔ `clickable`、`boxSelect` / `drag` / `waypoint` 与各自详细配置是「粗 / 细」两层(开关写布尔值就盖掉详细配置);`copy` / `merge` / `remove` 与各自配置的 `enabled` 本来就是一条路(`withFeatureEnabled`);`keyboard` 是键盘快捷键的总闸、`shortcuts` 是逐动作键位(**Ctrl / ⌘ 这类指针修饰键不受 `keyboard` 限制**:框选 / 加拐点 / 多选 / 复制预览由 `shortcuts.*` 决定);`filterWorker` ↔ `filter.worker` 是「总闸 + 这一份」;真正重合的只有 `label` / `lineLabel` / `tooltip` / `minimap` 与详细配置里的 `enabled: false` —— 而且两处读数从前不一致(`getFeatures()` 只看「配没配」,`label: { enabled: false }` 时一个标签节点都没有、读数却是 `true`),现在按「**与**」算:开关写 `true` 只是「不按没配处理」,详细配置没配或写了 `enabled: false` 就没有这个图层、读数跟着是 `false`(`create.js` 新增 `configuredEnabled`,与 overlay / minimap 自己的判定同一口径),口径与「哪些不写进 features」列进 `GisView.md` 的新表。② **重复方法 / 重复实现审计**:212 个实例成员的实现文本抹掉注释与空白后两两比对 —— **一组重复都没有**(209 个方法各自一条路,近义入口是「转发 + 多一层控制」);`src/gis-view/*.js` 的 368 个函数定义里逐字相同的只有 `resolveZIndex`,连带 `FALLBACK_BOUNDS`(`create.js` / `engine.js` 各一份,两边的读数必须是同一个世界范围)、`coordinateKey`(`filter.js` 那份没做 `Number` 归一,坐标写成字符串时与鹰眼图那份能算出两个键)、`clamp`(`projection.js` 重复了 `utils.js` 的导出)一起并掉,同名不同义的四对(`FALLBACK_COLOR` / `toNumber` / `resolveNumber` / `boundsCenter`)写明「不合并」的理由;近义入口(`select` ↔ `selectFeatures`、`zoomIn` ↔ `zoomBy`、`fitView` / `applyInitialView` / `resetView` / `zoomToBounds`、`pick` ↔ `pickAt`、`copyPoint` ↔ 剪贴板三件套…)在 `GisView.md` 新增「近义入口与别名」一节列全。③ **SVG 第一次选中闪一下**:贴图符号的颜色烘在贴图里,换选中色等于一份**新配方**,烤好之前那一两帧从前整个节点不画(探针:选中瞬间中心像素还是纯白 `255,255,255`);现在两条腿兜住 —— 符号库按「同一份来源」(标记 / `path` / `viewBox` 或图片地址,不含配色)留最新条目做**占位贴图**(新配方先借上一种配色的贴图顶着画,自己那张烤好当场换上),引擎空闲时按 `getSymbolCombos()` 的同一份账本把「全局状态样式 × 实际用到的贴图组合」提前烤(线路侧补「线色 × 动画图标」,逐要素写在数据里的状态样式照完整解析路扫一遍,没人写就整段跳过),`getSymbolCombos()` 收出来的 `combos` 因此也含状态样式那几条颜色;顺带修掉一处回归(`refreshLineDecorations` 的「只换样式」快路径要认分组符号的身份,否则线换了色、方向箭头还是旧色)。验证:`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS、`npm run build` 通过、`selftest-select.html` 32 → **38**、`selftest-features.html` 329 → **335**(细节见《实现原理》15.72)。
|
|
339
|
+
- 这一轮(「问题.txt」一条:放大缩小地图 + 调整元件大小之后,有些选中的节点丢了描边色):上一轮那套「占位贴图」(新配色第一次出现时先借同一份来源的上一种配色顶着画)在 `createEntry` 里被**当成自己那一份收下**了 —— 贴图配方(来源 + 填充 + 描边 + 符号描边宽)**不含尺寸**,同一配方、换个尺寸档就是另一条条目,而它的 `carried` 可能正是一条占位条目:借来那张贴图于是被记进自己的档位表、占位标记也被摘掉,`ensureBaked` 里「手上这张正是要画的这一档」那条早退立刻成立,这一条**从此不再烤**;放大换档能把它烤回来,缩回上一档时 `setBakeScale` 又从档位表里把借来那张换了上来 —— 画面上就是「放大缩小、改尺寸之后有些选中的节点描边色丢了」(阴影是另画一份剪影、按阴影色涂,配色丢了看不出来,所以「只丢 `strokeColor`」)。修法:`symbols.js` 的 `createEntry` 把「自己那一份」与「借来顶着的」分开算(`own = carried?.texture && !carried.placeholder ? carried : null`、`placeholder = Boolean(stand?.texture) && stand !== own`),占位条目照旧顶着一张画(不闪没的路一点不动),但不进档位表、也不摘占位标记 —— 这一份自己那张一定会烤,烤好由 `rasterize` 当场换上。自测 `selftest-select.html` 37 → **41**(「问题 4」那一组 4 条:底色到位 / 同时选中之后两个都是选中色 / 放大再缩小之后选中色还在,用 `window.Image` 的 `load` 延迟 150 ms 把异步窗口固定下来;修前 2 条红:`select / base`),十三页共 **1347** 条 0 FAIL(细节见《实现原理》15.73)。
|
|
340
|
+
- 这一轮(「问题.txt」一条:继续检查组件有没有 bug 以及可以优化和提高性能的地方):删除 / 合并那条增量路的边界上又抓出两处「下标不再是身份」—— ① **选中集不跟着要素走**:`applyRemovalResolve` 的平移只带走悬停 / 拐点键,删点之后多选静默只剩挪到老下标的那一条(其余丢),删线删到选中前面时引擎照着越界下标画(高亮留在邻线上、`select(null)` 清不掉),合并把选中整份丢掉;现在 `selectionKeys` 也走同一条平移(摘被删的 / 挪活着的,变了就 `syncSelectedLines()` 并补一条 `onSelect` 读数),组件侧新增 `pushSelectionToEngine()` / `repairEngineSelection(keys)` —— 合并赶在整份重建之前、删线跟在数据落地之后把平移过的镜像重新落地,`removePoints` 用删除前抓的「只留活口」读数兜底。② **选中的拐点顶到邻线**:整份重建那条删除路只按下标查活,`selectWaypoint` 现在把「当时那条线的数据对象」记进键里,`isWaypointAlive` 连身份一起认,`commitLine`(拐点增删 / 拖动换数据对象)与删除平移各自跟着换新,身份认不出时 `layoutScene` 用 `lines.indexOf(item)` 找回新下标。顺手把 `shiftKeyThroughRemoval` 从线性改二分(选中集上万 × 删上千原是平方级)。`selftest-features.html` 335 → **343**(新增「⑥ 删除 / 合并之后『下标不再是身份』」8 条断言),十三页自测共 1355 条 0 FAIL,见《实现原理》15.74 / 16.44。
|
|
341
|
+
- 这一轮(「问题.txt」一条:继续检查组件有没有 bug 以及可以优化和提高性能的地方):搜索那本账上抓出一处真 bug、压掉两处平方级,另把 `regex` 条件与文档对齐。① **通道副本**:`updateChannel` / `applyChannels` / 只导入通道这三条路从前只换主线程那一份,搜索 runner 手里那份 `channels` 没有任何作废路径 —— Worker 那条路拿旧副本在比(改过名的通道新名字搜不到、旧名字还搜得到);现在 `invalidateSearchChannels()` 把三处接上(只改分组声明不算,搜索不读 `groups`)。② **逐帧攒账**:`search-runner.js` 的 `touch()` 从「两个数组相加」改成「集合 -> 下标」两层 Map 去重(数字只记下标、发之前 `itemAt` 现取当前那一条),同一条改几次只留最后一次、发出去即清账:20000 次 touch 实测 100 ms → **1 ms**。③ **删除确认**:连带影响从逐点各扫一遍线路 / 通道(框选几千点 = 几千 × 全部线路 次比对)改成 `removeImpactsOfPoints(indexes)` 一趟算完(id -> 影响账的表 + 线路 / 通道各一次扫描),`buildRemovePlan` 顺带建「回线下标 -> 分组」「通道 id -> 全部组」两张表、下标先去重;`shiftIndexAfterRemoval` 与镜像平移改二分,顺带修掉 `entry.item.id` 的读崩;8000 点 / 8000 线全选 `requestRemove()`:423.9 ms → **10.6 ms**。④ **`regex` 条件**:文档写着的 `{ source, flags }` 写法实现里一直没有(静默比不等),现在收下;并去掉 `g` / `y` 标志(`test()` 的 `lastIndex` 会让逐要素判定 true / false 来回翻转)。验证:`selftest-scale.html` 78 → **82**、`selftest-perf.html` 123 → **124**,十三页自测共 **1360** 条 0 FAIL,`npx tsc --noEmit` 0 错、`node docs/steps/check-doc-coverage.mjs` 10 项全 PASS、`node docs/steps/gen-api-index.mjs --check` 最新(细节见《实现原理》15.75 / 16.45)。
|
|
342
|
+
|
|
343
|
+
- 这一轮(「问题.txt」四条:拐点 / 分组 / 节点移动的事件捕捉齐不齐):① **拐点**(已有,复核):`onWaypointChange` 覆盖增 / 选 / 删(`add` / `select` / `remove`)与拖动三相位(`start` / `move` / `end`),载荷带 `lineIndex` / `pathIndex` / `coordinate` / `group_id` / `channelId` / `lineIndexes` / `line`,程序化 `addWaypoint` / `removeWaypoint` 与分组拐点 `insertGroupWaypoint` / `removeGroupWaypoint` 都发;③ **节点移动**(已有,复核):`onPointDrag` 三相位 + 落地 `dataChange('pointDrag')`(`movedPoints` / `movedLines`);② **分组**从前只有 `dataChange('channelGroups')`(只有 `groups` 现状,不知道是拆是并),这一轮补上专用事件 `channelGroupChange` / `onChannelGroupChange` —— `action.type` 为 `splitGroups` / `mergeGroups` / `moveLine` / `addGroup` / `setGroups`,`action.groupId` / `groupIds` / `lineIds` 指到具体分组与回线(`updateLine` 顺带挪组标 `via: 'updateLine'`),`item` / `groups` 与 `dataChange` 那份是同一个引用;④ **不加事件也能接**:`onDataChange` 里判 `type === 'channelGroups'` + 随时 `getChannelGroups()` 就是另一条路,「取消」类走 `channelDraft` / `remove` / `pointCopy` / `pointMerge` 的 cancel 相位。顺带修掉一处真 bug:分组变完选中镜像里那条回线的 `groupId` / `groupIndex` 停在旧值(新增 `refreshChannelLineReadouts()` 按受影响回线重读 `engine.describe()`)。`selftest-features.html` 343 → **351**,十三页自测共 1368 条 0 FAIL(细节见《实现原理》15.76 / 16.46)。
|
|
344
|
+
|
|
345
|
+
- 这一轮(「问题.txt」六条:拐点样式文档 / 文档页搜索 / demo 右键新增场站 / demo 缩放限制 / 新增方法文档 / AI 对话汇总):① **拐点样式**(`waypointStyle` / `waypointSelectStyle`)功能一直有,缺的是文档 —— `GisView.md` 的 options 条目改写为「拐点样式」并补子字段表与缺省值,「线路拐点」新增「拐点样式」小节(覆盖补丁 / 选中环 / 拾取半径 / 整份重排口径);② **文档页搜索**(`docs/index.html`)从「只过滤标题」扩成「标题过滤 + 两字起跨文档全文搜索(按小节给结果、片段高亮、点一条切文档并滚到那一节)+ 回车当前文档查找」;③ **demo 空白处右键新增场站**(`GisViewDemo.vue`):画布菜单首行 + 表单弹层(自动编号、落点预填、校验),确认走 `view.addPoint(...)` 并选中;④ **demo 缩放限制**的入口与「更新配置不拉回相机、要显式 `setCamera`」写进 `GisView.md` 的「缩放范围」;⑤ 新增「按方法新增要素(节点 / 线路 / 通道 / 分组 / 拐点)」一节,把 `addPoint` / `addLines` / `addLine` / `addChannel(s)` / `addChannelGroup` / `splitChannelGroups` / `mergeChannelGroups` / `moveChannelLine` / `addWaypoint` / `setLineWaypoints` / `setChannelPath` 等一次查全;⑥ 新增 [docs/AI对话汇总.md](docs/AI对话汇总.md)(本机 39 个会话按内容整理)。自测:`selftest-demos.html` 135 → **140**、`selftest-docs.html` 36 → **43**。
|
|
346
|
+
- 这一轮(「问题.txt」三条:AI 对话写全问答 / md 与组件逐项对账 / 文档页搜索结果编树):① [docs/AI对话汇总.md](docs/AI对话汇总.md) 新增「五、逐会话问答」—— 39 个会话共 194 轮,问(原文,超 200 字截断)+ 答(该轮最后一条正式回复的首段 + 最多 4 条列表行,标原答体积;过程旁白与 `# Handoff Summary` 不算回答),文档 16 KB → **120 KB**;② 新对账脚本 [docs/steps/check-doc-truth.mjs](docs/steps/check-doc-truth.mjs) 管「值」:options 字面量默认值 21 处 × 源码、事件名 41 个 × `EVENT_NAMES`、开关 18 个 × `FEATURE_NAMES`、总表 85 键 × 详解小节一一对应且三行格式齐全、白名单 27 键都有下落 —— 据此补上总表缺的 `label` 行与详解、把 `filter` 类型改齐为 `GisViewFilterInput | null`、重建 12 处被自动换行切坏的详解小节、改掉 `features.js` 一句与实现相反的注释;③ 文档页搜索(`docs/index.html`)从扁平列表改成「文档 → 小节 → 命中行」的 `<details>` 树(按 slug 收拢、两层带计数、每份取数 4 → 6、点过的行留高亮)。自测:`selftest-docs.html` 43 → **45**。
|
|
347
|
+
|
|
348
|
+
- 这一轮(「问题.txt」两条:怎么手动取消选中 / updatePoint 之后通道宽度不立刻变):① **取消选中**新增与 `select` 对称的 `deselect(refs?)`(不传 / `null` 全清,传一个或一批引用只摘这些,空数组无操作,返回实例;`select(id)` 也放开成 `select(id, options)`,`mode` 与 `selectFeatures` 同一份),通道右键交互框补第 6 个动作 `deselect-group`(「取消该分组选中」);**没有**再加 `clearSelection`(15.6x 那轮已把它删掉,`select(null)` / `deselect()` 就是那条路);② **通道宽度**从前只在相机变化后重排(`refreshChannelSizes`),`updatePoint` 换了 `size` 只重解析点 + 挪骨架端点,回线还铺在旧宽度上;现在把重排抽成 `reflowChannelWidths(states)`,`updatePoint` 里比一次解析后的外框,变了就沿 `channelAnchorIndex` 只重排以它为锚点的通道、并进同一次 `patchLineSlots`(只改一端且另一端更小不白重排;换 `symbol` 也认)。自测 `selftest-select` 41 → **51** / `selftest-features` 351 → **355** / `selftest-demos` 140 → **141**,十三页共 **1398** 条 0 FAIL(细节见《实现原理》15.79 / 16.49)。
|
|
349
|
+
|
|
350
|
+
- 这一轮(「问题.txt」:updatePoint 改了电压等级 / 图标之后通道宽度还是要再点一下才变):上一轮那条「改尺寸就重排锚定通道」在**图标节点**上没生效,根因两层 —— ① `updatePoint` 里「符号结构变了就整份重排」那一支(`structural`)对**贴图(图片 / SVG 图标)节点永远命中**,重排被我写在了它的后面,图标节点根本走不到;② 重排只标了脏(`markLineDirty` 管的是拾取 / 装饰 / 细线账),**画出来的实例缓冲要 `patchLineSlots` 才会重写**,`structural` 那一支只走 `layoutScene()`,而它只在 `lineBatchDirty` 时重铺线路批次。修法:把「跟随几何 + 按新外框重排锚定通道宽度」整个挪到 `structural` 分支之前(`engine.js:9688`,顺带修了「挪坐标 + 换图标」同时发生时骨架端点停在旧坐标),`structural` 分支补一次 `patchLineSlots`(补不了会自己立 `lineBatchDirty` 兜底),定向重排抽成 `reflowAnchoredChannelWidths`(`engine.js:3477`,外框没变 / 没锚点直接跳过)。自测 `selftest-features` 355 → **361**(新增图标节点一段,含两条像素断言:新回线画在 ±12 上、旧位置 ±4 不再压着老线),十三页共 **1404** 条 0 FAIL(细节见《实现原理》15.80 / 16.50)。
|
|
351
|
+
|
|
352
|
+
- 这一轮(「问题.txt」两条:怎么删节点 / 通道 / 线路 / 分组 / 拐点、有没有文档;怎么改通道名 / 通道下增减回线 / 改某条回线颜色):审计结论是五类删除与「单条回线颜色」原本都有入口、也都有文档,**缺的是「删除一个分组」「通道下增减回线」两条粒度与改名入口的成文说明**。`create.js` 补四个方法:① `removeChannelGroup(channelId, groupId, options?)` —— 把这一组并进相邻的一组(缺省前一组,第一组并后一组,`options.targetGroupId` 可指名),骨架 / 拐点沿用目标组(与 `mergeChannelGroups` 同一条落地),并完只剩一组时声明回到「整条通道一组」(id 用通道自己的 id),单组通道 `ok: false`(删整条用 `removeChannel`),事件 `mergeGroups` + `via: 'removeChannelGroup'`;② `addChannelLines(channelId, lineIds, options?)` —— 只改成员表(回线本身早就在数据里),新线追加在声明末尾、几何改由通道推导,`options.groupId` 指定进哪一组(缺省第一组),已在别的通道 / 认不出 / 无 id 的进 `skipped`,目标组超 `channelGroupMaxLines` 整条拒绝;③ `removeChannelLines(channelId, lineIds)` —— 从成员表与分组声明一起摘掉(空组整组消失),回线留在数据里按自己的 `path` / `from` / `to` 画,摘完不剩时 `ok: false`;④ `setChannelName(id, name)` —— 只改名称一项(`updateChannel(id, { name })` 同一件事,传 `null` 摘掉),悬停提示 / 标签模板的 `{name}` / 搜索都取它。顺手把引擎侧「通道 id ?? `channel:<下标>`」的键解析补进这几个方法:传下标寻址时 groups 读数与声明写回不再落空。文档:「通道与多回线」新增「通道改名 / 通道下增减回线 / 删除分组」一节(含代码与口径),「按方法新增要素」「实例方法」「参数速查」三处补行,近义入口表补对应写法,`setChannelColors` 旁补「改单条回线颜色用 `setLineColor` / `updateLine({ color })`」;demo 通道面板补改名输入框与加 / 摘回线、删除分组四个控件。验证:`selftest-features` 361 → **373**(12 条:改名与事件 / 加回线 +1 且按车道几何画 / 重复与认不出 skipped / 摘出回到直连 / 摘完拒绝 / 删组并进前组 / 事件 via / 单组拒绝 / 传下标加回线照常落地 / 传下标删分组照常落地),十三页共 **1416** 条 0 FAIL、`npx tsc --noEmit` 0 错、`npm run build` 通过(细节见《实现原理》15.81 / 16.51)。
|
|
353
|
+
|
|
354
|
+
- 这一轮(「问题.txt」两条:所有方法补齐示例 / updateChannel 一次更新通道与成员回线):① 文档新增「逐方法示例(复制就能跑)」一节(`docs/GisView.md`,12 个分组、覆盖全部 217 个实例成员),凡「收一个或一批」的入口都并列写清单条 / 数组 / 混合写法(`removePoints` 细化到删一个 / 删多个 / 带下标 / 混合 / `'selection'` / `'visible'` / `'all'` / 带 `reason`;`removeLines` / `select` / `deselect` 等同样给了单条与数组两种),`updateChannel` 给两段与三段两种写法;② `updateChannel(id, patch, linePatches?)` 新增第三段 —— 一次提交「通道声明(`name` / `lineIds` / `groups` 等)+ 里面某几条回线的字段补丁」,成员写 `{ id | index, ...字段 }`(一条或一批都给),整段套一次 `batch()`:只发一条 `batch` 数据变更、`undo()` 一步全还原,只给两段时行为与从前逐字一致。验证:`selftest-features` 373 → **379**、十三页共 **1422** 条 0 FAIL、`npx tsc --noEmit` 0 错、`check-doc-coverage` 10 项 / `check-doc-truth` 6 项 PASS、`npm run build` 通过(细节见《实现原理》15.82 / 16.52)。
|
|
355
|
+
- 这一轮(「问题.txt」三条:屏幕坐标与真实坐标互转 / addChannel 连同线路一起新建 / beginAddChannel 选完两个节点交接):① 坐标互转补「浏览器事件」这一档 —— `project` / `unproject` 管世界 ↔ 画布 CSS 像素、`lngLatToWorld` / `worldToLngLat` 管经纬度,新增 `clientToWorld(clientX, clientY)` / `worldToClient(x, y)` 管鼠标事件坐标 ↔ 世界坐标(内部自动减 canvas rect、除宿主缩放,`worldToClient` 出来的就是 `position: fixed` 浮层直接可用的坐标);② `addChannel(input, lineItems?)` 补第二段(`addLine` / `addChannels` 每项同款、入参 `lineItems` 也认):一条或一批逐条属性,给了几条就建几条(`circuits` / `count` 忽略),与 `linePropsByMember` 按下标合并、第二段优先;③ `beginAddChannel({ handoff: true })` 交接模式:两个节点选完、`phase: 'ready'` 发完,草稿当场静默收掉(不补 `cancel`),使用方拿 ready 载荷自己调 `addChannel` 落地。验证:`selftest-features` 379 → **390**、十三页共 **1433** 条 0 FAIL、`npx tsc --noEmit` 0 错、`check-doc-coverage` 10 项 / `check-doc-truth` 6 项 PASS、`gen-api-index.mjs` 重生成(**219** 成员)、`npm run build` 通过(细节见《实现原理》15.83 / 16.53)。
|
|
356
|
+
- 这一轮(「问题.txt」三条:`addChannel` 更新已有通道的 `lineIds`、位置对账 `getMovedPoints` / `clearMovedPoints`、拐点与接口的交互方案):① `addChannel` 支持更新已有通道的成员表 —— 顶层 `channelId` / `groupId` / `groupIndex` 就是 `resolution` 的简写(与 `updateLine` 的分组指令同一套写法;只给 `channelId` 时按这条通道自己的推荐顺序挑:新分组 → 第一个能并的分组;两个节点之间还没有通道时它仍是新建通道的 id),结果与 `onChannelDraft` 的 `commit` 载荷回带 `channelLineIds`(落地后这条通道的完整成员表,独立线路为 `null`);② 新增「位置对账」:`getMovedPoints()` 一趟给出相对上次对账搬过位置的节点(`[{ index, id, x, y, item }]`,按 id 去重、拖动 / `updatePoint` / `patchPoints` / 撤销都算、`removePoints` 删掉的记录顺手清掉、整份换数据重新对齐),`clearMovedPoints(refs?)` 把当前坐标记成新基准(不传 = 全部对账,引用写法与 `removePoints` 一致)——「先取、发接口、发完对账」就是一条实时位置同步回路;③ 拐点与后端交互的推荐口径写进文档(整份覆盖不给 delta、优先攒批、改完就传要合并 + 延迟 + 失败回滚、分组拐点按组给)—— 演示页「编辑与保存」补位置对账读数与对账按钮。验证:`selftest-features` 390 → **406**(mvView 块 17 条)、`selftest-demos` 141 → **143**、十三页共 **1451** 条 0 FAIL、`npx tsc --noEmit` 0 错、`check-doc-coverage` 10 项 / `check-doc-truth` 6 项 PASS、`gen-api-index.mjs` 重生成(**221** 成员)、`npm run build` 通过(细节见《实现原理》15.84 / 16.54)。
|
|
357
|
+
- 这一轮(1 条问题:检查 bug、优化性能、组件内部方法审计,见「问题.txt」):起了一个临时审计页把 50k 点 / 30k 线(+ 1 条通道)场景下的实例方法逐条量了一遍,配 CPU profile 抓出四处真问题并修掉。① **有通道时「追加式补点」整份回落**:`canAppendPoints` 原来见到通道就返回 false,一次补 100 条 44 ms —— 改成只挡「新点正是通道声明的 `from` / `to` 锚点」的那种(`appendedClaimsChannelAnchor`),实测 44 → 3.3 ~ 6.3 ms;② **有通道时删除整份回落**:`canRemoveResolve` 的通道闸门原来把「有通道」并成一个否定,删 1 条也要 35 ms —— 改成「声明逐条没变(同引用)+ 没删到任何回线 + 没删到锚点」才认增量的纯删点,实测删 50 条 35 → 18.7 ~ 21 ms、删 1 条 35 → 12.5 ms(删锚点时通道整条收拾的行为不变);③ **一批按 id 的定位不再整表重建**:`removePoints` / `updateItems` 在条数 ≥ 32 时原来 `syncIdMaps()` 整表重建(8 ~ 10 ms,profile 占 6.8%),新增 `locateIdsInBatch` 一趟扫出要的那几十个 id;④ **两条读数**:`getSymbolCombos()` 先按颜色数组身份去重再转 CSS(30k 线 2.32 → 0.22 ms/次),`getMovedPoints()` 不再逐点开坐标数组(2.2 → 2.0 ms/次;这条的下限就是 O(数据量) 的查账,取舍见《实现原理》16.55)。`exportData({ format: 'full' })` 的 15 ~ 16 ms 留作候选(16.56)。验证:`selftest-features` 406 → **410**(通道在场时增删无关点的读数逐字节不变 / 删锚点连带收拾 / 通道等未到的锚点补点后当场可解析)、`selftest-perf` 124 → **125**(通道在场时补 / 删 2 个点:1.6 / 6.3 ms,改前 40 / 35 ms 上下)、十三页共 **1456** 条 0 FAIL、`npx tsc --noEmit` 0 错、`check-doc-coverage` 10 项 / `check-doc-truth` 6 项 PASS、`npm run build` 通过(细节见《实现原理》15.85 / 16.55)。
|