gis-view-webgl2 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +6 -3
  2. package/dist/index.cjs +1 -1
  3. package/dist/index.d.ts +172 -8
  4. package/dist/index.js +1 -1
  5. package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +121 -0
  6. package/docs/GisView.md +1 -1
  7. package/docs/ai/06-/344/274/232/350/257/235/347/264/242/345/274/225/344/270/216/345/244/215/346/237/245/346/226/271/346/263/225.md +117 -0
  8. package/docs/demos/LabelDemo.vue +2 -2
  9. package/docs/demos/MinimapDemo.vue +154 -1
  10. package/docs/demos/SearchFilterDemo.vue +113 -1
  11. package/docs/demos/StyleDemo.vue +1 -1
  12. package/docs/demos/api-index.js +22 -14
  13. package/docs/demos/demos.js +1 -1
  14. package/docs/demos/gis-view-demo/useGisViewDemo.js +2 -2
  15. package/docs/internals/13-/351/271/260/347/234/274/345/233/276.md +2 -0
  16. package/docs/internals/16-01-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/212.md +13 -2
  17. package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +11 -1
  18. package/docs/internals/18-/346/200/247/350/203/275/345/256/236/346/265/213/344/270/216WebGPU/350/257/204/344/274/260.md +61 -1
  19. package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +5 -4
  20. package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +38 -10
  21. package/docs/manual/05-/347/224/273/350/264/250/344/270/216/346/211/271/351/207/217/346/233/264/346/226/260.md +1 -1
  22. package/docs/manual/07-/346/214/207/351/222/210/344/270/216/350/246/201/347/264/240/347/274/226/350/276/221.md +7 -6
  23. package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +59 -3
  24. package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +14 -1
  25. package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +23 -5
  26. package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +12 -7
  27. package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +16 -8
  28. package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +27 -2
  29. package/docs/manual/19-/350/257/273/346/225/260/344/270/216/346/213/226/346/213/275/351/242/204/350/247/210.md +4 -0
  30. package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +9 -3
  31. package/docs/manual/21-/350/257/264/346/230/216/344/270/216/345/270/270/350/247/201/351/227/256/351/242/230.md +1 -1
  32. package/docs/steps/README.md +5 -5
  33. package/docs/steps/selftest-demos.html +105 -6
  34. package/docs/steps/selftest-docs.html +3 -3
  35. package/docs/steps/selftest-features.html +1 -1
  36. package/docs/steps/selftest-full.html +42 -1
  37. package/docs/steps/selftest-perf.html +600 -421
  38. package/docs/steps/selftest-select.html +168 -1
  39. package/package.json +1 -1
@@ -409,3 +409,124 @@
409
409
  - **实测**:20000 点 / 8000 线同一批对象回灌 5 次取中位,**14.3 ms → 6.3 ms**(优化后 5.6 / 6.3 / 6.3 / 6.4 / 10.7 ms)。
410
410
  - **核心文件**:`src/gis-view/engine.js`、`src/gis-view/types.d.ts`、`docs/manual/10` / `16` / `17` / `18`、`docs/steps/selftest-perf.html`、`docs/internals/16-01-优化与取舍-上.md`、`docs/internals/18-性能实测与WebGPU评估.md`、`docs/demos/api-index.js`(生成物)、本轮 aimd。
411
411
  - **验证**:`node --check`(engine / create)、`node docs/steps/gen-api-index.mjs`、`npm run check:docs`(**257** 个实例成员 = **254** 个方法 + 3 个属性 / **96** 个 options / **250** 个数据字段 / **43** 个事件 / **17** 个功能开关)、五页自测 `full 92` + `features 672` + `demos 351` + `docs 134` + `perf 167` = **1416** 条通过 / 0 失败、`npx tsc --noEmit`、`npm run build` 与 `git diff --check` 全部通过。复查入口见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-23-增量修改txt三项commitaddchannel-文档--传参示例--线路半跳过)。
