gis-view-webgl2 0.1.7 → 0.1.8
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 +9 -8
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +232 -228
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +49 -0
- package/docs/GisView.md +1 -1
- package/docs/ai/06-/344/274/232/350/257/235/347/264/242/345/274/225/344/270/216/345/244/215/346/237/245/346/226/271/346/263/225.md +48 -0
- package/docs/demos/BasicDemo.vue +2 -2
- package/docs/demos/BatchAddDemo.vue +6 -6
- package/docs/demos/ConnectDemo.vue +164 -33
- package/docs/demos/CopyMergeDemo.vue +27 -14
- package/docs/demos/EventDemo.vue +17 -20
- package/docs/demos/LabelDemo.vue +5 -5
- package/docs/demos/api-index.js +31 -29
- package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +1 -1
- package/docs/demos/gis-view-demo/GisViewDemoStage.vue +1 -1
- package/docs/demos/gis-view-demo/panels/DataPanels.vue +1 -1
- package/docs/demos/gis-view-demo/useGisViewDemo.js +31 -33
- package/docs/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +8 -7
- package/docs/from0/05-/347/233/270/346/234/272/344/270/216/344/272/244/344/272/222.md +1 -1
- package/docs/from0/07-/351/200/232/351/201/223/345/210/206/347/273/204/344/270/216/345/244/232/345/233/236/347/272/277.md +26 -10
- 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/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +21 -16
- package/docs/manual/06-/346/225/260/346/215/256/344/270/216/345/234/272/346/231/257/346/233/264/346/226/260.md +9 -10
- 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 +54 -31
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +3 -3
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +9 -6
- package/docs/manual/10-/351/200/232/351/201/223/347/274/226/350/276/221/344/270/216/346/226/260/345/242/236.md +226 -493
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +2 -2
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +12 -13
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +23 -21
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +19 -17
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +125 -78
- 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 +1 -1
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +2 -2
- 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 -4
- package/docs/steps/README.md +9 -7
- package/docs/steps/bench-scale.html +1 -1
- package/docs/steps/index.html +6 -6
- package/docs/steps/selftest-actions.html +26 -25
- package/docs/steps/selftest-data.html +1 -1
- package/docs/steps/selftest-demos.html +102 -6
- package/docs/steps/selftest-docs.html +3 -3
- package/docs/steps/selftest-features.html +283 -51
- package/docs/steps/selftest-full.html +3 -3
- package/docs/steps/selftest-labels.html +1 -1
- package/docs/steps/selftest-perf.html +31 -4
- package/docs/steps/selftest-select.html +20 -5
- package/docs/steps/step-05-pick.js +4 -8
- package/docs/steps/step-07-channels.js +3 -5
- package/docs/steps/step-08-labels.js +3 -3
- package/docs/steps/step-10-full.js +3 -3
- package/docs/steps/step-12-events.js +41 -24
- package/package.json +1 -1
|
@@ -319,9 +319,36 @@
|
|
|
319
319
|
big.removePoints(['bench-new', 'bench-new2'])
|
|
320
320
|
const channelRemoveMs = now() - channelRemoveStarted
|
|
321
321
|
diag('通道在场时补 2 个点', `${channelAddMs.toFixed(1)} ms(这一轮之前 40 ms 上下)`)
|
|
322
|
-
diag('通道在场时删这 2 个点', `${channelRemoveMs.toFixed(1)} ms(这一轮之前 35 ms 上下)`)
|
|
323
|
-
check('通道在场时的增删:通道成员表不动、点真的加上又删掉', big.getData().points.length === pointCount && big.getChannel('bench-ch').lineIndexes.length === 2, JSON.stringify({ points: big.getData().points.length, members: big.getChannel('bench-ch').lineIndexes }))
|
|
324
|
-
|
|
322
|
+
diag('通道在场时删这 2 个点', `${channelRemoveMs.toFixed(1)} ms(这一轮之前 35 ms 上下)`)
|
|
323
|
+
check('通道在场时的增删:通道成员表不动、点真的加上又删掉', big.getData().points.length === pointCount && big.getChannel('bench-ch').lineIndexes.length === 2, JSON.stringify({ points: big.getData().points.length, members: big.getChannel('bench-ch').lineIndexes }))
|
|
324
|
+
// addLine 是末尾补线:已有通道声明时也走追加式补线,不能为了加一条独立线路把整份场景重排。
|
|
325
|
+
// 这里同时盯正确性(新线进场景、通道声明一条不动)与代价(最快一次明显低于整份重排)。
|
|
326
|
+
const appendLineChannelsBefore = JSON.stringify(big.getOptions().channels)
|
|
327
|
+
big.setCamera({ center: [10, 0], scale: 2 })
|
|
328
|
+
const appendLineIds = []
|
|
329
|
+
let appendLineFastestMs = Infinity
|
|
330
|
+
const appendLineStarted = now()
|
|
331
|
+
for (let index = 0; index < 5; index += 1) {
|
|
332
|
+
const callStarted = now()
|
|
333
|
+
const appendedLine = big.addLine({ from: 'p0', to: 'p1', id: `bench-added-line-${index}`, lineProps: { color: '#ff3300', width: 2 } })
|
|
334
|
+
appendLineFastestMs = Math.min(appendLineFastestMs, now() - callStarted)
|
|
335
|
+
if (appendedLine.ok) {
|
|
336
|
+
appendLineIds.push(appendedLine.lineId)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const appendLineMs = (now() - appendLineStarted) / 5
|
|
340
|
+
const appendLineScene = big.snapshotScene({ background: false })
|
|
341
|
+
const appendLinePainted = appendLineScene.lines.filter(line => line.color === 'rgba(255,51,0,1)').length
|
|
342
|
+
const appendLineRefreshStarted = now()
|
|
343
|
+
big.refresh()
|
|
344
|
+
const appendLineRefreshMs = now() - appendLineRefreshStarted
|
|
345
|
+
check('通道声明在场时 addLine 走追加式补线:5 条都进场景、通道声明不动', appendLineIds.length === 5 && appendLinePainted === 5 && JSON.stringify(big.getOptions().channels) === appendLineChannelsBefore, JSON.stringify({ ids: appendLineIds, painted: appendLinePainted }))
|
|
346
|
+
check('addLine 的追加代价明显低于整份重排(有通道声明也不会回落整份重建)', appendLineFastestMs * 5 < appendLineRefreshMs, `最快一次 ${appendLineFastestMs.toFixed(2)} ms · 平均 ${appendLineMs.toFixed(2)} ms vs 整份重排 ${appendLineRefreshMs.toFixed(1)} ms`)
|
|
347
|
+
diag('通道在场时 addLine 追加 1 条', `${appendLineMs.toFixed(2)} ms / 次(最快一次 ${appendLineFastestMs.toFixed(2)} ms)· 同一场景整份重排 ${appendLineRefreshMs.toFixed(1)} ms`)
|
|
348
|
+
for (const id of appendLineIds) {
|
|
349
|
+
big.removeLine(id)
|
|
350
|
+
}
|
|
351
|
+
big.updateOptions({ channels: [] })
|
|
325
352
|
|
|
326
353
|
/* ---------------- 分区(分块)渲染值不值得:视口变化时一帧的成本怎么变 ---------------- */
|
|
327
354
|
// 「把地图拆成一块一块、只画视口里的块」省的是「视口外那些实例的顶点变换」。
|
|
@@ -2323,7 +2350,7 @@
|
|
|
2323
2350
|
await sleep(30)
|
|
2324
2351
|
const selectedAfter = clusterView.getSelections().map(entry => entry.item?.id ?? entry.index).join(',')
|
|
2325
2352
|
check('点聚合:点击聚合只发 clusterClick,不发普通 click,选中集不变',
|
|
2326
|
-
clusterEvents === 1 && plainEvents === 0 && selectedAfter === selectedBefore && lastClusterPayload?.action?.type === 'clusterClick' && lastClusterPayload?.
|
|
2353
|
+
clusterEvents === 1 && plainEvents === 0 && selectedAfter === selectedBefore && lastClusterPayload?.action?.type === 'clusterClick' && lastClusterPayload?.type === 'cluster' && lastClusterPayload?.count > 1 && Array.isArray(lastClusterPayload?.pointIds) && lastClusterPayload?.action?.data === undefined && lastClusterPayload?.originalEvent instanceof Event,
|
|
2327
2354
|
`clusterClick=${clusterEvents} click=${plainEvents} 选中 ${selectedBefore} → ${selectedAfter} action=${lastClusterPayload?.action?.type} clickPick=${clickPick?.type ?? 'null'} clickScreen=${clickScreen.map(value => value.toFixed(1)).join(',')} client=${clickClient.map(value => value.toFixed(1)).join(',')}`)
|
|
2328
2355
|
const fitScale = clusterView.getCamera().scale
|
|
2329
2356
|
// 聚合成员在相机运动期会推迟重算;这一档没有线、又显式关掉非 MSAA 后备缓冲,
|
|
@@ -497,14 +497,19 @@
|
|
|
497
497
|
check('onPointerDown / onPointerUp / onClick(通道回线):每个鼠标载荷都带 groups 与当前 group',
|
|
498
498
|
[gpDowns.at(-1), gpUps.at(-1), gpClicks.at(-1)].every(payload => payload?.groups?.length === 2 && String(payload.group?.id) === String(payload.groupId) && payload.action?.target === 'channel'),
|
|
499
499
|
JSON.stringify({ down: gpDowns.at(-1)?.groups?.length, up: gpUps.at(-1)?.groups?.length, click: gpClicks.at(-1)?.groups?.length }))
|
|
500
|
+
const gpLineChannelFields = ['channelId', 'channelIndex', 'channel', 'groupId', 'groupIndex', 'group', 'groups', 'lineIndexes', 'lineIds', 'channelContexts']
|
|
501
|
+
const gpSelectedLineIndexes = payload => (payload?.items ?? []).filter(item => item.type === 'line').map(item => item.index).join(',')
|
|
502
|
+
const gpSelectedLineIds = payload => (payload?.items ?? []).filter(item => item.type === 'line').map(item => item.item?.id).join(',')
|
|
500
503
|
const gpChannelPayloadOK = payload => String(payload?.channelId) === 'gp-ch' &&
|
|
501
504
|
payload?.groups?.length === 2 &&
|
|
502
505
|
payload?.channelContexts === undefined &&
|
|
503
506
|
String(payload.group?.id) === String(payload.groupId) &&
|
|
504
|
-
payload.
|
|
505
|
-
|
|
507
|
+
payload.lineIndexes?.join(',') === gpSelectedLineIndexes(payload) &&
|
|
508
|
+
payload.lineIds?.join(',') === gpSelectedLineIds(payload) &&
|
|
509
|
+
payload.items?.every(item => item.type !== 'line' || gpLineChannelFields.every(key => item[key] === undefined))
|
|
510
|
+
check('onSelect / onSelectChange(通道回线):单通道上下文只在顶层,items 不重复挂任何通道字段',
|
|
506
511
|
gpChannelPayloadOK(gpSelects.at(-1)) && gpChannelPayloadOK(gpChanges.at(-1)) && gpSelects.at(-1)?.items === gpChanges.at(-1)?.items,
|
|
507
|
-
JSON.stringify({ select: gpSelects.at(-1)?.channelId, change: gpChanges.at(-1)?.channelId, groups: gpSelects.at(-1)?.groups?.length,
|
|
512
|
+
JSON.stringify({ select: gpSelects.at(-1)?.channelId, change: gpChanges.at(-1)?.channelId, groups: gpSelects.at(-1)?.groups?.length, lineIds: gpSelects.at(-1)?.lineIds, items: gpSelects.at(-1)?.items?.map(item => ({ id: item.item?.id, channelId: item.channelId, groupId: item.groupId })) }))
|
|
508
513
|
check('真实点击的派发顺序:onClick → onSelect → onSelectChange',
|
|
509
514
|
gpOrder.slice(-3).join('>') === 'click>select>selectChange',
|
|
510
515
|
gpOrder.join('>'))
|
|
@@ -513,8 +518,18 @@
|
|
|
513
518
|
gpOrder.length = 0
|
|
514
519
|
gpView.selectFeatures({ type: 'channel', channelId: 'gp-ch' })
|
|
515
520
|
check('selectFeatures(整条通道):onSelect / onSelectChange 都带通道上下文,顺序是 onSelect → onSelectChange',
|
|
516
|
-
gpOrder.join('>') === 'select>selectChange' && gpChannelPayloadOK(gpSelects.at(-1)) && gpChannelPayloadOK(gpChanges.at(-1)) && gpSelects.at(-1)?.items?.length === 3,
|
|
521
|
+
gpOrder.join('>') === 'select>selectChange' && gpChannelPayloadOK(gpSelects.at(-1)) && gpChannelPayloadOK(gpChanges.at(-1)) && gpSelects.at(-1)?.items?.length === 3 && gpSelects.at(-1)?.lineIds?.join(',') === 'gp-l1,gp-l2,gp-l3',
|
|
517
522
|
JSON.stringify({ order: gpOrder, select: gpSelects.at(-1), change: gpChanges.at(-1) }))
|
|
523
|
+
const gpSelectionLines = gpView.getSelections().filter(entry => entry.type === 'line')
|
|
524
|
+
const gpOwner = gpView.getLineChannel(gpSelectionLines[0]?.index)
|
|
525
|
+
const gpSelectionData = gpView.getSelectionData()
|
|
526
|
+
check('getSelections / getSelectionData / getLineChannel:选中镜像不带通道字段,单条线路仍能反查通道与分组',
|
|
527
|
+
gpSelectionLines.length === 3 &&
|
|
528
|
+
gpSelectionLines.every(entry => gpLineChannelFields.every(key => entry[key] === undefined)) &&
|
|
529
|
+
gpSelectionData.items.every(entry => entry.type !== 'line' || gpLineChannelFields.every(key => entry[key] === undefined)) &&
|
|
530
|
+
gpSelectionData.channels.map(channel => String(channel.id)).join(',') === 'gp-ch' &&
|
|
531
|
+
String(gpOwner?.channelId) === 'gp-ch' && String(gpOwner?.channel?.id) === 'gp-ch' && gpOwner?.channelIndex === 0 && String(gpOwner?.group?.id) === 'gp-g1',
|
|
532
|
+
JSON.stringify({ lines: gpSelectionLines.length, owner: gpOwner && { channelId: gpOwner.channelId, channelIndex: gpOwner.channelIndex, groupId: gpOwner.groupId }, channels: gpSelectionData.channels.map(channel => channel.id) }))
|
|
518
533
|
gpOrder.length = 0
|
|
519
534
|
gpView.selectFeatures([
|
|
520
535
|
{ type: 'channel', channelId: 'gp-ch' },
|
|
@@ -529,7 +544,7 @@
|
|
|
529
544
|
gpSelects.at(-1).channelContexts.map(context => `${context.lineIndexes.length}/${context.lineIds.length}`).join(',') === '3/3,1/1' &&
|
|
530
545
|
gpSelects.at(-1).channelContexts.map(context => context.lineIds.join('+')).join(',') === 'gp-l1+gp-l2+gp-l3,gp-l4' &&
|
|
531
546
|
gpSelects.at(-1).items === gpChanges.at(-1)?.items &&
|
|
532
|
-
gpSelects.at(-1).items.every(item => item.type !== 'line' || (
|
|
547
|
+
gpSelects.at(-1).items.every(item => item.type !== 'line' || gpLineChannelFields.every(key => item[key] === undefined)),
|
|
533
548
|
JSON.stringify({ order: gpOrder, contexts: gpSelects.at(-1)?.channelContexts?.map(context => ({ channelId: context.channelId, lines: context.lineIds })), items: gpSelects.at(-1)?.items?.length }))
|
|
534
549
|
gpOrder.length = 0
|
|
535
550
|
const gpAt = gpView.selectAt(gpClientX, gpClientY)
|
|
@@ -85,20 +85,16 @@ export const createPickView = (target, options = {}) => {
|
|
|
85
85
|
/** 点优先于线:两个都命中也算点中节点 */
|
|
86
86
|
const pick = (x, y) => pickPoint(x, y) ?? pickLine(x, y)
|
|
87
87
|
|
|
88
|
-
/** 统一的指针载荷:命中的元素 +
|
|
88
|
+
/** 统一的指针载荷:命中的元素 + 光标位置(空白处 type 为 null、hit 为 false;按键读 originalEvent) */
|
|
89
89
|
const pointerPayload = (hit, point, event) => ({
|
|
90
90
|
type: hit ? hit.type : null,
|
|
91
91
|
index: hit ? hit.index : -1,
|
|
92
92
|
item: hit ? hit.item : null,
|
|
93
93
|
hit: Boolean(hit),
|
|
94
94
|
...(hit ?? {}),
|
|
95
|
-
button: event.button,
|
|
96
|
-
buttons: event.buttons,
|
|
97
95
|
world: core.unproject(point[0], point[1]),
|
|
98
96
|
x: point[0],
|
|
99
97
|
y: point[1],
|
|
100
|
-
clientX: event.clientX,
|
|
101
|
-
clientY: event.clientY,
|
|
102
98
|
originalEvent: event
|
|
103
99
|
})
|
|
104
100
|
const canvasPointOf = event => {
|
|
@@ -180,7 +176,7 @@ export const createPickView = (target, options = {}) => {
|
|
|
180
176
|
const point = canvasPointOf(event)
|
|
181
177
|
const hit = pick(point[0], point[1])
|
|
182
178
|
// 左键 / 中键 / 右键都发一条:使用方在按下这一刻就能拿到光标下的元素
|
|
183
|
-
listeners.onPointerDown?.(pointerPayload(hit, point, event)
|
|
179
|
+
listeners.onPointerDown?.(pointerPayload(hit, point, event))
|
|
184
180
|
if (event.button !== 0) {
|
|
185
181
|
return
|
|
186
182
|
}
|
|
@@ -215,7 +211,7 @@ export const createPickView = (target, options = {}) => {
|
|
|
215
211
|
const point = canvasPointOf(event)
|
|
216
212
|
const hit = pick(point[0], point[1])
|
|
217
213
|
if (event.button === 0 || event.button === 2) {
|
|
218
|
-
listeners.onPointerUp?.(pointerPayload(hit, point, event)
|
|
214
|
+
listeners.onPointerUp?.(pointerPayload(hit, point, event))
|
|
219
215
|
}
|
|
220
216
|
const active = pressed
|
|
221
217
|
pressed = null
|
|
@@ -253,7 +249,7 @@ export const createPickView = (target, options = {}) => {
|
|
|
253
249
|
/** 右键:点、线、空白都发一条 onContextMenu,命中什么都在载荷里 */
|
|
254
250
|
const handleContextMenu = event => {
|
|
255
251
|
const point = canvasPointOf(event)
|
|
256
|
-
listeners.onContextMenu?.(pointerPayload(pick(point[0], point[1]), point, event)
|
|
252
|
+
listeners.onContextMenu?.(pointerPayload(pick(point[0], point[1]), point, event))
|
|
257
253
|
}
|
|
258
254
|
|
|
259
255
|
// 框选矩形:一个绝对定位的 DOM 层,样式由使用方决定(组件不内置界面)
|
|
@@ -221,9 +221,9 @@ export const createChannelView = (target, options = {}) => {
|
|
|
221
221
|
onClick: payload => listeners.onClick?.(channelize(payload)),
|
|
222
222
|
onDblClick: payload => listeners.onDblClick?.(channelize(payload)),
|
|
223
223
|
onSelect: payload => listeners.onSelect?.(payload ? { ...payload, items: payload.items.map(channelize).filter(Boolean) } : null),
|
|
224
|
-
onPointerDown:
|
|
225
|
-
onPointerUp:
|
|
226
|
-
onContextMenu:
|
|
224
|
+
onPointerDown: payload => listeners.onPointerDown?.(channelize(payload)),
|
|
225
|
+
onPointerUp: payload => listeners.onPointerUp?.(channelize(payload)),
|
|
226
|
+
onContextMenu: payload => listeners.onContextMenu?.(channelize(payload))
|
|
227
227
|
})
|
|
228
228
|
|
|
229
229
|
// ---------- 新增通道:草稿状态机 ----------
|
|
@@ -301,8 +301,6 @@ export const createChannelView = (target, options = {}) => {
|
|
|
301
301
|
x: point[0],
|
|
302
302
|
y: point[1],
|
|
303
303
|
world: core.unproject(point[0], point[1]),
|
|
304
|
-
clientX: event.clientX,
|
|
305
|
-
clientY: event.clientY,
|
|
306
304
|
originalEvent: event
|
|
307
305
|
})
|
|
308
306
|
|
|
@@ -130,9 +130,9 @@ export const createLabelView = (target, options = {}) => {
|
|
|
130
130
|
renderTooltip(payload, payload ? payload.screen : null)
|
|
131
131
|
listeners.onHover?.(payload)
|
|
132
132
|
},
|
|
133
|
-
onPointerDown:
|
|
134
|
-
onPointerUp:
|
|
135
|
-
onContextMenu:
|
|
133
|
+
onPointerDown: payload => listeners.onPointerDown?.(payload),
|
|
134
|
+
onPointerUp: payload => listeners.onPointerUp?.(payload),
|
|
135
|
+
onContextMenu: payload => listeners.onContextMenu?.(payload)
|
|
136
136
|
})
|
|
137
137
|
canvas.addEventListener('pointermove', handlePointerMove)
|
|
138
138
|
// 每帧同步一次标签:注册进第 1 步的渲染任务表,画的顺序在 WebGL 之后,谁都不挡谁
|
|
@@ -82,9 +82,9 @@ export const createFullView = (target, options = {}) => {
|
|
|
82
82
|
onClick: payload => forward.onClick?.(payload),
|
|
83
83
|
onDblClick: payload => forward.onDblClick?.(payload),
|
|
84
84
|
onSelect: payload => forward.onSelect?.(payload),
|
|
85
|
-
onPointerDown:
|
|
86
|
-
onPointerUp:
|
|
87
|
-
onContextMenu:
|
|
85
|
+
onPointerDown: payload => forward.onPointerDown?.(payload),
|
|
86
|
+
onPointerUp: payload => forward.onPointerUp?.(payload),
|
|
87
|
+
onContextMenu: payload => forward.onContextMenu?.(payload),
|
|
88
88
|
onChannelDraftClick: payload => forward.onChannelDraftClick?.(payload),
|
|
89
89
|
onChannelDraft: payload => {
|
|
90
90
|
if (payload?.phase === 'commit' && payload.lineId) {
|
|
@@ -9,21 +9,20 @@
|
|
|
9
9
|
* (select 选中 / clearSelection 点空白 / addWaypoint 加点 / contextMenu / dblClick);
|
|
10
10
|
* - action.target:点在了什么上面(point / line / channel / empty)。通道回线的 type 也是 line,
|
|
11
11
|
* 但要按通道处理,所以 target 单独给 channel;
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
14
|
-
* -
|
|
15
|
-
* action
|
|
16
|
-
* - action.
|
|
17
|
-
* action.press(**按下时的意图**,与那一刻 onPointerDown 的 action.type 同一份)、action.duration
|
|
12
|
+
* - 命中的数据直接读载荷:节点 / 线路读 payload.item、通道回线读 payload.channel、选中集读
|
|
13
|
+
* payload.items、拐点读 payload.line,不再另打包一份 action.data;
|
|
14
|
+
* - 按键 / 修饰键 / 指针位置读 payload.originalEvent(button / ctrlKey / metaKey / shiftKey / altKey /
|
|
15
|
+
* clientX / clientY),action 只留 type / target / moved / double 这些判读;
|
|
16
|
+
* - 松开与点击载荷另带 action.press(**按下时的意图**,与那一刻 onPointerDown 的 action.type 同一份)、action.duration
|
|
18
17
|
* (按了多久,毫秒)与 action.longPress(超过 500ms 且中间没拖动过 —— 触摸 / 触控笔按它出菜单)。
|
|
19
|
-
* 中键按住就是临时平移:按下 / 松开的 action.type 都是 pan、
|
|
18
|
+
* 中键按住就是临时平移:按下 / 松开的 action.type 都是 pan、originalEvent.button 是 1,不经过 onClick。
|
|
20
19
|
*
|
|
21
20
|
* 于是「按右键弹哪一份菜单」就是 switch (payload.action.target),「拖完的是节点还是拐点」就是
|
|
22
21
|
* switch (payload.action.type),不用再各记一套 payload 形状。选中集变更也有同一套 action.type:
|
|
23
22
|
* boxSelect / toggleSelect(Ctrl 点击取反)/ select / clearSelection —— 框选没框到东西与
|
|
24
23
|
* 「点空白清空」在载荷上是同一个形状(都是 null),只有这一份补充读数能把它们分开。
|
|
25
24
|
* 标签事件(labelClick / labelDrag / labelContextMenu / labelDblClick)同一套写法:
|
|
26
|
-
* target 恒为 'label'
|
|
25
|
+
* target 恒为 'label'、标签与归属直接读载荷顶层的 label / parent。
|
|
27
26
|
*
|
|
28
27
|
* 两个只读查询:想主动问「这儿是什么」而不必先订阅一遍事件再把最近一次载荷记住,用
|
|
29
28
|
* - pickAt(clientX, clientY):参数就是鼠标事件里的 clientX / clientY,返回与指针载荷同一份结果
|
|
@@ -59,7 +58,21 @@ const withChannels = (data) => {
|
|
|
59
58
|
/** action.target -> 中文:日志里读起来不用记英文名 */
|
|
60
59
|
const TARGET_NAMES = { point: '节点', line: '线路', channel: '通道回线', label: '标签', waypoint: '拐点', selection: '选中集', empty: '空白' }
|
|
61
60
|
|
|
62
|
-
/** action.
|
|
61
|
+
/** 按 action.target 取载荷上那一份数据:节点 / 线路读 item、通道读 channel、选中集读 items */
|
|
62
|
+
const dataOfAction = (payload, action) => {
|
|
63
|
+
if (Array.isArray(payload?.items)) {
|
|
64
|
+
return payload.items
|
|
65
|
+
}
|
|
66
|
+
if (payload?.label) {
|
|
67
|
+
return payload.label
|
|
68
|
+
}
|
|
69
|
+
if (action?.target === 'waypoint') {
|
|
70
|
+
return payload.line ?? null
|
|
71
|
+
}
|
|
72
|
+
return payload?.channel ?? payload?.item ?? null
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** 数据那一份的名字:节点 / 线路看 name 与 id,选中集与标签给条数或内容 */
|
|
63
76
|
const nameOfData = (data) => {
|
|
64
77
|
if (Array.isArray(data)) {
|
|
65
78
|
return `${data.length} 个要素`
|
|
@@ -67,10 +80,6 @@ const nameOfData = (data) => {
|
|
|
67
80
|
if (!data || typeof data !== 'object') {
|
|
68
81
|
return ''
|
|
69
82
|
}
|
|
70
|
-
if (data.label) {
|
|
71
|
-
// 标签事件的 data 是 { label, parent }
|
|
72
|
-
return data.label.text ?? data.label.id ?? '标签'
|
|
73
|
-
}
|
|
74
83
|
return data.name ?? data.id ?? ''
|
|
75
84
|
}
|
|
76
85
|
|
|
@@ -81,19 +90,27 @@ const describeAction = (payload, phase) => {
|
|
|
81
90
|
return `${phase}:—`
|
|
82
91
|
}
|
|
83
92
|
const parts = [action.type, `→ ${TARGET_NAMES[action.target] ?? action.target}`]
|
|
84
|
-
const name = nameOfData(action
|
|
93
|
+
const name = nameOfData(dataOfAction(payload, action))
|
|
85
94
|
if (name !== '') {
|
|
86
95
|
parts.push(String(name))
|
|
87
96
|
}
|
|
88
|
-
|
|
97
|
+
const button = payload.originalEvent?.button
|
|
98
|
+
if (button === 0 || button === 1 || button === 2) {
|
|
99
|
+
parts.push(button === 2 ? '右键' : button === 1 ? '中键' : '左键')
|
|
100
|
+
}
|
|
89
101
|
if (action.moved) {
|
|
90
102
|
parts.push('拖动过')
|
|
91
103
|
}
|
|
92
104
|
if (action.double) {
|
|
93
105
|
parts.push('双击的第二下')
|
|
94
106
|
}
|
|
95
|
-
const
|
|
96
|
-
const held =
|
|
107
|
+
const event = payload.originalEvent
|
|
108
|
+
const held = [
|
|
109
|
+
event?.ctrlKey ? 'ctrl' : null,
|
|
110
|
+
event?.metaKey ? 'meta' : null,
|
|
111
|
+
event?.shiftKey ? 'shift' : null,
|
|
112
|
+
event?.altKey ? 'alt' : null
|
|
113
|
+
].filter(Boolean).join('+')
|
|
97
114
|
if (held) {
|
|
98
115
|
parts.push(`按住 ${held}`)
|
|
99
116
|
}
|
|
@@ -121,13 +138,13 @@ export const createActionView = (target, options = {}) => {
|
|
|
121
138
|
waypointEditable: true,
|
|
122
139
|
features: { pan: true, zoom: true, hover: true, select: true, contextMenu: true, keyboard: true, channelDraft: true },
|
|
123
140
|
// 下面五条是同一条路:每个回调先报一句判读,再交给页面自己那一条(日志、草稿读数都还在)
|
|
124
|
-
onPointerDown:
|
|
141
|
+
onPointerDown: payload => {
|
|
125
142
|
report(describeAction(payload, '按下'))
|
|
126
|
-
options.onPointerDown?.(payload
|
|
143
|
+
options.onPointerDown?.(payload)
|
|
127
144
|
},
|
|
128
|
-
onPointerUp:
|
|
145
|
+
onPointerUp: payload => {
|
|
129
146
|
report(describeAction(payload, '松开'))
|
|
130
|
-
options.onPointerUp?.(payload
|
|
147
|
+
options.onPointerUp?.(payload)
|
|
131
148
|
},
|
|
132
149
|
onClick: payload => {
|
|
133
150
|
// 空白处的点击载荷是 null(选中已经清空):这一下的判读在 onSelectChange 那条路上
|
|
@@ -138,13 +155,13 @@ export const createActionView = (target, options = {}) => {
|
|
|
138
155
|
report(describeAction(payload, '双击'))
|
|
139
156
|
options.onDblClick?.(payload)
|
|
140
157
|
},
|
|
141
|
-
onContextMenu:
|
|
158
|
+
onContextMenu: payload => {
|
|
142
159
|
report(describeAction(payload, '右键'))
|
|
143
|
-
options.onContextMenu?.(payload
|
|
160
|
+
options.onContextMenu?.(payload)
|
|
144
161
|
},
|
|
145
162
|
// 选中集变更:action.type 把框选 / 取反 / 选中 / 清空分开(前两种不经过 onClick)
|
|
146
163
|
onSelectChange: event => {
|
|
147
|
-
report(`选中:${event.action.type} · ${event.count} 个(节点 ${event.points} / 线路 ${event.lines} / 通道 ${event.channels}
|
|
164
|
+
report(`选中:${event.action.type} · ${event.count} 个(节点 ${event.points} / 线路 ${event.lines} / 通道 ${event.channels})→ ${event.points ? '节点' : event.lines ? '线路' : event.channels ? '通道' : '空白'}`)
|
|
148
165
|
options.onSelectChange?.(event)
|
|
149
166
|
},
|
|
150
167
|
onCameraChange: camera => options.onCameraChange?.(camera)
|