gis-view-webgl2 0.1.11 → 0.1.13
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 +0 -24
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +524 -33
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +156 -8
- package/docs/GisView.md +6 -6
- 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 +157 -7
- package/docs/demos/BackgroundDemo.vue +600 -0
- package/docs/demos/ChannelDemo.vue +145 -3
- package/docs/demos/StyleDemo.vue +280 -69
- package/docs/demos/api-index.js +35 -19
- package/docs/demos/demos.js +26 -18
- package/docs/demos/gis-view-demo/demo.css +76 -0
- package/docs/demos/gis-view-demo/panels/StylePanels.vue +24 -8
- package/docs/demos/gis-view-demo/panels/ViewPanels.vue +33 -3
- package/docs/demos/gis-view-demo/useGisViewDemo.js +236 -22
- package/docs/format/02-/350/212/202/347/202/271/344/270/216/347/272/277/350/267/257.md +1 -1
- 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 +3 -3
- 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 +13 -11
- 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 +1 -1
- package/docs/index.html +2 -2
- 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 +3 -3
- package/docs/internals/13-/351/271/260/347/234/274/345/233/276.md +1 -1
- package/docs/internals/14-/350/203/214/346/231/257/345/261/202.md +1 -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 +199 -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 +1 -1
- package/docs/internals/18-/346/200/247/350/203/275/345/256/236/346/265/213/344/270/216WebGPU/350/257/204/344/274/260.md +32 -0
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +1 -1
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +217 -26
- 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 +2 -2
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +37 -4
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +58 -1
- package/docs/manual/11-/346/240/267/345/274/217/344/270/216/350/247/204/345/210/231.md +59 -8
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +1 -1
- package/docs/manual/13-/345/212/250/346/225/210/344/270/216/345/233/276/345/205/203.md +220 -2
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +77 -16
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +30 -17
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +11 -4
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +17 -8
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +128 -4
- 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 +2 -2
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +13 -1
- 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 +4 -1
- package/docs/steps/index.html +1 -1
- package/docs/steps/selftest-demo.html +2 -2
- package/docs/steps/selftest-demos.html +626 -11
- package/docs/steps/selftest-docs.html +8 -4
- package/docs/steps/selftest-features.html +1428 -2
- package/docs/steps/selftest-perf.html +296 -25
- package/docs/steps/selftest-scale.html +61 -3
- package/docs/steps/selftest-select.html +303 -1
- package/docs/steps/step-06-style.js +3 -2
- package/package.json +2 -2
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 点配置
|
|
6
6
|
|
|
7
|
-
`GisViewPoint` 支持 `id`、`x`、`y`、`label` / `name`、`color`、`size`、`symbol`、`strokeColor`、`strokeWidth`、`opacity`、`shadow`、`visible`、`labelVisible` / `labelPosition` / `labelOffset`、`tooltip`、`clickable`、`draggable`、`hoverStyle`、`selectStyle`、`zIndex`,以及任意业务字段。样式优先级是 **状态样式 > 点自身字段 >
|
|
7
|
+
`GisViewPoint` 支持 `id`、`x`、`y`、`label` / `name`、`color`、`size`、`symbol`、`strokeColor`、`strokeWidth`、`opacity`、`shadow`、`visible`、`labelVisible` / `labelPosition` / `labelOffset`、`tooltip`、`clickable`、`draggable`、`hoverStyle`、`selectStyle`、`zIndex`,以及任意业务字段。样式优先级是 **状态样式 > 热力图 > `override` 规则 > 点自身字段 > 普通规则 > `pointStyle` > 内置默认**。普通 `pointRules` 只补空位,所以规则永远高于 `pointStyle`、默认低于点自身字段;某条规则写 `override: true` 后,它真正写了的字段压过点自身字段。`zIndex` 决定压盖关系:数值大的画在上面,同值的连成一层、层内按数据顺序,缺省是 0;它不参与状态样式,所以悬停 / 选中不会把节点抬到别的层上去。`color` 是节点的填充色、`strokeColor` 是外圈那一环描边的颜色(宽度由 `strokeWidth` 给):配色想只落在描边上(白底 + 彩色轮廓),就把 `color` 写成白色、把颜色与宽度写进 `strokeColor` / `strokeWidth`。`label` / `name` 既可以当业务字段,也是标签的默认文本来源(见「节点标签」);`visible: false` 的点不渲染,绑在它身上的线路端点退回自身坐标。 状态样式里除了 `selectStyle` / `hoverStyle`,还有 `multiSelectStyle`(Ctrl + 点击累加多选)与 `boxSelectStyle`(Shift + 拖动框选,可改 `shortcuts.boxSelect`):要素自己配了就用自己那份,没配再回落到对应的整体配置(`options.pointMultiSelectStyle` / `pointBoxSelectStyle`),最后才回落到选中样式——「多选 / 框选和选中长得一样」因此是默认行为,想区分开就单独配一份。
|
|
8
8
|
|
|
9
9
|
`symbol` 支持四种写法(前两种是内置形状,由着色器按距离场直接画,任意缩放下边缘都是清晰的):
|
|
10
10
|
|
|
@@ -44,7 +44,7 @@ const lines = [
|
|
|
44
44
|
|
|
45
45
|
线宽由屏幕空间展开的实例化四边形实现,缩放与 `devicePixelRatio` 变化时保持 CSS 像素宽度;`join: 'round'` 会在拐角与端点补圆点接头。`from` / `to` 与 `path` 可以共存:折线首尾端点跟着绑定的点走,中间顶点仍由 `path` 决定。
|
|
46
46
|
|
|
47
|
-
线型可以逐条配置,也能由规则或全局样式统一给。优先级与颜色 /
|
|
47
|
+
线型可以逐条配置,也能由规则或全局样式统一给。优先级与颜色 / 宽度一样:**状态样式 > `override` 规则 > 要素自身 > 普通规则 > `lineStyle` > 实线**(热力图只改颜色与线宽,不改线型)。
|
|
48
48
|
|
|
49
49
|
```js
|
|
50
50
|
// 1) 逐条配置:预设名
|
|
@@ -90,11 +90,11 @@ view.updateLineStyle({ lineType: 'dashed', dashOffset: 2 }) // 全局兜底:
|
|
|
90
90
|
view.updateOptions({ lineRules: [{ when: { vn_kv: { gte: 500 } }, style: { lineType: 'dashdot' } }] }) // 规则:按条件批量给
|
|
91
91
|
```
|
|
92
92
|
|
|
93
|
-
优先级在运行中同样成立:**状态样式(`selectStyle` / `hoverStyle` / `multiSelectStyle`)> 要素自身(`updateLine` / `patchLines`)>
|
|
93
|
+
优先级在运行中同样成立:**状态样式(`selectStyle` / `hoverStyle` / `multiSelectStyle`)> `override` 规则 > 要素自身(`updateLine` / `patchLines`)> 普通规则(`lineRules`)> 全局(`lineStyle` / `updateLineStyle`)> 实线**。`updateLine` 写进去的 `lineType` 会盖住普通规则与全局的那一份;若命中的是 `override: true` 的规则,则由规则决定,想恢复逐条控制要把那条规则改成普通规则、收窄条件或移除(想退回由普通规则决定,把逐条字段清成 `undefined` 再打一次补丁)。demo 的「线型」下拉走的就是逐条这一条路(给每条线写 `lineType`),提示里也写了这三种写法。
|
|
94
94
|
|
|
95
95
|
线同样支持 `visible`、`tooltip`(覆盖全局提示内容,`false` 表示这条线不显示提示)与 `waypointEditable`;`pointRules` / `lineRules` 可以按属性批量改线宽与颜色(见「批量样式规则」)。 线也可以按条配置状态样式:`selectStyle`、`hoverStyle` 与多选用的 `multiSelectStyle`(缺省回落到 `options.lineMultiSelectStyle`,再回落到选中样式)。
|
|
96
96
|
|
|
97
|
-
线路也支持 `zIndex`
|
|
97
|
+
线路也支持 `zIndex` 层序:**`override` 规则 > 要素自身 > 普通规则 > `lineStyle` > 0**,层号大的整条画在上层,同层内仍按数据顺序 —— 不写 `zIndex` 时铺批顺序与从前逐字节一致。典型用法是「电压等级高的线在低的线之上」:
|
|
98
98
|
|
|
99
99
|
```js
|
|
100
100
|
lineRules: [
|
|
@@ -105,6 +105,46 @@ lineRules: [
|
|
|
105
105
|
|
|
106
106
|
层号变化算结构性改动(样式快路径不接受它,`updateLine` 会走整层重排);只改 `zIndex` 的那一次补丁相当于给整批重排一次顺序,与改 `lineType` 同价。
|
|
107
107
|
|
|
108
|
+
## 改单个或多个节点 / 线路的样式
|
|
109
|
+
|
|
110
|
+
单个要素不必重发整份 `points` / `lines`,用 `updatePoint` / `updateLine` 按字段合并即可;颜色、尺寸、图标、显隐、层级与业务字段都走同一条补丁路。方法返回实例本身,找不到 id 时是空操作,适合表单「保存」按钮直接接。批量改同一种字段用 `patchPoints` / `patchLines`:也可以给 `[{ id, ...字段 }]` 逐条写不同补丁,返回 `{ ok, indexes, items, count, skipped }`,整次调用只记一条历史、只发一条数据变更。两种入口都只改「要素自身」这一层,不会动规则、通道结构或相机。
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
// 节点:换填充 / 描边、尺寸和图标;字符串图标名为内置形状或图片地址,也可以直接给 SVG / image 对象
|
|
114
|
+
view.updatePoint('station-a', {
|
|
115
|
+
color: '#ff3b30',
|
|
116
|
+
strokeColor: '#ffffff',
|
|
117
|
+
strokeWidth: 2,
|
|
118
|
+
size: 18,
|
|
119
|
+
symbol: '/marker.svg',
|
|
120
|
+
zIndex: 6
|
|
121
|
+
})
|
|
122
|
+
view.updatePoint('station-b', { symbol: { type: 'svg', path: 'M12 1 L23 12 L12 23 L1 12 Z', viewBox: [0, 0, 24, 24] } })
|
|
123
|
+
|
|
124
|
+
// 线路:颜色、线宽、透明度、线型与折线几何都能逐条改
|
|
125
|
+
view.updateLine('line-a-b', { color: '#1e88e5', width: 5, opacity: 0.85, lineType: 'dashed' })
|
|
126
|
+
|
|
127
|
+
// 批量:同一份补丁写一批,或者逐条给不同字段
|
|
128
|
+
const patchedPoints = view.patchPoints(['station-a', 'station-b'], { color: '#22c55e', size: 16 })
|
|
129
|
+
const patchedLines = view.patchLines([
|
|
130
|
+
{ id: 'line-a-b', color: '#f97316', width: 4 },
|
|
131
|
+
{ id: 'line-b-c', color: '#14b8a6', lineType: 'dotted' }
|
|
132
|
+
])
|
|
133
|
+
console.log(patchedPoints.count, patchedPoints.skipped, patchedLines.count)
|
|
134
|
+
|
|
135
|
+
// 线路上的图标是 decorations:挂一个 / 改第 0 个 / 整份换
|
|
136
|
+
view.addLineDecoration('line-a-b', { symbol: '/tower.svg', position: 0.5, size: 18, color: '#ffffff', strokeColor: '#1e88e5' })
|
|
137
|
+
view.updateLineDecoration('line-a-b', 0, { size: 22, position: 0.35 })
|
|
138
|
+
view.setLineDecorations('line-a-b', [])
|
|
139
|
+
|
|
140
|
+
// 批量改同一份字段;多次改动可以用 batch 收成一条数据变更、一步历史
|
|
141
|
+
view.patchPoints(['station-c', 'station-d'], { opacity: 0.6 })
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
把数据字段清成 `null` 可以摘掉这一层覆盖,让它回落到规则 / 全局样式,例如 `updatePoint('station-a', { color: null, size: null, symbol: null })`。唯一要留意的是 **`override: true` 的规则**:它写的字段优先级高于要素自身字段,所以逐条补丁会被它压住;此时要改这条规则(收窄 `when`、去掉 `override`、调低它的样式值或移除),或者换一个不被它写的字段。普通规则只补空位,不会挡住 `updatePoint` / `updateLine`。
|
|
145
|
+
|
|
146
|
+
**交互颜色永远在最上层**。悬停、单选、多选、框选、级联(以及节点 `boxSelectStyle`、线路 `multiSelectStyle` 这些逐要素状态样式)都位于规则之前:状态样式写到的字段即使被 `override: true` 规则命中,也仍按状态样式画。因此选中 / 框选 / 级联后看到的高亮色不会被批量规则改回业务色;线路框选没有单独的 `lineBoxSelectStyle`,沿用 `lineSelectStyle`。状态样式没写的字段仍按 `override` 规则 → 要素自身 → 普通规则 → 全局样式逐级回落。
|
|
147
|
+
|
|
108
148
|
## 条件语言
|
|
109
149
|
|
|
110
150
|
筛选、批量样式规则与标签的 `when` 共用同一套条件语言,三种写法可以混用,数组表示「并且」:
|
|
@@ -158,7 +198,11 @@ view.unregisterOperator('mod2')
|
|
|
158
198
|
|
|
159
199
|
## 批量样式规则
|
|
160
200
|
|
|
161
|
-
`pointRules` / `lineRules` 按属性批量改外观:每条规则是 `{ when?, style, name? }
|
|
201
|
+
`pointRules` / `lineRules` 按属性批量改外观:每条规则是 `{ when?, style, override?, name? }`。同一层内按数组顺序命中,后命中的覆盖前面的:
|
|
202
|
+
|
|
203
|
+
- `override` 缺省 `false`:普通规则**只补要素自身没写的字段**,但始终高于 `pointStyle` / `lineStyle`;
|
|
204
|
+
- `override: true`:这条规则写了的字段压过要素自身字段;规则没写的字段仍回落到要素自身、普通规则与全局样式;
|
|
205
|
+
- 两种规则都低于悬停 / 选中 / 多选 / 框选等状态样式;热力图开着时颜色与尺寸倍数也仍在它们之上。
|
|
162
206
|
|
|
163
207
|
```js
|
|
164
208
|
createGisView(container, {
|
|
@@ -177,17 +221,24 @@ createGisView(container, {
|
|
|
177
221
|
{ name: '风电', when: { station_type: 'wind' }, style: { symbol: { type: 'svg', path: 'M12 1.5 L22.5 12 L12 22.5 L1.5 12 Z', viewBox: [0, 0, 24, 24] } } },
|
|
178
222
|
// 分层:同一个 zIndex 的节点连成一层,高电压等级压在上面
|
|
179
223
|
{ name: '1050kV 层', when: { vn_kv: 1050 }, style: { zIndex: 4 } },
|
|
180
|
-
{ name: '525kV 层', when: { vn_kv: 525 }, style: { zIndex: 2 } }
|
|
224
|
+
{ name: '525kV 层', when: { vn_kv: 525 }, style: { zIndex: 2 } },
|
|
225
|
+
// 告警点强制红:即使数据里写了 color / size 也按这条规则画;悬停 / 选中仍优先
|
|
226
|
+
{ name: '告警强制红', when: { status: 'alarm' }, override: true, style: { color: '#ff3b30', strokeColor: '#7a0d08', strokeWidth: 2, size: 20 } }
|
|
181
227
|
],
|
|
182
228
|
lineRules: [
|
|
183
229
|
{ when: { vn_kv: 1050 }, style: { color: '#e0592e', width: 2.4 } },
|
|
184
230
|
{ when: { vn_kv: { gte: 525 } }, style: { color: '#e0a92e', width: 1.8 } },
|
|
185
|
-
{ when: { vn_kv: { lt: 525 } }, style: { color: '#2ea36b', width: 1.2 } }
|
|
231
|
+
{ when: { vn_kv: { lt: 525 } }, style: { color: '#2ea36b', width: 1.2 } },
|
|
232
|
+
{ name: '故障线路强制红', when: { status: 'fault' }, override: true, style: { color: '#ff3b30', width: 3.2 } }
|
|
186
233
|
]
|
|
187
234
|
})
|
|
188
235
|
```
|
|
189
236
|
|
|
190
|
-
|
|
237
|
+
优先级可以记成一张表:**状态样式 > 热力图 > `override` 规则 > 要素自身字段 > 普通规则 > `pointStyle` / `lineStyle` > 内置默认**。热力图只作用于配了色带 / 倍率的字段,其余字段继续按这张表回落。普通规则永远高于全局样式;默认规则不压逐条配置,只有 `override: true` 才会压过它。多条规则同时命中同一字段时,普通层内后命中的赢,`override` 层内也是后命中的赢,且 `override` 层整体高于普通层,与数组先后无关。
|
|
238
|
+
|
|
239
|
+
`options.visualMap` 不单独占用样式优先级,也不会改写规则或状态样式;它读同一套热力字段 / 值域 / 色带,并把当前选区接入运行时门控:显式 `target` 时只有该类、同 `field` 的热力要素在落选后按上面的优先级回落到普通样式;不写 `target` 时覆盖 `heatmap` 的全部启用目标,光晕与密度也一并退出。其它字段的要素不受影响。`onChange` 仍可把选择交给业务侧做额外联动。
|
|
240
|
+
|
|
241
|
+
点 / 线配置里那些单独的样式字段(`color`、`size`、`symbol`、`hoverStyle`…)只会被 `override` 规则盖住;需要整批换配色时,`updateOptions({ pointRules: [...], lineRules: [...] })` 传新数组即可。规则在编译时预计算真正写到的字段,10 万级数据下逐点判定与字段合并不会成为热点。
|
|
191
242
|
|
|
192
243
|
`zIndex` 写在规则里就是「按属性分层」:同一个值的节点连成一层,层与层之间从下往上画(层内仍是先贴图组、再形状批次)。分层把「谁压谁」从「提交顺序」变成确定的顺序,代价是绘制序列要按层切开——同一张符号贴图跨层出现就会一层一批,形状点也是一层一批,层数越多绘制调用越多(实测 4 层比不分层多 2 次绘制调用,帧耗时不变;几十层才会显出来)。它不减少填充量,因此不提高帧率;十万级数据的性能靠的还是同一层内合批、`progressiveInit` 与增量上传这几项。
|
|
193
244
|
|
|
@@ -44,7 +44,7 @@ view.updateOptions({
|
|
|
44
44
|
| `maxCount` | `number` | `200` | 最多渲染多少个标签(按当前视口内可见点的顺序取,被避让丢掉的候选不计入),`0` 表示不限制;只在 `labelOptimize: true` 时生效(缺省强制显示档不截断) |
|
|
45
45
|
| `className` / `style` | `string` / `Record<string, string>` | 无 | 附加类名与内联样式:**不用写 HTML 也能改外观** —— `style` 是内联样式的增量合并,`background` / `color` / `fontSize: '14px'` / `fontWeight` / `borderRadius` / `padding` 这些写什么加什么(值按 CSS 写法给字符串),逐条再叠 `updateLabel` 的 `style`;不配就是样式表里的默认(白底 88% / `#1f3550` / 12px),想清掉某一项就写 `{ 那一项: null }` |
|
|
46
46
|
| `zIndex` | `number` | 无 | 标签层级:越大越靠上。显式值优先于 `followNodeZIndex`;同类标签按层级从低到高排,高层的 DOM / Canvas 标签压住低层。参与「更高层节点图标盖住标签」的判定 |
|
|
47
|
-
| `followNodeZIndex` | `boolean` | `false` | 节点标签跟随节点样式链里生效的 `zIndex
|
|
47
|
+
| `followNodeZIndex` | `boolean` | `false` | 节点标签跟随节点样式链里生效的 `zIndex`(`override` 规则 > 节点自身 > 普通规则 > `pointStyle`)。跟随后的标签可被更高层节点图标整条让位,也可被更高层标签盖住;显式 `zIndex` 优先。线路标签忽略这一项 |
|
|
48
48
|
| `zoomScale` | `number` | `0` | 字号随地图缩放的指数:`0` 保持屏幕字号不变,`1` 与地图同倍率放大 / 缩小,`0.5` 表示地图放大 4 倍时字号放大 2 倍。基数是该项 `style.fontSize`(缺省 12px);实时交互按 0.1px 量化,停手后按真实排版校正盒子与避让 |
|
|
49
49
|
| `avoidOverlap` | `boolean` | `true` | 标签避让:和已经放下的标签重叠时按 `overlapMode` 处理;只在 `labelOptimize: true` 时生效 |
|
|
50
50
|
| `overlapGap` | `number` | `2` | 避让留白(CSS 像素),两个标签的矩形至少隔开这么多 |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 13 动效与图元
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 热力图、图例(visualMap)、动画效果、线路方向箭头、贴图预热与自定义图元。
|
|
4
4
|
|
|
5
5
|
## 热力图
|
|
6
6
|
|
|
@@ -38,7 +38,7 @@ view.updateOptions({
|
|
|
38
38
|
| `density` | `boolean \| number \| GisViewHeatmapDensity` | 开着(`style` `'smooth'`、`layer` `'below'`、`radiusRatio` 0.055、`resolution` 384、`levels` 12、`threshold` 平滑 0.02 / 分档 0.06、`core` 平滑 0 / 分档 0.07) | 密度场(KDE):把参与热力图的节点 / 线路撒成一张密度栅格,再按色带上色 —— 重叠处越叠越热、热区往外化开。`style: 'smooth'`(缺省)按色带连续取值、alpha 跟着场值涨,外缘一路淡到全透明,就是 ECharts 地图热力图那种「往外化开」的团;`style: 'band'` 是分档等值线,每档一个纯色、档与档之间硬边,最热的那一档换成提亮的「热核」。`layer` 决定铺在哪一层:`'below'`(缺省)数据之下、不挡节点与线路;`'above'` 数据之上、压住节点与线路;`'top'` 连框选与草稿线一起压住(`'over'` / `'under'` / `'bottom'` 一类是别名;只换画的顺序,不重新烘焙)。`false` / `0` 关掉就回到逐要素上色 + 光晕;写数字 = 核半径比例。可调 `{ style, layer, radius, radiusRatio, resolution, levels, threshold, edge, core, gamma, max, weight, opacity }`,见下 |
|
|
39
39
|
| `missing` | `'keep' \| 'min' \| 'max' \| number` | `'keep'` | 字段缺 / 不是数字时:`keep` 保持原样(这个要素不吃热力图)、`min` / `max` 按值域端点算、写数字按给定值算 |
|
|
40
40
|
|
|
41
|
-
颜色在整条样式链里的位置是 **状态样式 > 热力图 > 要素自身 `color` >
|
|
41
|
+
颜色在整条样式链里的位置是 **状态样式 > 热力图 > `override` 规则 > 要素自身 `color` > 普通规则 > `pointStyle` / `lineStyle` > 内置默认**:悬停 / 选中样式配了颜色就一直按它画,热力图只接静态那一层;普通规则始终高于全局样式、默认低于要素自身,单条规则写 `override: true` 才会压过要素字段。`size` / `width` 给的是倍数、乘在整条尺寸链的最后(节点 = 自身尺寸 × `pointSizeScale` × 热力图,线宽同理),既能放大选中态,也能和整体放大系数叠着用。
|
|
42
42
|
|
|
43
43
|
**光晕与色带透明度**决定「像不像热力图」。热力图是两层画法:先按 `glow` 铺一遍更大 / 更宽的渐隐色(光晕),再把本色画在上面。光晕强度是 `glow.strength × 值域比例`,所以冷端不发光、热端最亮;一屏里重叠的光晕会按叠加方式累加,密集处自然烧成一片亮色 —— 主流热力图那种「热区晕开」的观感就来自这一层。`glow.blend` 缺省 `'auto'`:背景亮度低于 0.5 用加法混合(越叠越亮,深底色最像发光图),浅底色自动退回普通混合(加法在浅底上会糊成一片白),`'add'` / `'normal'` 可以强制。`opacity` 是另一条路子:把色带的冷端压到半透明,低值在图上只剩一层淡色、高值才压得住,浅底色上主要靠它拉开层次。逐要素同样能逐项覆盖(`heatmap: { glow: false }` 就是这一个要素不发光、`heatmap: { opacity: 1 }` 就是它不透明)。光晕的渐隐按符号自己的距离场走(`distance + 1` 就是外扩到整块精灵的形状半径):圆符号取到 `length(point)`、与按圆淡出逐像素等价,方 / 圆角方形符号铺的是同形状的晕,不再是「方节点外面套一个圆点」。
|
|
44
44
|
|
|
@@ -77,6 +77,224 @@ view.updateOptions({
|
|
|
77
77
|
|
|
78
78
|
**性能**:取值与比例是逐要素查表(色带按 `colors` 的引用建一次、128 档),但**自动值域要扫一遍数据**(线路的值域会连通道自己的值一起扫,两者共用一把尺子),`updateOptions({ heatmap })`、换数据之后的整份重解析都会重算一次;数据量大又想省掉这次扫描就写死 `domain`。逐要素的 `heatmap` 对象按引用缓存,同一份配置反复解析只会归一化一次。
|
|
79
79
|
|
|
80
|
+
## 图例(visualMap)
|
|
81
|
+
|
|
82
|
+
`options.visualMap` 是内置的热力图图例,写法参考 ECharts 的 `visualMap`:把同一条色带画成一把**带刻度的连续尺**,或者一组**分段色块**。默认它是只读的 DOM 覆盖层,不参与画布拾取;写 `interactive: true` 后由组件内置分段点选、区间拖动与键盘操作,并把当前选区同步到热力层。显式写 `target` 时选区只门控这一类;不写时展示值域取 `heatmap` 启用目标中第一个有数据的一类,选区却门控全部启用目标,字段仍必须一致。落选值由 `outOfRange` 决定是只恢复普通样式、把对应网架一起隐藏,还是完全交给 `onChange`。三种模式都不删除数据、不改筛选,也不会误伤其它字段的要素。`render(context)` 还能完全接管图例内容,在保留定位与交互上下文的前提下画业务自己的 DOM。
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
const view = createGisView(container, {
|
|
86
|
+
points,
|
|
87
|
+
heatmap: {
|
|
88
|
+
field: 'load',
|
|
89
|
+
density: false, // 图例只展示色带;密度场 / 光晕可选
|
|
90
|
+
glow: false,
|
|
91
|
+
colors: ['#2f6fd0', '#4cb96b', '#e0c22e', '#dd4b2e']
|
|
92
|
+
},
|
|
93
|
+
visualMap: {
|
|
94
|
+
type: 'ruler', // 连续尺;写 'continuous' 同义
|
|
95
|
+
title: '负荷',
|
|
96
|
+
unit: 'kW',
|
|
97
|
+
position: 'bottom-left',
|
|
98
|
+
tickCount: 5 // 两端 + 中间三个刻度
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 内置交互
|
|
104
|
+
|
|
105
|
+
分段图例写 `interactive: true` 后,每一段都是可点击 / 可聚焦的按钮:点击、`Enter` 或空格切换选中态,`selectedMode: 'single'` 单选、`'multiple'`(缺省)多选;单选时再点已选中的那一段会清空选择。连续尺写 `interactive: true` 会同时打开 `calculable`,色带上出现两个区间手柄:拖单端改区间边界,拖区间内部整体平移,方向键 / `PageUp` / `PageDown` 每步 2%,按住 `Shift` 为 10%、`Alt` 为 0.5%,`Home` / `End` 跳到两端。键盘与指针操作都带 ARIA 读数。
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
view.updateOptions({
|
|
109
|
+
visualMap: {
|
|
110
|
+
type: 'piecewise',
|
|
111
|
+
interactive: true,
|
|
112
|
+
selectedMode: 'multiple',
|
|
113
|
+
selected: [0, 1],
|
|
114
|
+
pieces: [
|
|
115
|
+
{ lt: 60, label: '低' },
|
|
116
|
+
{ gte: 60, lt: 85, label: '中' },
|
|
117
|
+
{ gte: 85, label: '高' }
|
|
118
|
+
],
|
|
119
|
+
onChange: ({ selected, reason, source }) => {
|
|
120
|
+
// selected 是 0 起的段下标;图例选区已同步门控热力,下面只是额外的业务联动
|
|
121
|
+
const ranges = [[0, 60], [60, 85], [85, Infinity]]
|
|
122
|
+
const rangesToApply = selected.map(index => ranges[index])
|
|
123
|
+
console.log('图例变化', source, reason, rangesToApply)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
连续尺的受控写法相同:把 `range: [20, 80]` 放进配置,用户拖动后从 `onChange` 的 `range` / `rangeRatios` 读结果;下一次 `updateOptions({ visualMap })` 写回新配置时只移动手柄与遮罩,不会再触发一次 `onChange`。
|
|
130
|
+
|
|
131
|
+
### 两种形态
|
|
132
|
+
|
|
133
|
+
`ruler`(缺省,`continuous` 同义)是一条渐变尺:纵向时低温在下、高温在上,横向时从左往右递增;每个刻度显示数值,`title` 在上方,`unit` 追加在数值后面。`piecewise` 是分段色块:每段一个色块与一行范围文字,纵向时高档在上、横向时按声明顺序从左往右。
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
view.updateOptions({
|
|
137
|
+
visualMap: {
|
|
138
|
+
type: 'piecewise',
|
|
139
|
+
title: '负荷分档',
|
|
140
|
+
unit: 'kW',
|
|
141
|
+
position: 'top-right',
|
|
142
|
+
pieces: [
|
|
143
|
+
{ lt: 60, label: '低' }, // < 60
|
|
144
|
+
{ gte: 60, lt: 85, label: '中' }, // 60 ~ 85
|
|
145
|
+
{ gte: 85, color: '#dd4b2e', label: '高' } // 最后一档不写 max,右端取值域上限
|
|
146
|
+
]
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`pieces` 与 ECharts 的区间写法一致:`gte` / `gt` / `lte` / `lt` / `min` / `max` 都认,`label` 自定义文字、`color` 自定义这一段色块;只写一个边界时按「其余边界自动补齐」处理。不写 `pieces` 就按值域和 `tickCount` 等分成相同数量的色块。
|
|
152
|
+
|
|
153
|
+
### 配置项
|
|
154
|
+
|
|
155
|
+
| 字段 | 类型 | 默认值 | 说明 |
|
|
156
|
+
| --- | --- | --- | --- |
|
|
157
|
+
| `enabled` | `boolean` | `true` | `false` 等于不配;与 `visualMap: null` 一样会把节点从 DOM 上摘掉 |
|
|
158
|
+
| `type` | `'ruler' \| 'continuous' \| 'piecewise'` | `'ruler'` | 连续尺或分段色块;`continuous` 归一化为 `ruler`,读数里的 `type` 也是 `'ruler'` |
|
|
159
|
+
| `target` | `'point' \| 'line' \| 'channel'` | 跟随 `heatmap` | 展示值域用哪一类要素。不写时跟随 `heatmap` 的全部启用目标,展示值域按 `point → line → channel` 取第一个真正有数值的目标(全都缺值则回落第一个启用目标);显式写只门控这一类。没有 `heatmap` 时按 `point` |
|
|
160
|
+
| `field` | `string` | 跟随 `heatmap`,否则 `'value'` | 取值字段。没有 `heatmap` 时会扫对应数据自动算值域 |
|
|
161
|
+
| `domain` | `[number, number] \| 'auto'` | 自动 | 显式值域;反向写 `[最大, 最小]` 就是颜色反向。显式给出时图例这一侧不再扫数据 |
|
|
162
|
+
| `min` / `max` | `number` | 无 | 只固定一端,另一端仍按自动值域补齐 |
|
|
163
|
+
| `colors` | `string[]` | 跟随 `heatmap.colors`,最后回落到内置色带 | 渐变色(至少两段) |
|
|
164
|
+
| `stops` | `number[]` | 跟随 `heatmap.stops` | 色标位置(0 ~ 1,与 `colors` 一一对应) |
|
|
165
|
+
| `opacity` | `number \| [number, number]` | 跟随 `heatmap.opacity`,否则 `1` | 色带透明度,写法与热力图一致 |
|
|
166
|
+
| `title` | `string` | `''` | 图例标题;空字符串不占标题行 |
|
|
167
|
+
| `unit` | `string` | `''` | 每个数值后追加的单位;自定义 `formatter` 时不会自动追加 |
|
|
168
|
+
| `orient` | `'vertical' \| 'horizontal'` | `'vertical'` | 尺的方向;横向时刻度在色带下方,纵向时刻度在右侧 |
|
|
169
|
+
| `position` | 四角名或 `{ top, right, bottom, left }` | `{ left: 12, bottom: 12 }` | 相对组件根节点定位;数字按 CSS 像素,字符串原样作为 CSS 长度(如 `'50%'`) |
|
|
170
|
+
| `width` / `height` | `number` | 纵向 `14 × 160`;横向 `200 × 14` | `ruler` 色带的 CSS 像素尺寸;`piecewise` 由内容撑开 |
|
|
171
|
+
| `tickCount` | `number` | `5` | 连续尺的刻度数,也是不写 `pieces` 时的自动分段数(2 ~ 12) |
|
|
172
|
+
| `precision` | `number` | 自动 | 固定小数位;不写按值域跨度自动选 0 ~ 4 位 |
|
|
173
|
+
| `formatter` | `string \| (value, info) => string` | 无 | 字符串支持 `{value}` / `{min}` / `{max}` / `{unit}` / `{title}` / `{field}`;函数收到数值与 `{ min, max, ratio, type, target, field, index, piece }` |
|
|
174
|
+
| `pieces` | `GisViewVisualMapPiece[]` | 无 | `piecewise` 的分段定义;不写按值域等分 |
|
|
175
|
+
| `interactive` | `boolean` | `false` | 打开内置交互:`piecewise` 点选分段,`ruler` 出现可拖动区间(同时把 `calculable` 缺省打开)。打开后根节点变为 `pointer-events: auto`;当前选区会门控图例作用目标上同 `field` 的热力上色、光晕与密度 |
|
|
176
|
+
| `outOfRange` | `'heatmap' \| 'network' \| 'none'` | `'heatmap'` | 落选值处理。`heatmap` 只退出热力、网架恢复普通样式;`network` 把落选节点 / 独立线路 / 通道从画布、拾取与包围盒中隐藏,不写 `target` 时三类同时生效;`none` 不做内置门控,仅通过 `onChange` 交给业务 |
|
|
177
|
+
| `selectedMode` | `'single' \| 'multiple' \| boolean \| null` | 交互时 `'multiple'`,否则 `false` | 分段选择方式;`true` 等同 `'multiple'`。`single` 再点已选中的段会清空,`multiple` 可多选或全部取消 |
|
|
178
|
+
| `selected` | `number[] \| Record<string, boolean> \| null` | 全选 | 初始选中段:0 起的下标数组,或按下标 / 标签映射的布尔对象。受控更新会替换当前选择并同步门控热力,但不会发 `onChange` |
|
|
179
|
+
| `range` | `[number, number] \| null` | 完整值域 | 连续尺的初始 / 受控区间(数值低值在前);受控写回只移动手柄、遮罩与图例作用目标上同 `field` 的热力门控,不回声触发 `onChange` |
|
|
180
|
+
| `calculable` | `boolean` | 连续尺交互时 `true` | 是否显示区间手柄;显式 `false` 可以关掉连续尺拖动,分段图例不认这一项 |
|
|
181
|
+
| `render` | `(context) => Node \| string \| null \| void` | 无 | 完全自定义图例内容。返回 DOM / DocumentFragment / HTML 字符串,或往 `context.body` 里追加节点后返回空;内置标题会隐藏,定位与指针开关仍沿用根节点 |
|
|
182
|
+
| `onChange` | `(payload) => void` | 无 | 分段点选、区间拖动 / 键盘操作以及 `context.setSelection` / `setRange` 变化后回调;图例自身已同步门控热力,这个回调用于额外业务联动。同一帧多次变化合并成一次,受控配置更新不回声 |
|
|
183
|
+
| `container` | `string \| HTMLElement` | 组件根节点 | 挂到外部容器;选择器找不到时回落到组件根节点。外部容器最好自带 `position: relative`,图例的绝对定位才会落在它内部 |
|
|
184
|
+
| `className` | `string` | `''` | 追加在根节点上的 class,适合项目自定义主题 |
|
|
185
|
+
| `style` | `Record<string, string \| number>` | 无 | 直接写在这层 DOM 上的内联样式;更新时旧键会被移除,也可写 `--gis-view-visual-map-*` 变量 |
|
|
186
|
+
|
|
187
|
+
### 跟随热力图与独立值域
|
|
188
|
+
|
|
189
|
+
不写 `target` / `field` / `domain` / `colors` 时,图例默认复用主 `heatmap` 的那一份:字段相同、自动值域同一套、色带用同一个插值函数。这样画布上的颜色与图例上的颜色不会对不上;逐要素写 `heatmap: false` 的要素仍不参与热力着色,但图例展示的是这一类要素的整体值域,不按单条覆盖拆开。
|
|
190
|
+
|
|
191
|
+
`heatmap.target` 写数组且图例不写 `target` 时,展示值域不会盲目取数组第一项:组件按 `point → line → channel` 找第一类能扫到该字段数值的目标;例如 `new.json` 的节点没有 `max_loading_rate`、线路 / 通道有,图例就直接显示线路 / 通道的值域,而不是节点的 0 ~ 1 空值域。门控范围仍覆盖 `heatmap.target` 的全部类型,不因展示目标切换而缩小。
|
|
192
|
+
|
|
193
|
+
显式写 `target` 时,当前分段 / 区间只参与这一类的运行时门控;不写 `target` 时覆盖 `heatmap` 的全部启用目标,但与图例 `field` 不一致的字段仍不受影响。独立图例仍按自己的字段展示。
|
|
194
|
+
|
|
195
|
+
`outOfRange: 'network'` 把落选范围扩大到网架本身,但只改显示,不改数据与业务状态:`target: 'point'` 隐藏落选节点;`target: 'line'` 隐藏落选独立线路,属于通道的回线由通道自己决定,不会被线路字段误伤;`target: 'channel'` 按通道声明实时收起落选通道的全部成员回线。不写 `target` 时三路同时处理:落选节点、落选独立线路与落选通道的整组回线一起隐藏。缺值或非数字不会被当作落选值隐藏。被隐藏的选中项仍留在选中集里,恢复选区后原高亮回来;`getData()`、`getFilterResult()`、`getVisibleData()`(仅筛选口径)与 `getChannels()` / `getChannel()` 都保持原样。显式 `minimap.heatmap` 的小图即使独立取色,也会同步这套网架隐藏;`outOfRange: 'none'` 则连热力也不门控,适合完全由业务自己隐藏 / 过滤。
|
|
196
|
+
|
|
197
|
+
也可以让图例完全独立于热力图。下面的写法没有配 `heatmap`,图例按 `score` 字段自己扫当前节点,显示 0 ~ 100 的独立色带,并挂到页面上的 `#score-legend`:
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
view.updateOptions({
|
|
201
|
+
visualMap: {
|
|
202
|
+
type: 'continuous',
|
|
203
|
+
field: 'score',
|
|
204
|
+
domain: [0, 100],
|
|
205
|
+
colors: ['#2f6fd0', '#f5f7fa', '#dd4b2e'],
|
|
206
|
+
orient: 'horizontal',
|
|
207
|
+
container: '#score-legend',
|
|
208
|
+
title: '评分',
|
|
209
|
+
formatter: '{value}{unit}',
|
|
210
|
+
unit: ' 分'
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- `target: 'line'` 会按线路自己的字段取域,并像热力图一样把通道自己的值一起纳入范围;通道回线没有自己的值时,仍用通道的值。
|
|
216
|
+
- 只改图例时(`updateOptions({ visualMap })`,可顺带换回调)走独立快路径:引擎场景、鹰眼图与标签层都不重排。`heatmap` 配置变化时图例会重新读取字段 / 色带;数据变化(`updateData`、`updatePoint`、拖拽、撤销等)只记脏,下一帧再按新数据重算。
|
|
217
|
+
- 图例没配置时零 DOM;`updateOptions({ visualMap: null })` 或 `enabled: false` 会立即摘掉节点。外部容器里的根节点同样会被移除,不会留下空壳。
|
|
218
|
+
- 同一份渲染签名重复下发(对象引用变了、字段没变)会合到同一帧并跳过 DOM 写入;连续拖滑杆时只保留每帧一次重画。
|
|
219
|
+
- 自动值域需要扫一遍对应数据。数据量大且值域固定时,显式写 `domain`(或者给 `heatmap.domain` 让图例继承)可以省掉图例这一侧的扫描;两端都给出 `min` / `max` 时同样不扫描,只有只固定一端时才需要扫另一端。
|
|
220
|
+
- 图例是 CSS 覆盖层,位置与主题可以用 `className` / `style` 或下面这组变量整体替换:`--gis-view-visual-map-background` / `-border` / `-radius` / `-shadow` / `-color`、`--gis-view-visual-map-label-color` / `-font-size`、`--gis-view-visual-map-tick`、`--gis-view-visual-map-ruler-border`、`--gis-view-visual-map-swatch-border`,以及交互态的 `--gis-view-visual-map-piece-hover` / `-piece-radius` / `-unselected-opacity`、`--gis-view-visual-map-range-border` / `-range-fill` / `-range-shadow`、`--gis-view-visual-map-handle` / `-handle-border` / `-handle-radius` / `-focus`。
|
|
221
|
+
|
|
222
|
+
### `onChange` 载荷与数据联动
|
|
223
|
+
|
|
224
|
+
```js
|
|
225
|
+
onChange: (payload) => {
|
|
226
|
+
// payload = {
|
|
227
|
+
// type, target, field, domain, interactive, selectedMode, calculable, outOfRange,
|
|
228
|
+
// selected, range, rangeRatios, element,
|
|
229
|
+
// source: 'user' | 'api', reason: 'piece' | 'range' | 'api', originalEvent
|
|
230
|
+
// }
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
- 指针点选 / 拖动与键盘操作发 `source: 'user'`;`context.setSelection` / `setRange`、`clearSelection` / `emitChange` 发 `source: 'api'`。`reason` 区分分段选择、区间变化与主动通知。
|
|
235
|
+
- 同一帧里连续发生的多次变化会合并为一次回调,读到的是这一帧最终状态;`originalEvent` 是触发它的指针 / 键盘事件(受控更新与主动 API 调用可能是 `null`)。
|
|
236
|
+
- 图例选区会直接门控图例作用目标上同 `field` 的热力上色、光晕与密度场:显式 `target` 只作用该类,不写则覆盖 `heatmap` 的全部启用目标。缺省 `outOfRange: 'heatmap'` 让落选值退出热力、恢复普通样式;`'network'` 进一步隐藏对应网架;`'none'` 不做内置效果,只发 `onChange`。数据不删除、不触发筛选,也不影响其它字段的要素。默认继承主图热力的鹰眼图使用同一选区,显式写了 `minimap.heatmap` 的小图仍按自己的配置独立取色,但 `network` 隐藏仍会同步。`onChange` 用来在门控之外做业务联动(例如同步业务表或自己的状态);把结果写回 `visualMap.selected` / `range` 属于受控用法,这次写回不会再触发 `onChange`。
|
|
237
|
+
- `selected` 是 0 起的段下标;`range` 按数值大小返回 `[低, 高]`,`rangeRatios` 是同一区间在色带上的显示比例(反向值域时仍按左 / 下到右 / 上排序)。
|
|
238
|
+
|
|
239
|
+
### 完全自定义:`render(context)`
|
|
240
|
+
|
|
241
|
+
`render` 返回 DOM 节点、`DocumentFragment` 或 HTML 字符串即可替换整个内置正文;也可以不返回,直接往 `context.body` 追加节点。内置标题自动隐藏,外层定位、主题变量与 `interactive` 控制的 `pointer-events` 仍由图例根节点负责。
|
|
242
|
+
|
|
243
|
+
```js
|
|
244
|
+
view.updateOptions({
|
|
245
|
+
visualMap: {
|
|
246
|
+
type: 'piecewise',
|
|
247
|
+
interactive: true,
|
|
248
|
+
selectedMode: 'multiple',
|
|
249
|
+
pieces: [
|
|
250
|
+
{ lt: 60, label: '低' },
|
|
251
|
+
{ gte: 60, lt: 85, label: '中' },
|
|
252
|
+
{ gte: 85, label: '高' }
|
|
253
|
+
],
|
|
254
|
+
render: ({ body, pieces, selected, setSelection }) => {
|
|
255
|
+
const wrap = document.createElement('div')
|
|
256
|
+
const active = new Set(selected)
|
|
257
|
+
for (const [index, piece] of pieces.entries()) {
|
|
258
|
+
const button = document.createElement('button')
|
|
259
|
+
button.type = 'button'
|
|
260
|
+
button.textContent = piece.label
|
|
261
|
+
button.style.background = piece.color
|
|
262
|
+
button.addEventListener('click', () => {
|
|
263
|
+
if (active.has(index)) active.delete(index)
|
|
264
|
+
else active.add(index)
|
|
265
|
+
setSelection([...active]) // 触发 onChange,source = 'api'
|
|
266
|
+
})
|
|
267
|
+
wrap.append(button)
|
|
268
|
+
}
|
|
269
|
+
body.replaceChildren(wrap)
|
|
270
|
+
},
|
|
271
|
+
onChange: ({ selected }) => syncLegendFilter(selected)
|
|
272
|
+
}
|
|
273
|
+
})
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
自定义上下文里的可用字段与入口:
|
|
277
|
+
|
|
278
|
+
- 状态快照:`type` / `target` / `field` / `domain` / `colors` / `stops` / `opacity` / `orient` / `title` / `unit` / `ticks` / `pieces` / `selectedMode` / `calculable` / `selected` / `range` / `rangeRatios`。
|
|
279
|
+
- DOM:`root` / `body` / `titleElement`。
|
|
280
|
+
- 数值:`valueAt(ratio)` 把 0 ~ 1 的色带位置换成值,`formatValue(value)` 沿用图例的 `precision` / `formatter` / `unit`。
|
|
281
|
+
- 交互:`setSelection(indexesOrMap)`、`setRange([low, high])`、`clearSelection()`(分段清空选择 / 连续尺恢复完整区间)、`emitChange(reason?)`;前三个返回“是否真的发生变化”。
|
|
282
|
+
- `render` 在类型、值域、色带、分段、标题等渲染签名变化时重新调用,不会每次点选都重建你的 DOM。自定义内容应在自己的事件处理里更新按钮态;需要和外部面板同步就听 `onChange`,不要把每次选择都当成一次新的 `render`。
|
|
283
|
+
|
|
284
|
+
### 读数与外部扩展
|
|
285
|
+
|
|
286
|
+
```js
|
|
287
|
+
const element = view.getVisualMapElement() // 没配 visualMap 时是 null
|
|
288
|
+
const readout = view.getVisualMap()
|
|
289
|
+
// {
|
|
290
|
+
// type: 'ruler' | 'piecewise', target, field, domain, colors, orient, title, unit,
|
|
291
|
+
// interactive, selectedMode, calculable, selected, range, rangeRatios,
|
|
292
|
+
// ticks: [{ value, ratio, label }], pieces: [{ low, high, label, color, ratio }]
|
|
293
|
+
// }
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
`getVisualMap()` 返回当前读数的一份独立副本,每次调用会先把挂起的重画同步落下来,所以数据更新后可以立刻读到新值域与选择。`getVisualMapElement()` 用于挂自己的外观或额外监听;默认根节点 `pointer-events: none`,配置 `interactive: true` 后自动恢复,内置分段按钮与区间手柄已经可用,不必再手写一套。若要接业务筛选,优先用 `onChange`,而不是从 DOM 猜当前选择。
|
|
297
|
+
|
|
80
298
|
## 动画效果(effects)
|
|
81
299
|
|
|
82
300
|
线路上的图标跑动(ECharts 的 `lines.effect` 那一类)与节点上的扩散涟漪(`effectScatter` 那一类)。**配置本身就是开关**:没配就是关的;
|
|
@@ -287,11 +287,11 @@ createGisView(container, { points, lines, center: [500, 300], scale: 2 })
|
|
|
287
287
|
4. **按「节点间距」而不是「包围盒」算适应**:`focusOn([...几个代表性要素], { zoom: true })` / `zoomToBounds(业务范围)`;也可以给 `minScale` 一个下界,缩到这条线就停住。
|
|
288
288
|
5. **想让节点本身不跟着缩**:`pointZoomScale` / `lineZoomScale` 调小(`0` = 完全不跟随地图缩放),尺寸主要由 `pointSizeScale` / `lineWidthScale` 的常数倍数决定 —— 比例被带跑时至少节点不会一起变小。
|
|
289
289
|
|
|
290
|
-
6. **给节点最小间距兜底(先试它)**:打开 `fitSpacing` ——
|
|
290
|
+
6. **给节点最小间距兜底(先试它)**:打开 `fitSpacing` —— 适应视图时按可见节点最近中心距精确设定比例,远处孤立点撑大包围盒也不会再把主体压成一团;孤立点允许落到视口外,见「适应视图的节点间距(fitSpacing)」。
|
|
291
291
|
|
|
292
292
|
## 缩放范围(minScale / maxScale)
|
|
293
293
|
|
|
294
|
-
`minScale` / `maxScale` 是相机比例(像素 / 世界单位)的上下限,缺省 `0.002` ~ `
|
|
294
|
+
`minScale` / `maxScale` 是相机比例(像素 / 世界单位)的上下限,缺省 `0.002` ~ `1000000000`。所有会改比例的操作都夹在这两条线之间:滚轮缩放、双击放大、`setCamera({ scale })` / `zoomTo` / `zoomBy`,以及 `initialView` 里写的 `scale` / `zoom` —— 超出去就停在上 / 下限上,`getCamera().scale` 读到的是夹过之后的数值。上限缺省放到足够大,避免经纬度这类小量纲数据的 `fitSpacing.minDistance` 需要更高比例时被缺省值截住;显式配置的 `maxScale` 仍按给定值执行。
|
|
295
295
|
|
|
296
296
|
```js
|
|
297
297
|
// 只允许在「适应视图」附近缩放:上下各留 8 倍
|
|
@@ -309,7 +309,7 @@ view.updateOptions({ minScale: scale / 5, maxScale: scale * 5 })
|
|
|
309
309
|
- 数据坐标系跨度不同(经纬度 / 米 / 自定义单位),比例的数感完全不一样:先 `fitView()` 再读 `getCamera().scale`,按它算自己的两条线,别抄别人的数值。
|
|
310
310
|
- 数要成对给:只给上界(`maxScale`)防不住无限缩小,只给下界(`minScale`)防不住无限放大。
|
|
311
311
|
|
|
312
|
-
demo 的「初始视图与背景」面板里就有现成的入口(`docs/demos/GisViewDemo.vue`):两个输入框(最小比例 / 最大比例)直接写数字,改完走 `updateOptions({ minScale, maxScale })` **并顺手 `setCamera` 把当前相机夹回区间**(更新配置本身不会把相机拉回来,demo 里把这一步显式补上了);`以当前比例设限(÷5 ~ ×5)` 按当下比例算一对;`试一下下限` / `试一下上限` 各喂一个远超区间的比例给 `setCamera()`,读数里能看到相机停在哪;`解除限制` 回到组件缺省的 `0.002 ~
|
|
312
|
+
demo 的「初始视图与背景」面板里就有现成的入口(`docs/demos/GisViewDemo.vue`):两个输入框(最小比例 / 最大比例)直接写数字,改完走 `updateOptions({ minScale, maxScale })` **并顺手 `setCamera` 把当前相机夹回区间**(更新配置本身不会把相机拉回来,demo 里把这一步显式补上了);`以当前比例设限(÷5 ~ ×5)` 按当下比例算一对;`试一下下限` / `试一下上限` 各喂一个远超区间的比例给 `setCamera()`,读数里能看到相机停在哪;`解除限制` 回到组件缺省的 `0.002 ~ 1000000000`。自测页 `docs/steps/selftest-demo.html` 有 3 条断言盯着这一套(按当前比例设限并下发 / 越界喂值被夹在上下限 / 解除限制回到缺省)。
|
|
313
313
|
|
|
314
314
|
## 比例读写、画布事件与空闲检测
|
|
315
315
|
|
|
@@ -343,14 +343,39 @@ view.updateOptions({ idleTimeout: 0 }) // 关闭空闲回调
|
|
|
343
343
|
|
|
344
344
|
「两个本来一样的视图,只因为多了一个远处节点,节点就突然很大、很挤」——根因是自动适应读的是**数据包围盒**:远处节点把范围撑大,`fitView()` 只能把 `camera.scale` 压小,节点之间的距离随之变短;节点自己的屏幕尺寸不跟着缩,看起来就像放大了一样。
|
|
345
345
|
|
|
346
|
-
`fitSpacing`
|
|
346
|
+
`fitSpacing` 不改包围盒,而是在普通适应算完之后重新设定相机比例。它一次只用一种距离模式:`minDistance` 是**精确中心距**,不管普通适应得到的节点更疏还是更密,都按最近正距离反推出指定屏距;`maxDistance` 是**最大中心距上限**,只会在最远一对铺得过开时把比例压低。两种距离都用**屏幕 CSS 像素**,`minDistance` / `maxDistance` **二选一**,不会同时生效。它**不改相机中心**:中心始终沿用普通适应按数据包围盒算出的那一个,不会因为节点对换了一对就跳到图上另一个位置。规则网格里最近距离大量并列也不会影响结果。
|
|
347
|
+
|
|
348
|
+
### 距离口径与反推公式
|
|
349
|
+
|
|
350
|
+
节点间距使用节点中心的**世界坐标**距离,再乘当前相机比例换成屏幕距离。`camera.scale` 的单位是「CSS 像素 / 世界单位」:
|
|
351
|
+
|
|
352
|
+
```text
|
|
353
|
+
d_world(i, j) = sqrt((x_j - x_i)^2 + (y_j - y_i)^2)
|
|
354
|
+
d_screen(i, j) = d_world(i, j) * camera.scale
|
|
355
|
+
d_world(i, j) = d_screen(i, j) / camera.scale
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
- **minDistance 取最近的正距离**:在不同坐标的节点对中取 `d_world` 的最小正数。完全重合的节点对距离为 `0`,不计入最近距离,否则两个叠在一起的节点会让整套间距失效。最近距离并列时只是结果确定,不会改变相机中心。
|
|
359
|
+
- **maxDistance 取最远距离**:两个节点中心之间的最大 `d_world` 距离。实现上走凸包直径,不是逐对全量扫描;结果仍是真正的最远节点对。
|
|
360
|
+
|
|
361
|
+
```text
|
|
362
|
+
minDistance 精确模式:
|
|
363
|
+
targetScale = minDistance / d_world_nearest
|
|
364
|
+
finalScale = clamp(targetScale, minScale, maxScale)
|
|
365
|
+
|
|
366
|
+
maxDistance 上限模式:
|
|
367
|
+
targetScale = min(maxDistance / d_world_farthest, ordinaryFitScale)
|
|
368
|
+
finalScale = clamp(targetScale, minScale, maxScale)
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
`ordinaryFitScale` 是普通包围盒适应算出的比例。`minDistance` 的 `targetScale` 可以大于也可以小于它:例如最近世界距离是 `100`、`minDistance` 是 `24`,相机比例就是 `0.24`(世界单位 × 0.24 = 24 CSS 像素);改成 `minDistance: 12` 后比例就是 `0.12`,画面会真的缩小,不会停在普通适应或初始 `minDistance` 的屏幕上。`maxDistance` 则只有 `min(...)`,不会反过来放大画面。两者最后都只受 `minScale` / `maxScale` 的绝对范围夹取;显式把 `maxScale` 设得太小时,任何模式都只能停在那条上限。
|
|
347
372
|
|
|
348
373
|
```js
|
|
349
|
-
//
|
|
374
|
+
// 最省事:把最近可见节点的中心距精确设为 24 CSS 像素(仍受 minScale / maxScale 约束)
|
|
350
375
|
const view = createGisView('#map', { points, lines, channels, fitSpacing: true })
|
|
351
376
|
|
|
352
|
-
//
|
|
353
|
-
view.updateOptions({ fitSpacing: { minDistance: 32
|
|
377
|
+
// 最小中心距模式:最近节点中心距精确为 32px;比普通适应更疏时会缩小,更密时会放大
|
|
378
|
+
view.updateOptions({ fitSpacing: { minDistance: 32 } })
|
|
354
379
|
// 或最大中心距模式:最远节点对最多铺开 520px(不要和 minDistance 同时写)
|
|
355
380
|
view.updateOptions({ fitSpacing: { maxDistance: 520 } })
|
|
356
381
|
view.fitView() // 改配置不会当场动相机,再适应一次才看得到
|
|
@@ -363,14 +388,15 @@ view.updateOptions({ fitSpacing: false }) // 关掉,下一次 fitView 恢复
|
|
|
363
388
|
- **默认关**,打开后只影响「默认 fit 模式下的首次适应」与手动 `fitView()`;`zoomToBounds()` / `focusOn(..., { zoom: true })` 仍按指定范围算,不会强行套节点间距。
|
|
364
389
|
- 只统计**筛选 / 隐藏之后仍然可见**的节点。完全重合的坐标不算有效间距(否则两个重叠点会让整套兜底失效);少于两个不同位置时不调整。
|
|
365
390
|
- **只调比例、不跳视点**:`fitSpacing` 不重算中心,也不挑一对节点居中。需要改变中心时用 `initialView.center` / `initialView.bounds`、`focusOn()` 或 `setCamera({ center })`。
|
|
366
|
-
- `minDistance`
|
|
391
|
+
- `minDistance` 是**精确中心距模式**:最近两个不同位置节点的中心距(CSS 像素),默认 `24`。它按 `minDistance / 最近世界距离` 直接反推比例,所以调大、调小都会生效,不受普通适应比例或初始 `minDistance` 的“贴边”限制。想给节点本身和描边留更多空隙就调大。
|
|
367
392
|
- `maxDistance` 是**最大中心距模式**:节点中心之间的最大屏幕距离(CSS 像素),适合稀疏数据或只回显少量场站。普通适应会把最远两点顶到画布边缘、整体显得过大;配一个例如 `480` 的上限就会按最远节点对把比例压回来。
|
|
368
|
-
- 两种模式互斥:同时写 `minDistance` 与 `maxDistance` 时只按 `maxDistance` 计算,`minDistance`
|
|
369
|
-
- `maxZoom`
|
|
393
|
+
- 两种模式互斥:同时写 `minDistance` 与 `maxDistance` 时只按 `maxDistance` 计算,`minDistance` 不参与,不要靠两者同时生效。
|
|
394
|
+
- `maxZoom` 是旧配置的兼容字段,**不再限制** `minDistance` / `maxDistance`;屏距不生效时先查显式 `minScale` / `maxScale`,再用 `getPointSpacing()` 或 `getPointsSpacing(refs)` 核对实际 CSS 像素。
|
|
370
395
|
- 远处孤立点仍参与普通适应的包围盒与中心计算;放大后它可以落到视口外,这是「先保证节点间距」的取舍。如果孤立点连中心都不该影响,关掉 `fitSpacing` 或改用 `initialView.bounds` / 预筛数据钉住业务范围。
|
|
371
396
|
- 显式配了 `initialView.scale` / `initialView.zoom` 时以使用方给的为准;只有没有写死比例 / 倍数的 fit 模式才走间距兜底。
|
|
372
|
-
-
|
|
373
|
-
-
|
|
397
|
+
- 要读**当前可见节点**的实际距离用 `getPointSpacing()`:`minDistance` / `maxDistance` 以及 `nearest` / `farthest.distance` 都按当前相机比例返回 **CSS 像素**,与 `fitSpacing` 同一套口径,可直接拿来配置中心距;除以 `getScale()` 才是世界单位。筛选 / 隐藏后的节点不参与。
|
|
398
|
+
- 要读**指定一组节点**的整体距离用 `getPointsSpacing(refs)`:`refs` 可写一个或一组 id / 下标 / `{ id }` / `{ index }`,重复引用去重、非法引用忽略;只统计显式传入的节点,筛选 / 隐藏不影响。返回值与 `getPointSpacing()` 同形,`nearest` / `farthest` 都带两端节点的 id / 下标 / 数据对象。
|
|
399
|
+
- demo 的「样式」面板有「适应节点间距」开关、距离模式选择(最小 / 最大二选一)和对应中心距输入,并显示 `getPointSpacing()` 的屏幕距离读数;自测页 `docs/steps/selftest-scale.html` 覆盖精确缩小、`maxZoom` 兼容、指定节点组、空 / 单节点与隐藏节点等读数。
|
|
374
400
|
|
|
375
401
|
### 导出数据回显后比例不一样?(方案)
|
|
376
402
|
|
|
@@ -407,6 +433,9 @@ view.updateBackground({
|
|
|
407
433
|
pointSize: 12,
|
|
408
434
|
shadow: { blur: 8 },
|
|
409
435
|
aspectScale: 0.75, // 与 ECharts 的 geo.aspectScale 同义:只压经度方向,坐标不是经纬度时传 1
|
|
436
|
+
// 画出来的目标世界矩形:整份 GeoJSON 独立缩放 / 平移到这里;不写时用几何自身范围
|
|
437
|
+
bounds: { minX: 0, minY: 0, maxX: 1000, maxY: 600 },
|
|
438
|
+
rotation: 15, // 绕目标矩形中心顺时针旋转(度);图片把旋转后的四角写进 image.corners
|
|
410
439
|
nameProperty: 'name',
|
|
411
440
|
// 区域文字:show / text / formatter('{name}'、'{value}' 或函数)/ fontSize / fontWeight / fontFamily / color / offsetX / offsetY / opacity
|
|
412
441
|
label: { show: true, fontSize: 12, color: '#e6f0fa', formatter: '{name}', offsetY: -2 },
|
|
@@ -424,13 +453,43 @@ view.updateBackground({
|
|
|
424
453
|
| `color` | `string` | 画布底色 |
|
|
425
454
|
| `image` | `string \| GisViewBackgroundImage` | 图片层:`{ src, bounds?, corners?, fit?, size?, opacity?, pathStyle?, shadow? }` |
|
|
426
455
|
| `geoJson` | `GeoJsonInput` | GeoJSON 图层:几何体、`Feature`、`FeatureCollection` 及其数组 |
|
|
427
|
-
| `geoJsonStyle` | `GisViewGeoJsonStyle` | `areaColor` / `borderColor` / `borderWidth`(旧名 `fill` / `stroke` / `strokeWidth`)、`fillOpacity`、`opacity`、`pointColor`、`pointSize`、`symbol`、`shadow`,以及 `aspectScale`、`nameProperty`、`label`、`regions`、`itemStyle` |
|
|
456
|
+
| `geoJsonStyle` | `GisViewGeoJsonStyle` | `areaColor` / `borderColor` / `borderWidth`(旧名 `fill` / `stroke` / `strokeWidth`)、`fillOpacity`、`opacity`、`pointColor`、`pointSize`、`symbol`、`shadow`,以及 `bounds`、`rotation`、`aspectScale`、`nameProperty`、`label`、`regions`、`itemStyle` |
|
|
428
457
|
| `opacity` | `number` | 图片与 GeoJSON 图层的整体透明度 |
|
|
429
458
|
|
|
430
459
|
**图片**:`src` 接受 URL、dataURL、`HTMLImageElement`、`HTMLCanvasElement`、`SVGElement`,也可以直接给 SVG 路径的 `d` 字符串 —— 组件用 `getBBox()` 量出外框包成一份 SVG,用 `pathStyle`(`fill` / `stroke` / `strokeWidth` / `padding`)上色。摆放有三档:给了 `corners`(左上 / 右上 / 右下 / 左下四个世界坐标)就按四个点做仿射 + 裁剪(任意四边形都不会画歪);否则按 `bounds`(缺省用数据范围)与 `fit` 铺 —— `contain`(默认,等比铺满)、`cover`(铺满并裁剪)、`stretch`(拉伸)、`center`(原尺寸居中,`size: [宽, 高]` 给世界单位下的尺寸,不给就按图片像素 1:1)。
|
|
431
460
|
|
|
432
461
|
**GeoJSON**:支持 `Polygon` / `MultiPolygon`(内环挖洞)、`LineString` / `MultiLineString`、`Point` / `MultiPoint` 与 `GeometryCollection`,面在 CPU 侧完成耳切三角剖分后按颜色分桶批量画。`aspectScale` 与 ECharts 的 `geo.aspectScale` 同义(经纬度底图**默认 0.75**):只压经度(x)方向、围绕整体范围中心缩放,纬度不动 —— 经纬度看起来不会拉长;坐标不是经纬度(投影坐标 / 自定义平面坐标)时传 `1`。相机落位与 `fitView` 用的是缩放前的真实范围(`getResolvedBackground().geoJson.rawBounds`),`aspectScale` 只影响画出来的形状,主图、鹰眼图与数据仍然按真实经纬度对齐。
|
|
433
462
|
|
|
463
|
+
**GeoJSON 目标矩形与旋转(`geoJsonStyle.bounds` / `rotation`)**:不写 `bounds` 时按 GeoJSON 自身范围铺;写了 `{ minX, minY, maxX, maxY }` 就把解析后的整份底图独立缩放、平移到这个目标世界矩形,填充、描边、点、逐区域 `bounds` / 重心和区域文字位置一起搬过去。`rotation` 再绕目标矩形中心顺时针旋转(度,缺省 0),旋转后的填充、描边、点、区域范围与文字一起转;`bounds` 与 `getResolvedBackground().geoJson.bounds` 是旋转后的轴对齐外框,原始坐标仍留在 `rawBounds`。因此可以先用数据范围渲染网架,再按四角 / 八向手柄或宽高输入调整背景,不必回写 GeoJSON;目标宽高比与原始范围不同时就按 x / y 两轴独立缩放。图片的对应写法是 `image.corners`(或 `image.bounds` + `fit`),旋转时把四角按新角度算好写回去即可。运行时直接 `updateBackground()` 或 `updateOptions({ background })`。
|
|
464
|
+
|
|
465
|
+
**内置背景编辑器**:组件提供 `beginBackgroundEdit(options?)` / `updateBackgroundEdit(patch)` / `endBackgroundEdit()` / `getBackgroundEdit()` 四个实例方法,把背景拖动、缩放、旋转、编辑框与同帧刷新收进组件,调用方不必再手写手柄和指针数学。`beginBackgroundEdit()` 会自动打开 `backgroundEditing`(网架不接点击、拖拽、拐点、框选、右键和数据快捷键,滚轮缩放与 `fitView` 仍可用),`endBackgroundEdit()` 移除覆盖层并恢复进入前的值;两者都不改背景数据。当前没有图片或 GeoJSON 背景时 `beginBackgroundEdit()` 不做事并通过 `onWarning` 报一句。编辑器采用懒初始化:没调用过 `beginBackgroundEdit()` 的页面不会多挂 DOM 或监听,实例销毁时也会清理。
|
|
466
|
+
|
|
467
|
+
```js
|
|
468
|
+
view.beginBackgroundEdit({
|
|
469
|
+
aspectLocked: true, // true:四角等比缩放;false:四角 + 四边中点共八向
|
|
470
|
+
rotation: true, // 再显示一个顶部旋转点,因此最多 4 / 5 / 8 / 9 个编辑点
|
|
471
|
+
snapAngle: 15, // 拖动旋转时按住 Shift 的吸附角,0 关闭
|
|
472
|
+
minSize: 12, // 缩放时最小屏幕尺寸(CSS 像素)
|
|
473
|
+
rotationDistance: 30, // 旋转点离上边的附加距离
|
|
474
|
+
fitView: true, // 进入时把背景放回视野
|
|
475
|
+
fitPadding: 64,
|
|
476
|
+
showHint: true,
|
|
477
|
+
style: { color: '#2f78b7', fill: 'rgba(47,120,183,0.12)', lineWidth: 1.5, dashed: true, handleSize: 15, handleShape: 'circle' },
|
|
478
|
+
onChange: state => console.log(state),
|
|
479
|
+
onEnd: state => console.log('end', state)
|
|
480
|
+
})
|
|
481
|
+
view.updateBackgroundEdit({ rotation: false, style: { lineWidth: 2, handleShape: 'square' } })
|
|
482
|
+
view.updateBackgroundEdit({ center: [500, 300], width: 800, height: 450, angle: 30 })
|
|
483
|
+
const editor = view.getBackgroundEdit() // 未开启时 null;开启时含 active / center / width / height / rotation / handleCount 等
|
|
484
|
+
view.endBackgroundEdit()
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
- **写回字段**:图片编辑写回 `background.image.corners`;GeoJSON 编辑写回 `background.geoJsonStyle.bounds` 与 `rotation`。背景的底色、透明度、阴影以及其它 GeoJSON 样式字段原样保留。`center` / `width` / `height` / `angle` 可直接由 `updateBackgroundEdit()` 修改;`rotation` 写布尔值控制旋转点显隐,写数字等价于 `angle`。锁比例时改宽或高会同步另一轴。
|
|
488
|
+
- **旋转与缩放**:旋转按顺时针角度记录,范围归一化到 -180° ~ 180°;按住 Shift 按 `snapAngle` 吸附(缺省 15°)。八个缩放手柄都先把指针换算到背景的局部坐标,因此背景转过之后拖动方向仍与视觉一致;四边手柄只改所在边的局部轴并固定对边。
|
|
489
|
+
- **刷新与读数**:指针移动合并到动画帧,帧内先写背景再以 `refresh({ reflow: false })` 重绘,DOM 编辑层与底图同一帧落位,避免拖动时手柄抢跑。`onChange` 在移动 / 缩放 / 旋转 / 配置变化时给最新状态,`onEnd` 在一次手势结束时给最终读数。`fitView: true` 可在进入或运行中把整块背景放回视野。
|
|
490
|
+
|
|
491
|
+
演示「背景与网架」(`?demo=background`)只调用这套公开 API:图片 / GeoJSON 本地导入、四角 / 八向缩放、旋转开关、宽高与角度输入、编辑框样式、隐藏或压淡网架都保留;页面不再自己手写拖动、投影和 CSS 编辑框。编辑框样式属于组件编辑器的运行配置,经 `beginBackgroundEdit({ style })` / `updateBackgroundEdit({ style })` 传入,不占用顶层 options。
|
|
492
|
+
|
|
434
493
|
**逐区域样式与文字**:样式先取一层整体色(`areaColor` / `borderColor` / `borderWidth`),再按 `regions`(键是 `nameProperty` 取到的名字,也可以传 `Map`、数组或 `(name, region) => style` 函数)覆盖,最后走 `itemStyle(params)`(拿到 `{ index, name, properties, region }`,可以用来做「按属性配色」这类完全自定义的映射)。`label` 打开区域文字,位置取区域面积重心(退化时回落到包围盒中心),文字渲染在覆盖层上(导出图片时一起画进来)。
|
|
435
494
|
|
|
436
495
|
背景层与数据同坐标系:数据在哪,底图就画在哪,缩放 / 平移跟着一起走,永远画在数据下面。没有现成 GeoJSON 时可以在业务侧按数据范围现生成一份(街区、经纬网、河流都可以拼成 `FeatureCollection`),`docs/demos/demoBackground.js` 的 `buildDemoBasemap(bounds, { districts, grid, padding })` 就是一个纯 JS 示例(默认街区与经纬网都开),demo 与 `docs/steps/step-11-background.js` 共用它。
|
|
@@ -549,8 +608,8 @@ createGisView(container, {
|
|
|
549
608
|
| `lineStyle` | `{ color?, width?, opacity? }` | `#8fb2d8` / `1` / `0.55` | 线路的颜色、线宽与透明度,`opacity: 0` 就是不画线 |
|
|
550
609
|
| `pointRules` | `GisViewMinimapPointRule[]` | 无 | 小图自己的节点规则:按属性给每个节点定颜色 / 半径 / 形状(`'circle'` / `'square'`)/ 层级,条件语言与主图的 `pointRules` 完全一样;规则里没写的字段回落到 `pointStyle` |
|
|
551
610
|
| `lineRules` | `GisViewMinimapLineRule[]` | 无 | 小图自己的线路规则:颜色 / 线宽 / 透明度 / 层级,规则里没写的字段回落到 `lineStyle` |
|
|
552
|
-
| `heatmap` | `GisViewHeatmap \| false` | 沿用主图 | 小图自己的热力图:不写就沿用主图的 `options.heatmap
|
|
553
|
-
| `inheritRules` | `boolean` | `true` | 没配自己的规则时是否沿用主图样式:节点取主图规则的颜色 / 描边色 /
|
|
611
|
+
| `heatmap` | `GisViewHeatmap \| false` | 沿用主图 | 小图自己的热力图:不写就沿用主图的 `options.heatmap`(同一份色带、自动值域与 `visualMap` 选区),写对象就单独换一份且不跟随主图图例,`false` 表示小图不按热力着色(主图照旧)。`visualMap.outOfRange: 'network'` 的网架隐藏不受这一项影响,显式独立热力配置也会同步,见「热力图」 |
|
|
612
|
+
| `inheritRules` | `boolean` | `true` | 没配自己的规则时是否沿用主图样式:节点取主图规则的颜色 / 描边色 / 层级,并沿主图最终解析出的尺寸与图标(`override` 规则 > 点自身 > 普通规则 > `pointStyle`,圆、方、图片、SVG 与自定义图元都认);沿用尺寸 / 线宽时还会乘一次**小图与主图 CSS 视口宽高的较小比例**,只跟两边视口尺寸有关,不随主图相机倍数变化,小图比主图小时节点与线路不会仍顶着和主图一样的尺寸。线路沿用颜色 / 透明度与样式链线宽。显式 `false` 表示小图只用自己那份 `pointStyle` / `lineStyle` |
|
|
554
613
|
| `levels` | `number[]` | 无 | 只画这些层级的节点:层级取规则的 `zIndex`(没写算第 0 层),不配表示全部画;「每一层显示什么级别」就是它 —— 按电压写了 `zIndex` 之后,只看 500kV 以上就写 `levels: [500, 1000]` |
|
|
555
614
|
| `cascade` | `false \| 'binding' \| 'coordinate' \| 'both'` | `'binding'` | 被 `levels` 挡掉的节点,连在它们身上的线路怎么跟着隐藏:`binding` 按 `from` / `to` 绑定、`coordinate` 按端点坐标(端点正好落在被挡掉的节点上)、`both` 两者都算、`false` 不联动。与主视图筛选的 `cascade` 是同一套说法 —— 主视图筛掉的要素本来就带联动,这里管的是小图**自己**挡掉的那部分;只看某几层时不会留下一堆凭空断掉的线 |
|
|
556
615
|
| `layered` | `boolean` | `true` | 节点是否分层叠画:`zIndex` 大的后画、盖在上面;关掉就按数据顺序画(同坐标的节点谁在上面看数据顺序) |
|
|
@@ -571,7 +630,7 @@ createGisView(container, {
|
|
|
571
630
|
| `dragAction` | `'pan' \| 'box'` | `'pan'` | 小图空白处(视口框之外)拖动的动作:`pan` 平移小图内容(主图不动,内容夹在数据范围里,需手动缩放才有可移动余量),`box` 直接拉出框选矩形让主图显示过去。`box` 不依赖 `zoomable`,关闭缩放时也能直接框选;只有手动缩放模式下,按住 Ctrl / ⌘ 才切换成另一个动作 |
|
|
572
631
|
| `channelCircuits` | `boolean` | `true` | 通道是否画出全部回线:`false` 时每个分组按当前可见成员逐顶点取平均,只画一根居中的代表线(双回线落在两根正中间,三回线落在中间那条),通道走向仍然看得出来,通道多的底图更清爽 |
|
|
573
632
|
|
|
574
|
-
|
|
633
|
+
规则的用法与主图一致(同一套条件语言、按顺序命中、后命中的覆盖前面的,单条规则也可写 `override: true`),但只管小图的画法:节点按 `zIndex` 分层,`levels` 只留下指定层的节点,`layered: false` 时不排序、按数据顺序画;线路按「层级 + 颜色 + 线宽 + 透明度」分桶,一桶一条路径、一次 `stroke`,规则再多也不会把小图的绘制次数拉爆。没配自己的规则时(`inheritRules !== false`)完整沿用主图样式链:颜色 / 描边色 / 层级来自主图规则,节点尺寸与图标取主图最终解析出的那一份(`override` 规则 > 点自身 > 普通规则 > `pointStyle`),线路线宽取主图样式链解析出的原始值,再按小图与主图 CSS 视口宽高的较小比例缩一次(不跟主图相机倍数走)—— 圆、方、图片、SVG 与自定义图元都保留主图的形状,但不会在小图上仍和主图一样大。显式配了小图自己的 `pointRules` / `lineRules`(或 `inheritRules: false`)时,对应的尺寸 / 线宽不受这一步影响,仍由小图自己的规则与样式决定;只配 `pointStyle` / `lineStyle` 不会单独关掉继承。节点的颜色取解析后的 `strokeColor`、没有才回落 `color`:主图的节点是白底 + 一圈电压色的描边,小图上一个点是一块实心像素,不这样取就会画成一堆白点(自己配的小图规则同样可以写 `strokeColor`)。
|
|
575
634
|
|
|
576
635
|
**动态位置与外部挂载**:`offset` 是整张鹰眼图相对当前角落的位移,最适合做「拖动整个小图」的底移量 —— 指针每移动一段,就把累计位移写回 `updateOptions`;`position` / `margin` 仍决定基准角落。`docs/demos/MinimapDemo.vue`(`?demo=minimap`)在小图顶部内置了一个「拖动整图」手柄:把 `getMinimapElement()` 返回的根节点当作拖动目标,指针位移累计到 `offset`,同时保留了小图内部的框选 / 平移手势;右侧还有「整图位置复位」按钮可以直接回到 `[0, 0]`。
|
|
577
636
|
|
|
@@ -618,6 +677,8 @@ console.log(minimapElement?.parentElement === minimapHost) // true
|
|
|
618
677
|
|
|
619
678
|
交互与开销:**在框上按下直接拖框**(世界位移 = 屏幕位移 ÷ 小图比例),**空白处单击不改变视角**;未手动缩放且 `dragAction` 为 `pan` 时,按住超过 `longPressDelay` 才进入「跟着拖」;`dragAction: 'box'` 或手动缩放时,空白处按住即按对应动作开始,拖动过程中始终跟着光标;光标停在小图上滚动滚轮按光标位置缩放主图(`wheelZoom: false` 关掉)。每个有位移的手势结束时可通过 `onInteraction` 收到一条通知,不必自己在小图 DOM 上再挂一套指针监听。底图画的是筛选之后真正画出来的线,通道骨架与回线都在内(直接问引擎要几何,不用自己再推一遍通道偏移),画法是先铺线、再把节点整层盖上去 —— 节点层级高于线路,密集底图上的节点不会被线咬掉。底图只在数据或筛选结果变化时重画(节点直接写进像素缓冲,十万级也只在重建时花一次时间),相机变化只改视口框的 `transform`,所以拖动小图不掉帧。小图还会缓存**上一次的节点像素结果**与**数据范围**:一次改动排在前后两帧各重画一次(引擎重排之后要再取一次几何),第二次与第一次的数据、范围、尺寸完全一样时直接复用那份像素,十万点下第二次重画从 ~20ms 降到 0.3ms(配了 `pointMapZoomScale` / `lineMapZoomScale`(或存在沿用主图线宽的线路)时把「主视图倍数 / 两边视口比例」也并进缓存的键,倍数变了才重铺);打开 `extentFollowCamera` 后 `extent: auto` 的取舍才跟相机有关,相机挪进 / 挪出那份小范围时才补一次重画(每帧只做几次比较,判断用的范围是缓存的)。小图拖框 / 小图滚轮带来的相机变化不算主画布上的交互:配了 `hideOnInteract` 的图层不会在这时被收起来,拖动结束还会补一次真实排版 —— 不会出现「动一下鹰眼图,主视图标签全没了」。真在主画布上拖动过之后、两层标签同时收着时,收尾计时器也照样挂上 —— 覆盖层停手的收尾是无条件的,不会因为「两层都在收着、当下没东西可排」把恢复一起跳过。
|
|
620
679
|
|
|
680
|
+
`visualMap.outOfRange: 'network'` 的隐藏也作用于鹰眼图:继承主图热力时,图例选区与网架隐藏都按主图状态走;显式写了 `minimap.heatmap` 时小图的**取色**仍独立,但落选节点与线路是否隐藏仍同步主图。小图只做显示门控,不改数据、筛选结果与通道声明。
|
|
681
|
+
|
|
621
682
|
线路层另有自己的离屏 canvas 与缓存:缓存键包含画布尺寸、范围、投影、小图倍率与线路样式,命中时整层直接合成,不再重复投影与描边;`collectMinimapData()` 复制路径时顺路算出每条线的包围盒,完全落在小图视野外的线不建路径。只有布局尺寸实际变化时才重设 canvas 尺寸,单纯补画或移动视口框不会清空这份缓存。
|
|
622
683
|
|
|
623
684
|
`zoomable` 有三种互斥的模式:`false`(默认)只看固定范围,滚轮缩放主图;`true` 手动缩放小图自身(滚轮以光标为锚点,Ctrl / ⌘ + 滚轮仍留给主图);`'follow'` 让整个小图的显示范围跟随主图倍数变化(滚轮仍直接缩放主图,`followZoomScale` 是主图相对基准倍数的指数,`0` 表示不跟随、`1` 同倍率、`0.5` 是主图放大 4 倍时小图放大 2 倍;`zoom` 在这里是基准倍数,结果仍夹在 `minZoom` ~ `maxZoom`)。跟随模式下 `setMinimapZoom` / `zoomMinimapBy` 不抢控制权,倍数始终由主图决定。
|