412
+
413
+ ## 2026-10-23 增量(「问题.txt」两项:鹰眼图范围锁定开关 / 整图拖动 demo)
414
+
415
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录的 `问题.txt`:① 鹰眼图不应因主视图外围内容消失而自动重算包围盒,把这个行为做成默认关闭的开关;② 增加整体拖拽鹰眼图的 demo。
416
+
417
+ - **范围跟随开关**:`minimap.extentFollowContent?: boolean` 默认 `false`。`resolveExtent()` 在开关关闭时用 `pinnedExtent` 锁定首次取得的动态数据 / 底图范围,筛选、显隐、删除、新增内容只重画要素,不再改小图 `bounds` / `scale` / 中心;打开后才按当前可见内容重算。显式改 `extent`(预设或固定范围按值比较)以及切换开关都会清锁,下一次重画重新取范围;`extentFollowCamera` 的相机取舍基于锁定后的两份范围。
418
+ - **整图拖动 demo**:`MinimapDemo.vue` 通过 `getMinimapElement()` 在根节点追加独立手柄,手柄手势 `stopPropagation()`,不触发小图原有框选 / 平移;位移累计写回 `minimap.offset`,窗口级 `pointermove` / `pointerup` 收尾,并提供方向键微调、复位按钮和位移读数。
419
+ - **测试与文档**:`selftest-full.html` 新增 2 条断言覆盖默认锁定与打开后跟随内容;`selftest-demos.html` 新增 4 条断言覆盖开关下发、手柄拖动与复位。同步更新配置项 / 视图手册、鹰眼图内部文档、两个 README、类型注释、demo 描述与生成的 `docs/demos/api-index.js`。
420
+ - **核心文件**:`src/gis-view/minimap.js`、`src/gis-view/types.d.ts`、`docs/demos/MinimapDemo.vue`、`docs/demos/demos.js`、`docs/demos/api-index.js`(生成物)、`docs/manual/03` / `14`、`docs/internals/13-鹰眼图.md`、`src/gis-view/README.md`、根 `README.md`、`docs/steps/selftest-full.html`、`docs/steps/selftest-demos.html` 与本轮 aimd。
421
+ - **验证**:定向三页 `selftest-full` **94** / `selftest-demos` **355** / `selftest-docs` **134**,合计 **583** 条通过 / 0 失败;全量 14 页 **2295** 条通过 / 2 条失败,失败均为 `selftest-scale` 的筛选回调计数(本轮未改动筛选链路)。`node --check src/gis-view/minimap.js`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(**257** 个实例成员 / **96** 个 options / **250** 个数据字段 / **43** 个事件 / **17** 个功能开关)、`npm run build` 与 `git diff --check` 全部通过。复查入口见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-23-增量问题txt两项鹰眼图范围锁定开关--整图拖动-demo)。
422
+
423
+ ## 2026-10-23 增量(「修改.txt」两项:数据包围盒缓存与相机飞行)
424
+
425
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录的 `修改.txt`:① 继续优化组件整体性能;② 增加一些后续可能需要的新功能、方法、属性,并补 demo。
426
+
427
+ - **数据包围盒缓存**:`engine.js` 增加 `cachedDataBounds` / `cachedDataBoundsValid`,整份解析完成时直接接住 `fusedDataBounds`,普通 `getBounds()` 不再每次扫描全部已解析节点与线路;缓存对象与 `fusedDataBounds` 各持一份,`getBounds()` 也返回独立副本,调用方就地修改不会污染后续读数。`updatePoint` 坐标变化、`updateLine` 路径变化、拖拽、追加点 / 线以及复制预览改坐标都会显式失效缓存。十万点 + 十万线(每条 10 顶点)连续 10 次读数由约 **5.2 ~ 6.6 ms / 次** 降到 **0.00 ms / 次**;改一个点后首次重建约 **3.6 ms**,读数正确。
428
+ - **相机飞行 API**:新增 `flyTo(target, options?)`、`stopCameraAnimation()`、`getCameraAnimation()` 与只读属性 `cameraAnimating`。`flyTo` 认与 `focusOn` 相同的目标及 `zoom` / `scale` / `center` / `padding` / `maxZoom` / `select` / `mode` 语义,另支持 `duration`(缺省 450 ms)和 `easing`(`linear` / `ease-in` / `ease-out` / `ease-in-out` / 自定义函数);中心线性插值、比例按对数插值。再次 `flyTo`、用户拖拽 / 滚轮、程序化改相机、`fitView` / `resetView` 和销毁都会取消上一条动画;`duration <= 0` 或系统 `prefers-reduced-motion: reduce` 时立即落位。
429
+ - **Demo**:`docs/demos/SearchFilterDemo.vue` 增加“飞向选中”“停止飞行”按钮与实时动画读数;`docs/steps/selftest-perf.html` 增加 3 条包围盒缓存 / 失效 / 防污染断言,`docs/steps/selftest-demos.html` 增加 5 条飞行 API 与 demo 交互断言,并同步成员清单 261。
430
+ - **核心文件**:`src/gis-view/engine.js`、`src/gis-view/create.js`、`src/gis-view/types.d.ts`、`docs/demos/SearchFilterDemo.vue`、`docs/demos/api-index.js`(生成物)、`docs/GisView.md`、`docs/manual/14` / `16` / `17` / `18` / `20`、`docs/steps/README.md`、`docs/steps/selftest-{perf,demos,docs}.html`、根 `README.md`、`src/gis-view/README.md` 与本轮 aimd。
431
+ - **验证**:`node --check`(engine / create)、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(**261** 个实例成员 = **257** 个方法 + **4** 个属性 / **96** 个 options / **250** 个数据字段 / **43** 个事件 / **17** 个功能开关 / **16** 个分组)、四页定向自测 `full 94` + `demos 360` + `docs 134` + `perf 170` = **758** 条通过 / 0 失败、`npm run build` 与 `git diff --check` 全部通过。复查入口见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-23-增量修改txt两项数据包围盒缓存与相机飞行)。
432
+
433
+ ## 2026-10-23 增量(「问题.txt」两项:线路样式窄路与新 API 能力)
434
+
435
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录的 `问题.txt`:① 继续深度优化 gis-view 组件整体性能;② 从长远角度补充可能需要的功能、方法、属性,并添加 demo。
436
+
437
+ - **线路样式窄路**:`line.js` 抽出 `resolveLinePaintInto`,整份解析与窄路共用基准宽 / 半宽 / 颜色 / 透明度的取值链;`engine.js` 新增 `NARROW_LINE_STYLE_BUCKETS` / `lineStyleNarrowChange()` / `applyLineNarrowFast()`,只换 `lineStyle.width` / `color` / `opacity` 时逐条重算 paint 并补实例槽位。规则、热力图、线型 / 阴影 / 效果 / 方向等结构输入存在时回退整份解析;逐线自身样式与状态样式仍优先。整份重解析、单线更新和窄路现在都会同步 `baseWidth`,`getLineStyle` 不会读回旧线宽。
438
+ - **新增 API**:`flyToCamera(camera, options?)` 直接飞 `{ center?, scale? }`,缺省项沿用当前相机;只读 `dataVersion` 在数据变更时 +1、样式 / 配置变更不递增,`onDataChange` 载荷同步带出版本;`getLineStyle(index)` 返回解析后的 `{ width, color, opacity, zIndex }`,线宽取要素自身 > 规则 > `lineStyle`,不含状态样式、热力倍率、整体倍率与缩放联动。成员总数为 **264 = 259 个方法 + 5 个属性**,options / 数据字段 / 事件 / 功能开关 / 分组保持 **96 / 250 / 43 / 17 / 16**。
439
+ - **Demo 与自测**:`SearchFilterDemo.vue` 增加固定相机飞行、线路换色 / 加宽、改点后版本读数;`selftest-demos.html` 覆盖 `flyToCamera`、`getLineStyle`、`dataVersion` 与 `onDataChange` 同步,`selftest-perf.html` 覆盖窄路读数、`dataVersion` 不变、逐线自身样式 / 虚线保持、规则回退,并记录 2000 线窄路耗时。
440
+ - **性能**:1000 点 + 50000 线连续 5 次交替(无头 Chrome + SwiftShader)。换色稳态 **10.1 ~ 11.7 ms → 6.1 ~ 7.4 ms**,换宽 **9.2 ~ 10.4 ms → 5.8 ~ 6.4 ms**;首轮 18.9 / 17.3 ms,包含槽位表与交互期细线账本首次重建。profile 热点为 `patchLineSlotsInner` 30.8% / `patchLineSlots` 17.3% / `applyLineNarrowFast` 13.0% / `resolveLinePaintInto` 3.8%。
441
+ - **核心文件**:`src/gis-view/line.js`、`src/gis-view/engine.js`、`src/gis-view/create.js`、`src/gis-view/types.d.ts`、`docs/demos/SearchFilterDemo.vue`、`docs/demos/api-index.js`(生成物)、`docs/GisView.md`、`docs/manual/04` / `14` / `16` / `17` / `18` / `19` / `20`、`docs/internals/16-01-优化与取舍-上.md`、`docs/internals/18-性能实测与WebGPU评估.md`、`docs/steps/README.md`、`docs/steps/selftest-{demos,docs,perf}.html`、根 `README.md`、`src/gis-view/README.md` 与本轮 aimd。
442
+ - **验证**:`node --check`(create / engine / line)、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(**264** 个实例成员 / **96** 个 options / **250** 个数据字段 / **43** 个事件 / **17** 个功能开关)、四页自测 `full 94` + `demos 365` + `docs 134` + `perf 174` = **767** 条通过 / 0 失败、`npm run build` 与 `git diff --check` 全部通过。复查入口见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-23-增量问题txt两项线路样式窄路与新-api-能力)。
443
+
444
+ ## 2026-10-23 增量(「修改.txt」:继续深度优化 gis-view 整体性能)
445
+
446
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录的 `修改.txt`:继续深度优化组件整体性能,允许大改,只要确实有效。
447
+
448
+ - **聚合空转**:`flushPointClusterLayout()` 在聚合未启用且上一帧没有聚合圆 / 隐藏点 / 聚合实例时只清脏标记,不再调用 `layoutScene()`;`notifyCameraChange()` 也只在聚合启用且当前档可能聚合、或上一帧确有聚合结果时因缩放标脏。关闭聚合后连续缩放不再整份重排点和重传实例缓冲。纯相机缩放引起的成员变化在运动期先沿用上一档,数据 / 配置 / 选中 / 拖动 / 跨出聚合档仍立即生效。
449
+ - **程序化相机运动**:新增 `cameraMoving` 160 ms 短窗口,`zoomBy` / `setCamera` / `centerOn` / `flyToCamera` 的真实位置变化复用细线 `gl.LINES` 与非 MSAA 后备缓冲;同一相机位置重复 `setCamera` 不续期,避免外部定时同步视角永久停在轻量档。停稳后补一张全质量帧、刷新通道宽度并恢复符号预烤。
450
+ - **停稳边界**:聚合重排被推迟时,即便当前场景没有线且显式关闭了后备缓冲、这一轮没有画出任何轻量几何,计时器也会补一帧完成聚合重算;新增 12000 点、无边场景的回归断言钉住这一条。
451
+ - **实测**:无头 Chrome + ANGLE(AMD Radeon Graphics / D3D11),1280×800,10 万点 / 5 万线,`quality: 'high'`;同一命令下连续缩放 p50 **67.9 → 22.5 ms**、p95 **76.2 → 25.1 ms**、avg **60.77 → 20.57 ms**。平移 p50 17.1 → 17.2 ms(帧节拍波动),`refresh()` 56.2 → 53.7 ms;终态 `thinLines=false`、`noMsaa=false`、`frameMs=0.10`。CPU profile 里 `render` 自身约 0.1%,剩余成本主要是 GPU 光栅化与帧节拍,收益口径是省掉整份重排 / 上传与运动期 MSAA 细线光栅化,不是把单帧渲染降到 1 ms。
452
+ - **核心文件**:`src/gis-view/engine.js`、`docs/steps/selftest-perf.html`、`docs/manual/20-性能与大数据.md`、`docs/internals/16-03-优化与取舍-下.md`、`docs/internals/18-性能实测与WebGPU评估.md`、根 `README.md`、`src/gis-view/README.md`、本轮 aimd。
453
+ - **验证**:`node --check src/gis-view/engine.js`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(**264** 个实例成员 / **96** 个 options / **250** 个数据字段 / **43** 个事件 / **17** 个功能开关)、四页自测 `full 94` / `demos 365` / `docs 134` / `perf 181`(合计 **774** 条通过 / 0 失败)、`npm run build` 与 `git diff --check` 全部通过。公开实例成员、options 与数据字段计数未变。复查入口见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-23-增量修改txt继续深度优化-gis-view-整体性能)。
454
+
455
+ ## 2026-10-26 增量(「修改.txt」:修复放大缩小与移动时的短暂模糊)
456
+
457
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录最新的 `修改.txt`:修改 gis-view 组件;修改之后放大缩小 / 移动会模糊一下。核对后确认根因是上一轮加入的运动期自适应分辨率:帧间隔超过 22 ms 时把后备缓冲降到 0.65× / 0.5×,再用线性过滤放大贴回。
458
+
459
+ - **根因与撤回**:`engine.js` 删除 `MOTION_RESOLUTION_STEPS` / `sampleMotionResolution()`,运动帧不再缩小 viewport,也不再按 `drawWidth` / `drawHeight` / `framePixelRatio` 计算像素尺度;`shaders.js` 的 `u_offsetScale`、`u_uvOffset` / `u_uvScale` 与 `create.js` 的 `exportFrame` 配套路径一并撤回。交互、滚轮和 `zoomBy` / `setCamera` / 飞行帧现在始终按画布全分辨率绘制,整幅贴回保持 1:1;停稳 160 ms 后照旧恢复 MSAA 静态帧。
460
+ - **保留的优化**:相机轻量窗口、细线 `gl.LINES` 与非 MSAA 后备缓冲仍保留,只换画法、不降分辨率。`pointSizeScale` / `lineWidthScale` / `pointZoomScale` / `lineZoomScale` / `zoomBaseScale` 连续变化时继续走 `scaleMotion` 窗口,暂停符号重烤与预烤,停手后对齐当前尺寸档;相机与尺寸窗口重叠时由最后停下的一方补质量帧。
461
+ - **回归与性能**:`selftest-perf.html` 新增 1 条断言:连续改相机并人为拉长帧间隔,记录到的每次 `gl.viewport` 都必须等于画布全尺寸。最终全分辨率基准为无头 Chrome + ANGLE / AMD D3D11、1280×800、三次取中位:10 万点 / 5 万线缩放 p50 **23.2 ms** / p95 **28.6 ms**、平移 **16.7 / 18.2 ms**;10 万点 / 10 万线缩放 **42.6 / 46.3 ms**、平移 **16.7 / 24.5 ms**。
462
+ - **核心文件**:`src/gis-view/engine.js`、`docs/steps/selftest-perf.html`、`docs/steps/README.md`、`docs/manual/20-性能与大数据.md`、根 `README.md`、`src/gis-view/README.md` 与本轮 aimd;`src/gis-view/shaders.js` / `create.js` 的上一轮配套改动已撤回至 HEAD。
463
+ - **验证**:`node --check src/gis-view/engine.js`、`node --check src/gis-view/create.js`、`node --check src/gis-view/shaders.js`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs`(264 个实例成员未变)、`npm run check:docs`(264 个实例成员 / 250 个方法 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、九个定向自测页 **1504** 条通过 / 0 失败(features 672 / actions 96 / data 103 / decoration 75 / select 69 / labels 165 / effects 96 / docs 134 / full 94)、`npm run build` 与 `git diff --check` 全部通过。`selftest-perf` **181 / 1**(共 182 条;标签定向重排读数 1313 次、重复 0 条,HEAD 基线已复现),新增的相机全分辨率断言通过;`selftest-scale` 117 / 2(onFilter 计数为 0,筛选链路未改)。公开实例成员、options 与数据字段计数未变。复查入口见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-26-增量修改txt修复放大缩小与移动时的短暂模糊)。
464
+
465
+ ## 2026-10-26 增量(「修改.txt」:继续优化 gis-view,回答“感觉没提升多少”)
466
+
467
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录最新的 `修改.txt`:修改 gis-view 组件;这样修改之后性能提升了吗,感觉没提升多少。
468
+
469
+ - **先排除 JS**:10 万线交互的 CPU profile 约 **98.6% idle**、`render` 自身约 **0.1%**;上一轮省掉的是场景重排、实例上传与相机排帧,主线程已经接近空转。用户仍会觉得慢,是因为每条可见线在 GPU 片元里继续执行虚线取模、`smoothstep`、边缘覆盖率与光晕分支,且此前优化没有覆盖所有线宽。
470
+ - **片元管线专用化**:`shaders.js` 新增 `THIN_LINE_FRAGMENT_SHADER`,交互期 `gl.LINES` 只输出实例颜色,顶点侧不再携带虚线 / AA / 光晕 varying;新增 `SOLID_LINE_FRAGMENT_SHADER`,整批无虚线且这一遍不是热力光晕的四边形线路跳过虚线取模 / `smoothstep` / 光晕分支,只保留颜色与非 MSAA 边缘覆盖率。`engine.js` 的 `drawLineBatch` 按批切换,`refreshLineThinLedger` 同时返回是否全实线;线宽、端点圆头、全分辨率与停稳后的 MSAA 静态帧都保持。另加属性槽位一致性检查,驱动给两个程序分配不同位置时自动回退通用片元,避免跨驱动画面异常。
471
+ - **门槛修正**:细线快路径实际条件是最大半宽 ≤ 0.6 设备像素且无虚线(dpr = 1 时约 1.2px 线宽,2px 实线不会命中);文档里此前写的「3px 以内」已同步纠正。2px 实线只吃四边形轻量片元,10 万线实测约 **82.4 → 63.9 ms**。
472
+ - **实测**:无头 Chrome + ANGLE / AMD D3D11、1280×800、`quality: 'high'`,三次运行取中位。10 万点 / 5 万线缩放 p50 **23.2 → 16.7 ms**、p95 **28.6 → 16.8 ~ 16.9 ms**;10 万点 / 10 万线缩放 p50 **42.6 → 18.0 ms**、p95 **46.3 → 21.4 ms**,平移 p50 **16.7 ms**。16 ~ 18 ms 已接近 60Hz 帧节拍,剩余成本是 GPU 填充率;继续明显提速只能减少可见线段数或屏幕覆盖面积。
473
+ - **正确性回归**:`selftest-perf.html` 新增 1 条像素级对拍,在同一交互状态下比较细线专用管线与四边形画法的线像素数量和整幅颜色差,防止新管线画丢、画黑或换色;新增断言与既有断言均通过。
474
+ - **核心文件**:`src/gis-view/shaders.js`、`src/gis-view/engine.js`、`docs/steps/selftest-perf.html`、`docs/steps/README.md`、`docs/manual/03-配置项.md`、`docs/manual/05-画质与批量更新.md`、`docs/manual/20-性能与大数据.md`、`docs/manual/21-说明与常见问题.md`、`docs/internals/18-性能实测与WebGPU评估.md`、根 `README.md`、`src/gis-view/README.md` 与本轮 aimd。
475
+ - **验证**:`node --check src/gis-view/engine.js`、`node --check src/gis-view/shaders.js`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs`(264 个实例成员未变)、`npm run check:docs`(264 个实例成员 / 250 个方法 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、九个定向自测页 **1504** 条通过 / 0 失败、`npm run build` 与 `git diff --check` 全部通过。`selftest-perf` **182 / 1**(共 183 条;标签读数 1313 次、重复 0 条,HEAD 基线已复现),新增像素断言通过;`selftest-scale` 117 / 2(既有的 onFilter 计数失败,筛选链路未改)。公开实例成员、options 与数据字段计数未变。复查入口见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-26-增量修改txt线片段着色管线专用化)。
476
+
477
+ ## 2026-10-26 增量(「修改.txt」:选中 / 取消选中入口速查)
478
+
479
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录最新的 `修改.txt`:修改 gis-view 组件;现在组件如何选中和取消选中。
480
+
481
+ - **现状核对**:公开入口只有 `selectFeatures(refs, options?)` 与 `deselect(refs?)`,`select()` 已在重复入口收口时删除。鼠标单选、Ctrl / ⌘ 取反、`multiSelect`、`boxSelect`、空白点击 / Esc 清空,以及接口替换 / 追加 / 取反 / 移除 / 清空都已存在,因此没有新增重复方法。
482
+ - **文档改动**:`docs/manual/08-选中与拐点.md` 顶部新增「选中与取消选中速查」,把鼠标 / 键盘 / 接口入口、`selectTrigger: 'dblClick'` 的差异、读回方法与 `onSelectChange` 放在一处;同时修正正文与当前 demo / 自测注释里的过期 `select()` 名称。
483
+ - **核心文件**:`docs/manual/08-选中与拐点.md`、`docs/demos/StyleDemo.vue`、`docs/demos/gis-view-demo/useGisViewDemo.js`、`docs/steps/selftest-features.html`、`docs/AI对话汇总.md`、`docs/ai/06-会话索引与复查方法.md`。
484
+ - **验证**:`node docs/steps/gen-api-index.mjs --check`(生成索引最新)、`npm run check:docs`(264 个实例成员 / 250 个方法 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关全部命中)、`npx tsc --noEmit`、`npm run build`、`selftest-select.html` **69** 条通过 / 0 失败,以及 `git diff --check` 全部通过。公开 API 计数未变。
485
+ ## 2026-10-26 增量(「修改.txt」:按坐标模拟单击选中 selectAt)
486
+
487
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、我说的选中是手动触发单击选中,比如手动选中某个节点,某个通道,线路等,组件有这个方法吗」。
488
+
489
+ - **结论**:已有 `selectFeatures(refs, options?)` 可按 id / 下标 / `{ type, index }` 手动选中节点、线路、通道;`pickAt()` 只读命中、不改选中。为「拿鼠标坐标当一次单击」,本轮新增 `selectAt(clientX, clientY)`,没有恢复已删除的 `select()`。
490
+ - **行为**:`selectAt` 按真实单击相同的 `pick()` 顺序命中节点 / 线路并立即调用 `applySelection`;通道回线按 `channelSelectMode` 摊成一条 / 一组 / 整条通道。空白或聚合圆返回 `null`,不清空原选中;不等待 `selectTrigger: 'dblClick'`,也不派发 `onClick` / `onDblClick`,但 `onSelect` / `onSelectChange` 正常发,`cause` 为 `api`。
491
+ - **设计取舍**:这是接口入口,固定替换当前选中集;追加 / 取反仍用 `selectFeatures(refs, { mode: 'add' | 'toggle' })`。要清空仍用 `deselect()` / `selectFeatures(null)`。返回值为 `GisViewSelectPayload | null`。
492
+ - **核心文件**:`src/gis-view/create.js`、`src/gis-view/engine.js`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/manual/08-选中与拐点.md`、`docs/manual/09-通道与多回线.md`、`docs/manual/16-实例方法总表.md`、`docs/manual/17-参数速查.md`、`docs/manual/18-逐方法示例.md`、`README.md`、`docs/GisView.md`、`docs/steps/README.md`、`docs/steps/selftest-docs.html`、`docs/steps/selftest-demos.html`、`docs/demos/api-index.js`(生成物)与本轮 aimd。
493
+ - **验证**:`node --check src/gis-view/create.js`、`node --check src/gis-view/engine.js`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(265 个实例成员 = 260 个方法 + 5 个属性 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关全部命中)、`selftest-select.html` **73** 条、`selftest-docs.html` **134** 条、`selftest-demos.html` **365** 条,三页共 **572** 条通过 / 0 失败;`npm run build` 与 `git diff --check` 全部通过。详细口径见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-26-增量修改txt按坐标模拟单击选中-selectat)。
494
+ ## 2026-10-26 增量(「修改.txt」:选中接口的回调口径)
495
+
496
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、selectFeatures为什么不会触发onSelect事件,也会将内容标记为选中的样式,不是选中吗,新增的selectAt和selectFeatures有什么区别」。
497
+
498
+ - **结论**:`selectFeatures()` / `deselect()` 确实会改同一份选中 state,选中样式、`isSelected()` 与 `getSelectionData()` 立即更新;它们属于按引用改选中集的接口路径,只发 `onSelectChange`(`cause: 'api'`),不补发点击 / 框选语义的 `onSelect`。`selectAt()` 是明确的「按坐标模拟单击」,因此同时发 `onSelect` 与 `onSelectChange`,但不发 `onClick` / `onDblClick`。
499
+ - **事件口径**:`onSelectChange` 覆盖点击、框选、键盘清空与接口调用,并且只在选中集真的变化时发,适合统一同步面板;`onSelect` 覆盖点击 / 框选语义(真实点击 / Ctrl 多选 / 框选(含 `selectInBox()`)/ 空白清空)与模拟单击的 `selectAt()`。此前手册 17 把 `selectFeatures` 写成也会发 `onSelect`,与实际行为不一致,已改正。
500
+ - **两个方法的区别**:`selectAt(clientX, clientY)` 吃坐标、走与真实单击相同的 `pick()`,通道回线按 `channelSelectMode` 摊开,空白返回 `null` 且不动原选中,返回本次载荷;`selectFeatures(refs, options)` 吃 id / 下标 / 通道 / 分组引用,`mode` 支持 replace / add / toggle / remove,`null` / 空数组清空,返回实例本身。两者最终改的是同一份选中 state,样式与读数没有区别。
501
+ - **核心文件**:`docs/manual/04-事件与回调.md`、`docs/manual/08-选中与拐点.md`、`docs/manual/16-实例方法总表.md`、`docs/manual/17-参数速查.md`、`docs/manual/18-逐方法示例.md`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/steps/README.md`、`docs/demos/api-index.js`(生成物)与本轮 aimd。
502
+ - **验证**:`selftest-select.html` 新增 2 条事件口径断言(73 → **75**),三页定向自测 **574** 条通过 / 0 失败(select 75 / docs 134 / demos 365);`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(265 个实例成员 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关全部命中)、`npm run build` 与 `git diff --check` 全部通过。运行时代码与公开成员计数未变。详细口径见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-26-增量修改txt选中接口的回调口径)。
503
+
504
+ ## 2026-10-26 增量(「修改.txt」:统一选中回调与鼠标分组载荷)
505
+
506
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、选中指的不就是点击触发select的选中吗,什么叫真正的选中 2、那先selectFeatures有什么用,我想直接通过方法选中一个节点如何操作,用selectAt传坐标,不多余吗?3、鼠标事件里的groups怎么没」。
507
+
508
+ - **选中语义统一**:点击、框选与 `selectFeatures()` / `deselect()` / `selectAt()` 现在共用同一份选中 state,实际改变选中集时都从组件级 `handleSelectPayload(payload, detail)` 发出 `onSelect` 与 `onSelectChange`;接口来源的 `cause` 仍是 `'api'`,原样重设同一选中集只同步镜像与读数、不重复发回调。`selectAt()` 只解决「手上只有客户端坐标」这一种场景:走与单击相同的 `pick()`,立即生效但不派发 `onClick` / `onDblClick`;已知节点 / 线路 id 时直接 `selectFeatures('id')` 或传引用。上一轮「接口只发 onSelectChange」的章节保留为历史,本轮口径已取代它。
509
+ - **鼠标事件分组载荷**:通道回线最终命中时,拾取结果除 `group`(当前组)外补 `groups`(同一通道全部分组,按声明顺序,每项带 `groupIndex` / `lineIds` / `path` 与成员 / 拐点读数)。`pickAt()`、`hoverAt()` / `onHover`、`onPointerDown` / `onPointerUp`、`onClick` / `onDblClick`、`onContextMenu` 都是同一份;全部分组只在最终命中后构建,悬停扫描过程中不复制成员数组。
510
+ - **核心文件**:`src/gis-view/create.js`、`src/gis-view/engine.js`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/manual/04-事件与回调.md`、`07-指针与要素编辑.md`、`08-选中与拐点.md`、`09-通道与多回线.md`、`16-实例方法总表.md`、`17-参数速查.md`、`18-逐方法示例.md`、`src/gis-view/README.md`、`docs/steps/README.md`、`docs/demos/api-index.js`(生成物)与本轮 aimd。
511
+ - **验证**:`node --check src/gis-view/create.js`、`node --check src/gis-view/engine.js`、`npx tsc --noEmit`;四页定向自测 **1250** 条通过 / 0 失败(select 79 / features 672 / docs 134 / demos 365);`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(265 个实例成员 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关全部命中);`npm run build` 与 `git diff --check` 通过(后者仅有 CRLF 提示)。详细口径见 [06 的增量记录](ai/06-会话索引与复查方法.md#2026-10-26-增量修改txt统一选中回调与鼠标分组载荷)。
512
+
513
+ ## 2026-10-26 增量(「修改.txt」:选中事件分工、通道上下文与派发顺序)
514
+
515
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、onSelectChange和onSelect有必要同时存在吗,各自的意义是什么 2、为什么这两个事件里都拿不到通道的信息,group和groups都没有,隐藏这两个参数的意义是什么 3、为什么控制台顺序onSelectChange>onSelect>onClick」。
516
+
517
+ - **两个事件的职责**:保留 `onSelect` 与 `onSelectChange`。`onSelect` 是**本次选中操作**回调:真实单击 / Ctrl / `multiSelect` 取反 / 框选 / `selectAt()` 每次成功操作都发,清空或取反取没了时载荷为 `null`;`selectFeatures()` / `deselect()` / `focusOn({ select })` 等按引用接口只在实际改变选中集时发。`onSelectChange` 是**选中集合状态**回调:所有来源都只在实际变化时发,带 `ids` / `count` / 类型计数 / `cause` / `action`,需要统一同步面板时只订阅它。
518
+ - **通道上下文**:两条选中回调现在带同一套上下文。选中集只涉及一条通道时顶层平铺 `channelId` / `channelIndex` / `channel` / `groupId` / `groupIndex` / `group` / `groups`;多条通道时只给 `channelContexts`,每项带同样字段与本次选中的 `lineIndexes` / `lineIds`。`items[]` 里的通道回线也补 `group` / `groups`。真实单击与 `selectAt()` 的当前组是命中的那一组;框选 / 按引用选中取选中集里第一条通道回线所在的组;`groups` 始终是整条通道的全部分组。
519
+ - **派发顺序**:旧顺序 `onSelectChange → onSelect → onClick` 改为固定 `onClick → onSelect → onSelectChange`。普通单击由组件层 `deferredSelection` 只延后选中回调、先同步选中镜像与标签,等 `onClick` 派发后再补 `onSelect` / `onSelectChange`;Ctrl / `multiSelect` 取反、框选与接口选中没有 `onClick`,顺序为 `onSelect → onSelectChange`;双击模式第二下为 `onClick → onSelect → onSelectChange(有变化时)→ onDblClick`。回调里读 `getSelectionData()` 仍是最新值。
520
+ - **channelIndex 修正**:分组通道此前在拾取 / 悬停 / `describe` / `listLines` / 级联 / `getLineChannel` 等六处取了分组状态序号 `state.order`,与「通道在 `channels` 声明数组里的下标」不是一回事;现统一改为 `state.channelIndex`,通道增删或分组重排后读数不再漂移。
521
+ - **核心文件**:`src/gis-view/engine.js`、`src/gis-view/create.js`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/manual/04-事件与回调.md`、`07-指针与要素编辑.md`、`08-选中与拐点.md`、`09-通道与多回线.md`、`16-实例方法总表.md`、`17-参数速查.md`、`18-逐方法示例.md`、`src/gis-view/README.md`、`docs/steps/README.md`、`docs/demos/gis-view-demo/useGisViewDemo.js`、`docs/demos/api-index.js`(生成物)与本轮 aimd。
522
+ - **验证**:`node --check`(engine / create)、`npx tsc --noEmit`;`selftest-select.html` **83** 条、`selftest-features.html` **672** 条、`selftest-docs.html` **134** 条、`selftest-demos.html` **365** 条,四页共 **1254** 条通过 / 0 失败;`npm run build` 通过;`node docs/steps/gen-api-index.mjs --check` PASS(索引最新)、`npm run check:docs` 全 PASS(265 个实例成员 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、`git diff --check` 通过(仅有 CRLF 提示)。公开实例成员 / options / 数据字段 / 事件 / 功能开关计数均未变。
523
+
524
+ ## 2026-10-26 增量(「修改.txt」:选中载荷去重与通道索引缓存)
525
+
526
+ 本轮仍是现有会话的继续增量,没有新建会话归档,因此不改上表的 39 条会话统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、本次修改会影响组件性能吗,比如事件参数添加group和groups,selectFeatures触发onSelect等,如果影响性能,如何优化 2、现在onSelect和onSelectChange参数都有通道信息,下面的items里面也有通道信息,是不是有些多余, 3、继续检查组件,有没有冗余代码,继续提高组件性能」。
527
+
528
+ - **性能结论**:选中事件不进入逐帧渲染路径,只在选中操作 / 选中集实际变化时构造;`onSelect` 与 `onSelectChange` 共享同一批 `items`、分组对象与数组引用,不深拷贝。`selectFeatures()` 仍只在集合实际变化时发事件,同内容、同顺序的重设现在会在 `selectionKeys()`、引擎刷新与 `describeSelection()` 之前直接返回;`selectAt()` 每次成功命中发 `onSelect` 是既定语义,开销只随本次选中的通道 / 分组数增长,不随全量点线数增长。
529
+ - **载荷去重**:单通道只把 `channelId` / `channelIndex` / `channel` / `groupId` / `groupIndex` / `group` / `groups` 摊平到载荷顶层,不再额外创建 `channelContexts`;多通道才给 `channelContexts`。`items[]` 里的通道回线只保留 `channelId` / `channelIndex` / `groupId` / `groupIndex` 标量归属,不再逐条挂 `group` / `groups`,分组读数统一从顶层或 `channelContexts` 取。指针事件载荷保持原样,不受这条限制。上一节“items 里的通道回线也补 group / groups”的口径由本节撤回。
530
+ - **冗余与性能**:通道重建时按 `channelIndex` 同步收好分组状态数组,并把 `groupOrdinal` 直接记在通道状态上;`channelSiblings()` / `channelGroupOrdinal()` 从扫整张通道表降为 O(1),构造一条通道的全部分组读数从 O(组数 × 通道数) 降为 O(组数)。选中上下文按已记录的组序号取当前组,不再 `findIndex`;删掉已无引用的 `state.order` 和建通道时的重复 `channels.filter(...)`。单通道选中不再构造随后丢弃的 `lineIndexes` / `lineIds`,多通道才在 `channelContexts` 里按选中顺序补一次。
531
+ - **核心文件**:`src/gis-view/engine.js`、`src/gis-view/create.js`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/steps/README.md`、`docs/manual/04-事件与回调.md`、`07-指针与要素编辑.md`、`09-通道与多回线.md`、`20-性能与大数据.md`、`src/gis-view/README.md` 与本轮 aimd。
532
+ - **验证**:`node --check`(engine / create)、`npx tsc --noEmit`、`npm run build` 通过;五页定向自测 **1438** 条通过 / 0 失败(select 84 / features 672 / docs 134 / demos 365 / perf 183);`node docs/steps/gen-api-index.mjs --check` PASS(索引最新)、`npm run check:docs` 全 PASS(265 个实例成员 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、`git diff --check` 通过(仅有 CRLF 提示)。公开实例成员与配置计数未变。
package/docs/GisView.md CHANGED
@@ -32,7 +32,7 @@
32
32
  | [14 视图与筛选](manual/14-视图与筛选.md) | 筛选、搜索定位、初始视图与缩放范围、背景层、鹰眼图、近义入口 |
33
33
  | [15 导出与集成](manual/15-导出与集成.md) | 导出图片 / 选中节点 / 数据、自定义导出格式,插件、自建界面与键盘交互 |
34
34
  | [16 实例方法总表](manual/16-实例方法总表.md) | 按功能分组的实例方法主表(找方法从这一章开始) |
35
- | [17 参数速查(逐个方法)](manual/17-参数速查.md) | 257 个成员逐个列全:参数、默认值与返回 |
35
+ | [17 参数速查(逐个方法)](manual/17-参数速查.md) | 265 个成员逐个列全:参数、默认值与返回 |
36
36
  | [18 逐方法示例(复制就能跑)](manual/18-逐方法示例.md) | 每个成员一段可复制的调用示例(API 速查页读的就是这一章) |
37
37
  | [19 读数与拖拽预览](manual/19-读数与拖拽预览.md) | 自建界面常用的读数 / 定位方法;拖拽落点预览 |
38
38
  | [20 性能与大数据](manual/20-性能与大数据.md) | 十万级数据的开关与实测、WebGPU 取舍、渲染后端开关 |
@@ -393,6 +393,123 @@
393
393
  - **自测与文档**:`selftest-perf.html` 新增 3 条断言,锁住原样回灌、通道声明未变沿用派生折线、换骨架必须重推几何;性能页 **167** 条通过。性能记录同步到内部文档 `16-01` 的 16.37 与 `18` 的第十六轮。
394
394
  - **验证**:五页自测 full **92** / features **672** / demos **351** / docs **134** / perf **167**,合计 **1416** 条通过 / 0 失败;`node --check`、`node docs/steps/gen-api-index.mjs`、`npm run check:docs`(257 个实例成员 / 96 个 options / 250 个数据字段 / 43 个事件 / 17 个功能开关)、`npx tsc --noEmit`、`npm run build` 与 `git diff --check` 全部通过。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-23-增量修改txt三项commitaddchannel-文档--传参示例--线路半跳过)。
395
395
 
396
+ ### 2026-10-23 增量(「问题.txt」两项:鹰眼图范围锁定开关 / 整图拖动 demo)
397
+
398
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录的 `问题.txt`:外围内容消失不应让鹰眼图自动重算包围盒,该行为做成默认关闭的开关;另补整体拖拽鹰眼图的 demo。
399
+
400
+ - **范围锁定**:`minimap.extentFollowContent` 默认 `false`。`minimap.js` 用 `pinnedExtent` 锁住首次取得的动态数据 / 底图范围,内容筛选、显隐或增删只重画要素,小图范围与视口框保持不动;打开后跟随当前可见内容。显式改 `extent` 或切换开关会清锁并重新取范围,`extentFollowCamera` 的相机取舍使用锁定后的两份范围。
401
+ - **整图拖动**:`MinimapDemo.vue` 在 `getMinimapElement()` 根节点挂独立手柄,手势不进入小图框选 / 平移;累计位移写回 `offset`,窗口级事件收尾,并提供方向键微调、复位与读数。
402
+ - **自测与文档**:`selftest-full.html` 增加 2 条范围锁定断言,`selftest-demos.html` 增加 4 条开关 / 拖动 / 复位断言;同步手册 `03` / `14`、内部文档 `13`、两个 README、类型注释与生成的 `docs/demos/api-index.js`。
403
+ - **验证**:定向三页 full **94** / demos **355** / docs **134**,合计 **583** 条通过 / 0 失败;全量 14 页 **2295** 条通过 / 2 条失败,失败均为 `selftest-scale` 的筛选回调计数,与 minimap 改动无关。`node --check`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(257 个实例成员 / 96 个 options / 250 个数据字段 / 43 个事件 / 17 个功能开关)、`npm run build` 与 `git diff --check` 全部通过。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-23-增量问题txt两项鹰眼图范围锁定开关--整图拖动-demo)。
404
+
405
+ ### 2026-10-23 增量(「修改.txt」两项:数据包围盒缓存与相机飞行)
406
+
407
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录的 `修改.txt`:① 继续优化组件整体性能;② 补充后续可能需要的新功能、方法、属性与 demo。
408
+
409
+ - **包围盒缓存**:`engine.js` 把整份解析得到的范围接给 `getBounds()` 读数缓存,读数返回独立副本,缓存本身不与 `fusedDataBounds` 共享;`updatePoint` 改坐标、`updateLine` 改路径、拖拽、追加点 / 线、复制预览改坐标都会失效缓存。十万点 + 十万线实测连续读数 **5.2 ~ 6.6 ms / 次 → 0.00 ms / 次**,单点改动后重建约 **3.6 ms**。
410
+ - **相机飞行**:新增 `flyTo` / `stopCameraAnimation` / `getCameraAnimation` / `cameraAnimating`;时长缺省 450 ms,中心线性、比例对数插值,支持三种预设缓动与自定义函数。用户操作、程序化改相机、再次飞行和销毁都会取消;零时长或系统减少动态效果时立即落位。`SearchFilterDemo.vue` 增加飞向选中、停止飞行与进度读数。
411
+ - **自测与文档**:`selftest-perf.html` 增 3 条缓存断言,`selftest-demos.html` 增 5 条飞行 / demo 断言并更新成员清单;同步方法总表、参数速查、逐方法示例、性能手册、两个 README、类型声明与生成的 `docs/demos/api-index.js`。
412
+ - **验证**:`node --check`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(261 个实例成员 / 96 个 options / 250 个数据字段 / 43 个事件 / 17 个功能开关)、四页自测 full **94** / demos **360** / docs **134** / perf **170**(合计 **758** 条通过 / 0 失败)、`npm run build` 与 `git diff --check` 全部通过。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-23-增量修改txt两项数据包围盒缓存与相机飞行)。
413
+
414
+ ### 2026-10-23 增量(「问题.txt」两项:线路样式窄路与新 API 能力)
415
+
416
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录的 `问题.txt`:① 继续深度优化组件整体性能;② 从长远角度补充可能需要的功能、方法、属性与 demo。
417
+
418
+ - **窄路**:`line.js` 抽出 `resolveLinePaintInto`;`engine.js` 用 `NARROW_LINE_STYLE_BUCKETS` / `lineStyleNarrowChange()` / `applyLineNarrowFast()` 认出只改线宽 / 颜色 / 透明度的补丁。命中时逐条重算 paint 并补实例槽位,规则、热力、线型 / 阴影 / 效果 / 方向存在时回退整份解析;逐线自身样式与状态样式仍优先。整份重解析、单线更新和窄路都会同步 `baseWidth`。
419
+ - **新 API**:`flyToCamera` 直接飞相机目标;`dataVersion` 只在数据变更时递增,`onDataChange` 载荷同步带出;`getLineStyle` 返回原始线宽 / RGBA / 透明度 / 层级快照,线宽不含状态样式、热力与缩放倍率。成员 264 = 259 方法 + 5 属性。
420
+ - **Demo 与文档**:`SearchFilterDemo.vue` 增加相机飞行、线路换色 / 加宽、改点与版本读数;自测覆盖 `flyToCamera`、`getLineStyle`、`dataVersion`、虚线保持与规则回退。同步 `docs/manual/04` / `14` / `16` / `17` / `18` / `19` / `20`、内部性能文档、两个 README、类型声明与生成的 `docs/demos/api-index.js`。
421
+ - **验证**:`node --check`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(264 个实例成员 / 96 个 options / 250 个数据字段 / 43 个事件 / 17 个功能开关)、四页自测 full **94** / demos **365** / docs **134** / perf **174**(合计 **767** 条通过 / 0 失败)、`npm run build` 与 `git diff --check` 全部通过。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-23-增量问题txt两项线路样式窄路与新-api-能力)。
422
+
423
+ ### 2026-10-23 增量(「修改.txt」:继续深度优化 gis-view 整体性能)
424
+
425
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求来自仓库根目录的 `修改.txt`:继续深度优化组件整体性能,允许大改,只要确实有效。
426
+
427
+ - **聚合空转**:`flushPointClusterLayout()` 在聚合未启用且没有上一帧聚合结果时只清脏标记,不跑 `layoutScene()`;`notifyCameraChange()` 只在聚合启用且当前档可能变化、或确有旧聚合结果时因缩放标脏。纯相机缩放引起的成员变化在运动期沿用旧档,停稳后重算;配置 / 数据 / 选中 / 拖动 / 跨出 `maxZoom` 仍立即生效。
428
+ - **程序化相机快路径**:`zoomBy` / `setCamera` / `centerOn` / `flyToCamera` 的真实相机变化进入 160 ms 短窗口,运动帧走细线 `gl.LINES` 与非 MSAA 后备缓冲;同一位置重复 `setCamera` 不续期。停稳后补全质量帧、刷新通道宽度并恢复符号预烤。若聚合重排被推迟但这一轮没有轻量几何,计时器仍会补帧完成重算。
429
+ - **核心文件**:`src/gis-view/engine.js`、`docs/steps/selftest-perf.html`、`docs/manual/20-性能与大数据.md`、`docs/internals/16-03-优化与取舍-下.md`、`docs/internals/18-性能实测与WebGPU评估.md`、根 `README.md`、`src/gis-view/README.md`、`docs/AI对话汇总.md`。
430
+ - **实测**:无头 Chrome + ANGLE / AMD D3D11,1280×800,10 万点 / 5 万线,`quality: 'high'`;连续缩放 p50 **67.9 → 22.5 ms**、p95 **76.2 → 25.1 ms**、avg **60.77 → 20.57 ms**。平移 p50 17.1 → 17.2 ms(帧节拍波动),`refresh()` 56.2 → 53.7 ms;终态 `thinLines=false`、`noMsaa=false`。收益口径是消除整份重排 / 上传与运动期 MSAA 细线光栅化,不是把单帧渲染降到 1 ms。
431
+ - **验证**:`node --check`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(264 个实例成员 / 96 个 options / 250 个数据字段 / 43 个事件 / 17 个功能开关)、四页自测 `full 94` / `demos 365` / `docs 134` / `perf 181` 共 **774** 条通过 / 0 失败、`npm run build` 与 `git diff --check` 全部通过。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-23-增量修改txt继续深度优化-gis-view-整体性能)。
432
+
433
+ ### 2026-10-26 增量(「修改.txt」:修复放大缩小与移动时的短暂模糊)
434
+
435
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录最新的 `修改.txt`:修改 gis-view 组件;修改之后放大缩小 / 移动会模糊一下。根因是上一轮运动帧按 rAF 间隔降到 0.65× / 0.5× 后线性放大贴回,本轮已移除。
436
+
437
+ - **撤回降采样**:`engine.js` 删除 `MOTION_RESOLUTION_STEPS` / `sampleMotionResolution()` 与 `drawWidth` / `drawHeight` / `framePixelRatio` 一套像素尺度,运动帧 viewport 始终是画布全尺寸;`shaders.js` 的 `u_offsetScale` / `u_uvOffset` / `u_uvScale` 和 `create.js` 的 `exportFrame` 配套标记同步撤回至 HEAD。交互、滚轮、程序化相机与尺寸倍率变化都保持全分辨率,停稳 160 ms 后恢复 MSAA 静态帧。
438
+ - **保留轻量窗口**:相机运动窗口、细线 `gl.LINES`、非 MSAA 后备缓冲不变;`scaleMotion` 仍只在尺寸倍率连续变化时暂停符号重烤与预烤,停手后对齐当前档,不改变分辨率。相机与尺寸窗口重叠时由最后停下的一方补质量帧。
439
+ - **回归与实测**:`selftest-perf.html` 新增 1 条断言,拉长运动帧间隔并连续改相机,所有 `gl.viewport` 仍必须等于画布全尺寸。无头 Chrome + ANGLE / AMD D3D11、1280×800、三次取中位:10 万点 / 5 万线缩放 p50 **23.2 ms** / p95 **28.6 ms**、平移 **16.7 / 18.2 ms**;10 万点 / 10 万线缩放 **42.6 / 46.3 ms**、平移 **16.7 / 24.5 ms**。
440
+ - **核心文件**:`src/gis-view/engine.js`、`docs/steps/selftest-perf.html`、`docs/steps/README.md`、`docs/manual/20-性能与大数据.md`、根 `README.md`、`src/gis-view/README.md` 与本轮 aimd。
441
+ - **验证**:`node --check`(engine / create / shaders)、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs`(264 个实例成员未变)、`npm run check:docs`(264 个实例成员 / 250 个方法 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、九个定向自测页 **1504** 条通过 / 0 失败(features / actions / data / decoration / select / labels / effects / docs / full)、`npm run build` 与 `git diff --check` 全部通过。`selftest-perf` **181 / 1**(共 182 条;新增相机全分辨率断言通过,标签布局读数在 HEAD 基线已复现);`selftest-scale` 117 / 2(onFilter 计数,筛选链路未改)。公开 API 计数未变。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-26-增量修改txt修复放大缩小与移动时的短暂模糊)。
442
+ ### 2026-10-26 增量(「修改.txt」:线片段着色管线专用化)
443
+
444
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录最新的 `修改.txt`:修改 gis-view 组件;这样修改之后性能提升了吗,感觉没提升多少。
445
+
446
+ - **判断**:CPU profile 约 **98.6% idle**、`render` 约 **0.1%**,说明上一轮主要省的是场景重排 / 上传 / 相机调度,剩余在 GPU 片元。用户感觉提升有限,还因为细线快路径的真实门槛是最大半宽 ≤ 0.6 设备像素且无虚线(dpr = 1 约 1.2px 线宽),2px 线不会命中 `gl.LINES`。
447
+ - **改动**:`shaders.js` 新增 `THIN_LINE_FRAGMENT_SHADER`(细线只输出颜色)与 `SOLID_LINE_FRAGMENT_SHADER`(全实线、非光晕四边形跳过虚线取模 / `smoothstep` / 光晕分支,保留非 MSAA 覆盖率);`engine.js` 的 `drawLineBatch` 按批切换,`refreshLineThinLedger` 同时提供是否全实线;两个程序属性槽位不一致时自动回退通用片元。端点圆头、全分辨率与停稳后的 MSAA 静态帧不变。
448
+ - **实测**:10 万点 / 5 万线缩放 p50 **23.2 → 16.7 ms**、p95 **28.6 → 16.8 ~ 16.9 ms**;10 万点 / 10 万线缩放三次中位 p50 **42.6 → 18.0 ms**、p95 **46.3 → 21.4 ms**;2px 实线的 10 万线只走四边形轻量片元时约 **82.4 → 63.9 ms**。16 ~ 18 ms 已接近 60Hz 帧节拍,剩余成本是 GPU 填充率。
449
+ - **核心文件**:`src/gis-view/shaders.js`、`src/gis-view/engine.js`、`docs/steps/selftest-perf.html`、`docs/steps/README.md`、`docs/manual/03-配置项.md`、`docs/manual/05-画质与批量更新.md`、`docs/manual/20-性能与大数据.md`、`docs/manual/21-说明与常见问题.md`、`docs/internals/18-性能实测与WebGPU评估.md`、根 `README.md`、`src/gis-view/README.md` 与本轮 aimd。
450
+ - **验证**:`node --check`(engine / shaders)、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs`(264 个实例成员未变)、`npm run check:docs`(264 个实例成员 / 250 个方法 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、九个定向自测页 **1504** 条通过 / 0 失败、`npm run build` 与 `git diff --check` 全部通过。`selftest-perf` **182 / 1**(共 183 条;新增像素对拍通过,标签读数失败在 HEAD 已复现);`selftest-scale` 117 / 2(既有 onFilter 计数失败)。公开 API 计数未变。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-26-增量修改txt继续优化-gis-view回答感觉没提升多少)。
451
+
452
+ ### 2026-10-26 增量(「修改.txt」:选中 / 取消选中入口速查)
453
+
454
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录最新的 `修改.txt`:修改 gis-view 组件;现在组件如何选中和取消选中。
455
+
456
+ - **结论**:公开入口只有 `selectFeatures(refs, options?)` 与 `deselect(refs?)`,没有 `select()`;现有鼠标、键盘、框选与接口路径已经覆盖选中 / 取消,不需要新增重复 API。
457
+ - **文档**:`docs/manual/08-选中与拐点.md` 顶部新增「选中与取消选中速查」,并修正当前正文、demo 与自测注释中把 `select()` 当现存接口的说法。
458
+ - **核心文件**:`docs/manual/08-选中与拐点.md`、`docs/demos/StyleDemo.vue`、`docs/demos/gis-view-demo/useGisViewDemo.js`、`docs/steps/selftest-features.html`、`docs/AI对话汇总.md` 与本章。
459
+ - **验证**:`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(264 个实例成员 / 250 个方法 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、`npx tsc --noEmit`、`npm run build`、`selftest-select.html` **69** 条通过 / 0 失败,以及 `git diff --check` 全部通过;公开 API 计数未变。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-26-增量修改txt选中--取消选中入口速查)。
460
+
461
+ ### 2026-10-26 增量(「修改.txt」:按坐标模拟单击选中 selectAt)
462
+
463
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、我说的选中是手动触发单击选中,比如手动选中某个节点,某个通道,线路等,组件有这个方法吗」。
464
+
465
+ - **结论**:已有 `selectFeatures(refs, options?)` 可按 id / 下标 / `{ type, index }` 手动选中节点、线路、通道;`pickAt()` 只读命中、不改选中。为「拿鼠标坐标当一次单击」,本轮新增 `selectAt(clientX, clientY)`,没有恢复已删除的 `select()`。
466
+ - **行为**:`selectAt` 按真实单击相同的 `pick()` 顺序命中节点 / 线路并立即调用 `applySelection`;通道回线按 `channelSelectMode` 摊成一条 / 一组 / 整条通道。空白或聚合圆返回 `null`,不清空原选中;不等待 `selectTrigger: 'dblClick'`,也不派发 `onClick` / `onDblClick`,但 `onSelect` / `onSelectChange` 正常发,`cause` 为 `api`。
467
+ - **设计取舍**:这是接口入口,固定替换当前选中集;追加 / 取反仍用 `selectFeatures(refs, { mode: 'add' | 'toggle' })`。要清空仍用 `deselect()` / `selectFeatures(null)`。返回值为 `GisViewSelectPayload | null`。
468
+ - **核心文件**:`src/gis-view/create.js`、`src/gis-view/engine.js`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/manual/08-选中与拐点.md`、`docs/manual/09-通道与多回线.md`、`docs/manual/16-实例方法总表.md`、`docs/manual/17-参数速查.md`、`docs/manual/18-逐方法示例.md`、`README.md`、`docs/GisView.md`、`docs/steps/README.md`、`docs/steps/selftest-docs.html`、`docs/steps/selftest-demos.html`、`docs/demos/api-index.js`(生成物)与本轮 aimd。
469
+ - **验证**:`node --check src/gis-view/create.js`、`node --check src/gis-view/engine.js`、`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(265 个实例成员 = 260 个方法 + 5 个属性 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关全部命中)、`selftest-select.html` **73** 条、`selftest-docs.html` **134** 条、`selftest-demos.html` **365** 条,三页共 **572** 条通过 / 0 失败;`npm run build` 与 `git diff --check` 全部通过。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-26-增量修改txt按坐标模拟单击选中-selectat)。
470
+
471
+ ### 2026-10-26 增量(「修改.txt」:选中接口的回调口径)
472
+
473
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、selectFeatures为什么不会触发onSelect事件,也会将内容标记为选中的样式,不是选中吗,新增的selectAt和selectFeatures有什么区别」。
474
+
475
+ - **结论**:`selectFeatures()` / `deselect()` 确实会改同一份选中 state,选中样式、`isSelected()` 与 `getSelectionData()` 立即更新;它们属于按引用改选中集的接口路径,只发 `onSelectChange`(`cause: 'api'`),不补发点击 / 框选语义的 `onSelect`。`selectAt()` 是明确的「按坐标模拟单击」,因此同时发 `onSelect` 与 `onSelectChange`,但不发 `onClick` / `onDblClick`。
476
+ - **事件口径**:`onSelectChange` 覆盖点击、框选、键盘清空与接口调用,并且只在选中集真的变化时发,适合统一同步面板;`onSelect` 覆盖点击 / 框选语义(真实点击 / Ctrl 多选 / 框选(含 `selectInBox()`)/ 空白清空)与模拟单击的 `selectAt()`。此前手册 17 把 `selectFeatures` 写成也会发 `onSelect`,与实际行为不一致,已改正。
477
+ - **两个方法的区别**:`selectAt(clientX, clientY)` 吃坐标、走与真实单击相同的 `pick()`,通道回线按 `channelSelectMode` 摊开,空白返回 `null` 且不动原选中,返回本次载荷;`selectFeatures(refs, options)` 吃 id / 下标 / 通道 / 分组引用,`mode` 支持 replace / add / toggle / remove,`null` / 空数组清空,返回实例本身。两者最终改的是同一份选中 state,样式与读数没有区别。
478
+ - **核心文件**:`docs/manual/04-事件与回调.md`、`docs/manual/08-选中与拐点.md`、`docs/manual/16-实例方法总表.md`、`docs/manual/17-参数速查.md`、`docs/manual/18-逐方法示例.md`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/steps/README.md`、`docs/demos/api-index.js`(生成物)与本轮 aimd。
479
+ - **验证**:`selftest-select.html` 新增 2 条事件口径断言(73 → **75**),三页定向自测 **574** 条通过 / 0 失败(select 75 / docs 134 / demos 365);`npx tsc --noEmit`、`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(265 个实例成员 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关全部命中)、`npm run build` 与 `git diff --check` 全部通过。运行时代码与公开成员计数未变。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-26-增量修改txt选中接口的回调口径)。
480
+
481
+ ### 2026-10-26 增量(「修改.txt」:统一选中回调与鼠标分组载荷)
482
+
483
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、选中指的不就是点击触发select的选中吗,什么叫真正的选中 2、那先selectFeatures有什么用,我想直接通过方法选中一个节点如何操作,用selectAt传坐标,不多余吗?3、鼠标事件里的groups怎么没」。
484
+
485
+ - **选中回调统一**:点击、框选与 `selectFeatures()` / `deselect()` / `selectAt()` 共用同一份选中状态,实际改变选中集时都从组件级 `handleSelectPayload(payload, detail)` 发出 `onSelect` 与 `onSelectChange`;接口来源 `cause: 'api'`,原样重设同一选中集只同步镜像与读数、不重复发回调。
486
+ - **selectAt 与 selectFeatures 的分工**:`selectAt(clientX, clientY)` 只用于手上只有客户端坐标、需要像普通单击一样命中的场景;它走同一套 `pick()`,立即生效但不派发 `onClick` / `onDblClick`。已知节点 / 线路 id 时直接 `selectFeatures('id')` 或传引用,不必先求坐标;两套入口落到同一份选中 state,样式与读数没有区别。
487
+ - **鼠标分组载荷**:通道回线最终命中时,拾取结果补 `groups`(同一通道全部分组,按声明顺序,每项带 `groupIndex` / `lineIds` / `path` 与成员 / 拐点读数);`group` 仍是当前命中的那一组。`pickAt`、`hoverAt` / `onHover`、`onPointerDown` / `onPointerUp`、`onClick` / `onDblClick`、`onContextMenu` 都带这两个字段,且全部分组只在最终命中后构建,避免悬停扫描反复复制成员数组。
488
+ - **核心文件**:`src/gis-view/create.js`、`src/gis-view/engine.js`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/manual/04-事件与回调.md`、`07-指针与要素编辑.md`、`08-选中与拐点.md`、`09-通道与多回线.md`、`16-实例方法总表.md`、`17-参数速查.md`、`18-逐方法示例.md`、`src/gis-view/README.md`、`docs/steps/README.md`、`docs/demos/api-index.js`(生成物)与本轮 aimd。
489
+ - **验证**:`node --check src/gis-view/create.js`、`node --check src/gis-view/engine.js`、`npx tsc --noEmit`;`selftest-select.html` **79** 条、`selftest-features.html` **672** 条、`selftest-docs.html` **134** 条、`selftest-demos.html` **365** 条,四页共 **1250** 条通过 / 0 失败;`node docs/steps/gen-api-index.mjs --check`、`npm run check:docs`(265 个实例成员 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关全部命中)、`npm run build` 与 `git diff --check` 全部通过(后者仅有 CRLF 提示)。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-26-增量修改txt统一选中回调与鼠标分组载荷)。
490
+ - **历史口径**:本节之前的「选中接口的回调口径」保留上一轮“接口只发 onSelectChange”的记录;本轮已统一为实际变化时同时发 `onSelect` 与 `onSelectChange`,以本节为准。
491
+
492
+ ### 2026-10-26 增量(「修改.txt」:选中事件分工、通道上下文与派发顺序)
493
+
494
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、onSelectChange和onSelect有必要同时存在吗,各自的意义是什么 2、为什么这两个事件里都拿不到通道的信息,group和groups都没有,隐藏这两个参数的意义是什么 3、为什么控制台顺序onSelectChange>onSelect>onClick」。
495
+
496
+ - **分工结论**:保留两个事件。`onSelect` 是本次选中操作回调(真实单击 / Ctrl / `multiSelect` 取反 / 框选 / `selectAt()` 每次成功操作都发,清空或取反取没了时载荷 `null`;`selectFeatures` / `deselect` / `focusOn({ select })` 只在集合实际变化时发),`onSelectChange` 是集合状态回调(所有来源都只在实际变化时发,带 `ids` / `count` / 类型计数 / `cause` / `action`)。
497
+ - **通道载荷**:两条回调都带同一份通道上下文。单通道时顶层平铺 `channelId` / `channelIndex` / `channel` / `groupId` / `groupIndex` / `group` / `groups`;多通道时只给 `channelContexts`(每项另有本次选中的 `lineIndexes` / `lineIds`)。`items[]` 里的通道回线也补 `group` / `groups`。真实单击与 `selectAt` 的当前组是命中的组;框选与按引用接口取选中集里第一条通道回线所在的组;`groups` 始终是整条通道的全部分组。
498
+ - **顺序修复**:旧顺序 `onSelectChange → onSelect → onClick` 已改为普通单击 `onClick → onSelect → onSelectChange`;Ctrl / `multiSelect` 取反、框选与接口选中是 `onSelect → onSelectChange`;双击模式第二下最后再补 `onDblClick`。`deferredSelection` 只延后点击路径的选中回调,选中镜像与标签仍先同步,回调内读 `getSelectionData()` 已是最新。
499
+ - **根因修复**:分组通道的 `channelIndex` 原来误取分组状态序号 `state.order`,现统一为通道在 `channels` 声明数组里的下标 `state.channelIndex`,覆盖拾取 / 悬停 / `describe` / `listLines` / 级联 / `getLineChannel` 六处。
500
+ - **核心文件**:`src/gis-view/engine.js`、`src/gis-view/create.js`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/manual/04-事件与回调.md`、`07-指针与要素编辑.md`、`08-选中与拐点.md`、`09-通道与多回线.md`、`16-实例方法总表.md`、`17-参数速查.md`、`18-逐方法示例.md`、`src/gis-view/README.md`、`docs/steps/README.md`、`docs/demos/gis-view-demo/useGisViewDemo.js`、`docs/demos/api-index.js`(生成物)与本轮 aimd。
501
+ - **验证**:`node --check src/gis-view/engine.js`、`node --check src/gis-view/create.js`、`npx tsc --noEmit`;四页定向自测 **1254** 条通过 / 0 失败(select 83 / features 672 / docs 134 / demos 365);`npm run build` 通过;`node docs/steps/gen-api-index.mjs --check` PASS(索引最新)、`npm run check:docs` 全 PASS(265 个实例成员 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、`git diff --check` 通过(仅有 CRLF 提示)。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-26-增量修改txt选中事件分工通道上下文与派发顺序)。
502
+
503
+ ### 2026-10-26 增量(「修改.txt」:选中载荷去重与通道索引缓存)
504
+
505
+ 本轮仍是现有会话的继续增量,没有独立会话归档,因此不改上表的 39 条统计。需求原话来自仓库根目录最新的 `修改.txt`:「修改gis-view组件 1、本次修改会影响组件性能吗,比如事件参数添加group和groups,selectFeatures触发onSelect等,如果影响性能,如何优化 2、现在onSelect和onSelectChange参数都有通道信息,下面的items里面也有通道信息,是不是有些多余, 3、继续检查组件,有没有冗余代码,继续提高组件性能」。
506
+
507
+ - **性能结论**:选中事件不进入逐帧渲染路径;两条事件共享同一批 `items`、分组对象与数组引用,不深拷贝。`selectFeatures()` 只在集合实际变化时发事件,同内容、同顺序的重设会在 `selectionKeys()`、引擎刷新与 `describeSelection()` 之前直接返回;`selectAt()` 每次成功命中发 `onSelect` 是既定语义。新增开销只随本次选中的通道 / 分组数增长。
508
+ - **载荷去重**:单通道只摊平顶层 `channelId` / `channelIndex` / `channel` / `groupId` / `groupIndex` / `group` / `groups`,不再生成 `channelContexts`;多通道才用 `channelContexts`。`items[]` 的通道回线只保留 `channelId` / `channelIndex` / `groupId` / `groupIndex` 标量,不重复挂 `group` / `groups`。指针事件仍保留自己的 `group` / `groups`。上一节逐条补充的口径已撤回。
509
+ - **索引与冗余**:`resolveChannels()` 同步维护 `channelGroupStatesByIndex`,状态上记录 `groupOrdinal`;`channelSiblings()` / `channelGroupOrdinal()` 变 O(1),分组读数从 O(组数 × 通道数) 降为 O(组数)。当前组改用组序号直取,删掉 `findIndex`、`state.order` 和建通道时重复的 `channels.filter(...)`。单通道选中不再构造随后丢弃的 `lineIndexes` / `lineIds`,多通道才在 `channelContexts` 里按选中顺序补一次。
510
+ - **核心文件**:`src/gis-view/engine.js`、`src/gis-view/create.js`、`src/gis-view/types.d.ts`、`docs/steps/selftest-select.html`、`docs/steps/README.md`、`docs/manual/04-事件与回调.md`、`07-指针与要素编辑.md`、`09-通道与多回线.md`、`20-性能与大数据.md`、`src/gis-view/README.md` 与本轮 aimd。
511
+ - **验证**:`node --check src/gis-view/engine.js`、`node --check src/gis-view/create.js`、`npx tsc --noEmit`、`npm run build` 通过;五页定向自测 **1438** 条通过 / 0 失败(select 84 / features 672 / docs 134 / demos 365 / perf 183);`node docs/steps/gen-api-index.mjs --check` PASS(索引最新)、`npm run check:docs` 全 PASS(265 个实例成员 / 250 个数据字段 / 96 个 options / 43 个事件 / 17 个功能开关)、`git diff --check` 通过(仅有 CRLF 提示)。详细口径见 [AI 对话汇总的本轮增量](../AI对话汇总.md#2026-10-26-增量修改txt选中载荷去重与通道索引缓存)。
512
+
396
513
  ## 六、怎么自己复查
397
514
 
398
515
  会话文件是逐行 JSON,不用整份读 —— 只挑两种行解析就够:`session_meta` 行里的 `payload.cwd` 认项目,`payload.type === 'user_message'` 行里的 `payload.message` 是用户原话:
@@ -477,8 +477,8 @@ onMounted(() => {
477
477
  pushLog('labelChange', describeChange(payload))
478
478
  syncLabelStates()
479
479
  }],
480
- // 接口选中(select / clearSelection)不发 onSelect,只发 selectChange:两条路都接上
481
- ['selectChange', () => syncSelection()]
480
+ // 选中镜像统一走 selectChange:覆盖点击与接口来源,且重复设同一份选中集不会重复通知
481
+ ['selectChange', () => syncSelection()]
482
482
  ].map(([name, handler]) => view.on(name, handler))
483
483
  window.addEventListener('keydown', onKeyDown)
484
484
  window.addEventListener('pointerdown', onGlobalPointerDown)
@@ -27,7 +27,9 @@ const channelCircuits = ref(true)
27
27
  const zoomMode = ref('manual')
28
28
  const followZoomScale = ref(1)
29
29
  const dragAction = ref('pan')
30
+ const extentFollowContent = ref(false)
30
31
  const extentFollowCamera = ref(false)
32
+ const offset = ref([0, 0])
31
33
  const maxPixelRatio = ref('2')
32
34
  const gridPlacement = ref('below-data')
33
35
  const customLayers = ref(true)
@@ -36,6 +38,7 @@ const zoomReadout = ref('—')
36
38
  const layerReadout = ref('—')
37
39
  const eventText = ref('—')
38
40
  const interactionReadout = ref('还没有手势')
41
+ const offsetReadout = ref('整图位移 0, 0')
39
42
 
40
43
  const SIZES = {
41
44
  small: [148, 104],
@@ -45,6 +48,8 @@ const SIZES = {
45
48
 
46
49
  const dataset = buildDemoData({ count: 64, channels: 3 })
47
50
  let view = null
51
+ let minimapHandle = null
52
+ let wholeMapDrag = null
48
53
  let layerPaints = { grid: 0, compass: 0, gridStage: '—' }
49
54
 
50
55
  const drawGrid = ({ context, width, height, stage }) => {
@@ -130,7 +135,9 @@ const minimapOptions = () => {
130
135
  margin: 12,
131
136
  padding: 0.05,
132
137
  extent: extent.value,
138
+ extentFollowContent: extentFollowContent.value,
133
139
  extentFollowCamera: extentFollowCamera.value,
140
+ offset: [...offset.value],
134
141
  background: 'inherit',
135
142
  backgroundStyle: 'rgba(255, 255, 255, 0.94)',
136
143
  borderColor: '#c7d8eb',
@@ -179,6 +186,7 @@ const refreshReadout = () => {
179
186
  if (view) {
180
187
  zoomReadout.value = `小图倍数 ${view.getMinimapZoom().toFixed(2)} · ${zoomModeLabel.value}`
181
188
  }
189
+ offsetReadout.value = `整图位移 ${Math.round(offset.value[0])}, ${Math.round(offset.value[1])}`
182
190
  }
183
191
 
184
192
  const zoomModeLabel = computed(() => zoomMode.value === 'manual'
@@ -192,6 +200,9 @@ const applyMinimap = () => {
192
200
  view.updateOptions({ minimap: minimapOptions() })
193
201
  eventText.value = `updateOptions:${enabled.value ? '已更新鹰眼图配置' : '已卸载鹰眼图'}`
194
202
  requestAnimationFrame(refreshReadout)
203
+ if (enabled.value) {
204
+ requestAnimationFrame(installMinimapHandle)
205
+ }
195
206
  }
196
207
 
197
208
  const resetZoom = () => {
@@ -220,6 +231,96 @@ const showDragDemo = (action) => {
220
231
  : '拖拽演示:已切到手动缩放,在小图空白处拖动平移小图内容'
221
232
  }
222
233
 
234
+ /** 把 offset 写回实例:整图拖动只改角落位移,不重画小图,也不碰主图相机 */
235
+ const setWholeMapOffset = (next) => {
236
+ const x = Number(next?.[0])
237
+ const y = Number(next?.[1])
238
+ offset.value = [Number.isFinite(x) ? x : 0, Number.isFinite(y) ? y : 0]
239
+ const options = view?.getOptions().minimap
240
+ if (options) {
241
+ view.updateOptions({ minimap: { ...options, offset: [...offset.value] } })
242
+ }
243
+ offsetReadout.value = `整图位移 ${Math.round(offset.value[0])}, ${Math.round(offset.value[1])}`
244
+ }
245
+
246
+ const resetWholeMapOffset = () => {
247
+ setWholeMapOffset([0, 0])
248
+ eventText.value = '整图拖动:位置已复位到当前角落'
249
+ }
250
+
251
+ const beginWholeMapDrag = (event) => {
252
+ if (!view || event.button !== 0) {
253
+ return
254
+ }
255
+ event.preventDefault()
256
+ event.stopPropagation()
257
+ const current = view.getOptions().minimap?.offset
258
+ const origin = Array.isArray(current) ? [Number(current[0]) || 0, Number(current[1]) || 0] : [...offset.value]
259
+ wholeMapDrag = { pointerId: event.pointerId, clientX: event.clientX, clientY: event.clientY, origin }
260
+ minimapHandle?.classList.add('is-dragging')
261
+ eventText.value = '整图拖动:按住顶部手柄移动,松手停在当前角落'
262
+ }
263
+
264
+ const moveWholeMapDrag = (event) => {
265
+ if (!wholeMapDrag || event.pointerId !== wholeMapDrag.pointerId) {
266
+ return
267
+ }
268
+ event.preventDefault()
269
+ event.stopPropagation()
270
+ setWholeMapOffset([
271
+ wholeMapDrag.origin[0] + event.clientX - wholeMapDrag.clientX,
272
+ wholeMapDrag.origin[1] + event.clientY - wholeMapDrag.clientY
273
+ ])
274
+ }
275
+
276
+ const endWholeMapDrag = (event) => {
277
+ if (!wholeMapDrag || (event?.pointerId !== undefined && event.pointerId !== wholeMapDrag.pointerId)) {
278
+ return
279
+ }
280
+ event?.preventDefault()
281
+ event?.stopPropagation()
282
+ minimapHandle?.classList.remove('is-dragging')
283
+ wholeMapDrag = null
284
+ eventText.value = `整图拖动:已停在位移 ${Math.round(offset.value[0])}, ${Math.round(offset.value[1])}`
285
+ }
286
+
287
+ const nudgeWholeMap = (event) => {
288
+ const step = event.shiftKey ? 10 : 2
289
+ const delta = event.key === 'ArrowLeft' ? [-step, 0]
290
+ : event.key === 'ArrowRight' ? [step, 0]
291
+ : event.key === 'ArrowUp' ? [0, -step]
292
+ : event.key === 'ArrowDown' ? [0, step] : null
293
+ if (!delta) {
294
+ return
295
+ }
296
+ event.preventDefault()
297
+ event.stopPropagation()
298
+ setWholeMapOffset([offset.value[0] + delta[0], offset.value[1] + delta[1]])
299
+ eventText.value = '整图拖动:方向键也能微调整图位置(按住 Shift 每次 10px)'
300
+ }
301
+
302
+ /** 给库创建的根节点挂一个独立的拖动手柄:手柄自己 stopPropagation,不会触发小图框选 / 平移手势 */
303
+ const installMinimapHandle = () => {
304
+ if (!view || minimapHandle) {
305
+ return
306
+ }
307
+ const root = view.getMinimapElement()
308
+ if (!root) {
309
+ return
310
+ }
311
+ minimapHandle = document.createElement('div')
312
+ minimapHandle.className = 'demo-minimap-drag-handle'
313
+ minimapHandle.dataset.demo = 'minimap-drag-handle'
314
+ minimapHandle.setAttribute('role', 'button')
315
+ minimapHandle.setAttribute('tabindex', '0')
316
+ minimapHandle.setAttribute('aria-label', '拖动整张鹰眼图')
317
+ minimapHandle.title = '按住拖动整张鹰眼图;方向键可微调'
318
+ minimapHandle.textContent = '拖动整图'
319
+ minimapHandle.addEventListener('pointerdown', beginWholeMapDrag)
320
+ minimapHandle.addEventListener('keydown', nudgeWholeMap)
321
+ root.append(minimapHandle)
322
+ }
323
+
223
324
  const refreshLayerReadout = () => {
224
325
  layerReadout.value = `网格层 ${layerPaints.grid} 次(${layerPaints.gridStage}) · 指北针层 ${layerPaints.compass} 次`
225
326
  refreshReadout()
@@ -252,9 +353,18 @@ onMounted(() => {
252
353
  eventText.value = `warning:${text}`
253
354
  }
254
355
  })
356
+ installMinimapHandle()
357
+ window.addEventListener('pointermove', moveWholeMapDrag, true)
358
+ window.addEventListener('pointerup', endWholeMapDrag, true)
359
+ window.addEventListener('pointercancel', endWholeMapDrag, true)
255
360
  })
256
361
 
257
362
  onBeforeUnmount(() => {
363
+ window.removeEventListener('pointermove', moveWholeMapDrag, true)
364
+ window.removeEventListener('pointerup', endWholeMapDrag, true)
365
+ window.removeEventListener('pointercancel', endWholeMapDrag, true)
366
+ minimapHandle?.remove()
367
+ minimapHandle = null
258
368
  view?.destroy()
259
369
  view = null
260
370
  })
@@ -312,10 +422,16 @@ const sizeSummary = computed(() => `${SIZES[size.value]?.[0] ?? 0}×${SIZES[size
312
422
  </select>
313
423
  </label>
314
424
  </div>
425
+ <div class="dv-row">
426
+ <label><input v-model="extentFollowContent" type="checkbox" data-demo="minimap-extent-content" @change="applyMinimap" /> 自动范围随内容更新(默认关)</label>
427
+ </div>
315
428
  <div class="dv-row">
316
429
  <label><input v-model="extentFollowCamera" type="checkbox" data-demo="minimap-extent-follow" @change="applyMinimap" /> 自动范围随相机收窄(默认关)</label>
317
430
  </div>
318
- <p class="dv-hint"><code>maxPixelRatio</code> 越大越清晰,也越吃填充率;高分屏按机器档位调到 2 通常就够。</p>
431
+ <div class="dv-row">
432
+ <button type="button" class="dv-button" data-demo="minimap-offset-reset" @click="resetWholeMapOffset">整图位置复位</button>
433
+ </div>
434
+ <p class="dv-hint"><code>extentFollowContent</code> 默认关闭:筛选或删除外围要素不会改小图范围。按住小图顶部的「拖动整图」手柄可移动整张鹰眼图,方向键可微调;<code>offset</code> 会随其他控件一起保留。<code>maxPixelRatio</code> 越大越清晰,也越吃填充率;高分屏按机器档位调到 2 通常就够。</p>
319
435
  </div>
320
436
  <div class="dv-group">
321
437
  <h3>规则与交互</h3>
@@ -396,9 +512,46 @@ const sizeSummary = computed(() => `${SIZES[size.value]?.[0] ?? 0}×${SIZES[size
396
512
  <div class="dv-group">
397
513
  <h3>读数</h3>
398
514
  <p class="dv-readout" data-demo="minimap-readout">{{ readout }}</p>
515
+ <p class="dv-readout" data-demo="minimap-offset-readout">{{ offsetReadout }}</p>
399
516
  <p class="dv-readout" data-demo="minimap-event" aria-live="polite">{{ eventText }}</p>
400
517
  <p class="dv-readout" data-demo="minimap-interaction" aria-live="polite">{{ interactionReadout }}</p>
401
518
  </div>
402
519
  </aside>
403
520
  </section>
404
521
  </template>
522
+
523
+ <style scoped>
524
+ :deep(.demo-minimap-drag-handle) {
525
+ position: absolute;
526
+ z-index: 4;
527
+ top: 4px;
528
+ left: 50%;
529
+ box-sizing: border-box;
530
+ min-width: 68px;
531
+ padding: 2px 9px;
532
+ transform: translateX(-50%);
533
+ border: 1px solid rgba(54, 120, 191, 0.38);
534
+ border-radius: 999px;
535
+ background: rgba(255, 255, 255, 0.9);
536
+ box-shadow: 0 2px 7px rgba(31, 62, 105, 0.14);
537
+ color: #2f5e94;
538
+ font-size: 10px;
539
+ line-height: 16px;
540
+ text-align: center;
541
+ cursor: grab;
542
+ user-select: none;
543
+ touch-action: none;
544
+ }
545
+
546
+ :deep(.demo-minimap-drag-handle:hover),
547
+ :deep(.demo-minimap-drag-handle:focus-visible) {
548
+ border-color: #3678bf;
549
+ background: #fff;
550
+ outline: none;
551
+ }
552
+
553
+ :deep(.demo-minimap-drag-handle.is-dragging) {
554
+ cursor: grabbing;
555
+ background: #eaf2fc;
556
+ }
557
+ </style>