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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<div id="map" style="width:640px;height:400px"></div>
|
|
9
9
|
<pre id="out"></pre>
|
|
10
10
|
<script type="module">
|
|
11
|
-
// 这一页的自测:① 多选开关(options.multiSelect)—— 打开后不用修饰键、单击即取反累加;
|
|
11
|
+
// 这一页的自测:① 多选开关(options.multiSelect)—— 打开后不用修饰键、单击即取反累加;toggleSelect 另测;
|
|
12
12
|
// ② 状态样式里的阴影(selectStyle / lineSelectStyle 写 shadow 要画得出来、取消选中要收掉);
|
|
13
13
|
// ③ 取消选中 / 改变选中集之后颜色与阴影要自动恢复,不必等悬停来顺手重解析;
|
|
14
14
|
// ④ 同配方不同尺寸的贴图符号:借来的占位贴图不能被当成「自己烤好的那一份」(见问题 4)。
|
|
@@ -371,6 +371,127 @@
|
|
|
371
371
|
epView.destroy()
|
|
372
372
|
epHost.remove()
|
|
373
373
|
|
|
374
|
+
/* ---------- 本轮:框选保留、整框拖动、DOM 样式与手动关闭 ---------- */
|
|
375
|
+
const persistHost = document.createElement('div')
|
|
376
|
+
persistHost.style.cssText = 'width:400px;height:260px'
|
|
377
|
+
document.body.append(persistHost)
|
|
378
|
+
const persistBoxStyle = { render: 'div', className: 'persist-box', borderStyle: 'dashed', strokeWidth: 3, radius: 5, fill: '#eef4ff', fillOpacity: 0.25 }
|
|
379
|
+
const persistView = createGisView(persistHost, {
|
|
380
|
+
points: [
|
|
381
|
+
{ id: 'pv-a', x: 0, y: 0, size: 22 },
|
|
382
|
+
{ id: 'pv-b', x: 100, y: 0, size: 22 }
|
|
383
|
+
],
|
|
384
|
+
lines: [
|
|
385
|
+
{ id: 'pv-line', from: 'pv-a', to: 'pv-b', width: 5 },
|
|
386
|
+
{ id: 'pv-ch-line', from: 'pv-a', to: 'pv-b', width: 5 }
|
|
387
|
+
],
|
|
388
|
+
channels: [{ id: 'pv-ch', from: 'pv-a', to: 'pv-b', lineIds: ['pv-ch-line'], width: 36 }],
|
|
389
|
+
features: 'edit',
|
|
390
|
+
selectionKinds: ['point'],
|
|
391
|
+
selectionBoxPersistent: true,
|
|
392
|
+
selectionBoxStyle: persistBoxStyle,
|
|
393
|
+
showGrid: false,
|
|
394
|
+
fitOnInit: false,
|
|
395
|
+
initialView: { center: [50, 0], scale: 1 },
|
|
396
|
+
width: 400,
|
|
397
|
+
height: 260
|
|
398
|
+
})
|
|
399
|
+
await settle(120)
|
|
400
|
+
const persistPointBox = () => {
|
|
401
|
+
const data = persistView.getData().points
|
|
402
|
+
const left = persistView.project(data.find(point => point.id === 'pv-a').x, data.find(point => point.id === 'pv-a').y)
|
|
403
|
+
const right = persistView.project(data.find(point => point.id === 'pv-b').x, data.find(point => point.id === 'pv-b').y)
|
|
404
|
+
const x = Math.min(left[0], right[0]) - 34
|
|
405
|
+
const y = Math.min(left[1], right[1]) - 34
|
|
406
|
+
return { x, y, width: Math.abs(right[0] - left[0]) + 68, height: 68 }
|
|
407
|
+
}
|
|
408
|
+
persistView.selectInBox(persistPointBox(), { selectionKinds: ['point'] })
|
|
409
|
+
const retainedBox = persistView.getSelectionBox()
|
|
410
|
+
const retainedElement = persistHost.querySelector('.gis-view__selection-box.persist-box')
|
|
411
|
+
const persistIds = () => persistView.getSelectionData().ids.join(',')
|
|
412
|
+
const initialBoxBorderStyle = retainedElement?.style.getPropertyValue('--gis-view-selection-box-border-style').trim() ?? ''
|
|
413
|
+
persistView.updateOptions({ selectionBoxStyle: { ...persistBoxStyle, className: 'persist-box-live', borderStyle: 'dotted' } })
|
|
414
|
+
const liveElement = persistHost.querySelector('.gis-view__selection-box.persist-box-live')
|
|
415
|
+
check('selectionBoxPersistent:框选结束后保留矩形,选中集仍是框内节点', Boolean(retainedBox) && persistView.getSelectionData().pointCount === 2, JSON.stringify({ box: retainedBox, ids: persistIds() }))
|
|
416
|
+
check('selectionBoxStyle.render: div:挂载 DOM 矩形并加自定义类名,样式可运行时替换', Boolean(retainedElement) && initialBoxBorderStyle === 'dashed' && Boolean(liveElement) && liveElement.style.getPropertyValue('--gis-view-selection-box-border-style').trim() === 'dotted' && Boolean(retainedBox), retainedElement?.className ?? '没有 DOM 框')
|
|
417
|
+
const persistLineBefore = persistView.getLine('pv-line')?.path ?? []
|
|
418
|
+
const persistChannelLineBefore = persistView.getLine('pv-ch-line')?.path ?? []
|
|
419
|
+
const persistChannelBefore = persistView.getChannel('pv-ch')?.skeleton ?? []
|
|
420
|
+
const persistPointsBefore = new Map(persistView.getData().points.map(point => [point.id, [point.x, point.y]]))
|
|
421
|
+
const dragStart = [retainedBox.x + retainedBox.width / 2, retainedBox.y + retainedBox.height / 2]
|
|
422
|
+
const dragDelta = [36, 24]
|
|
423
|
+
const dragWorldDelta = [dragDelta[0], -dragDelta[1]]
|
|
424
|
+
const persistRect = persistView.canvas.getBoundingClientRect()
|
|
425
|
+
const persistPointer = (id, type, at, buttons) => {
|
|
426
|
+
persistView.canvas.dispatchEvent(new PointerEvent(type, {
|
|
427
|
+
bubbles: true,
|
|
428
|
+
cancelable: true,
|
|
429
|
+
pointerId: id,
|
|
430
|
+
pointerType: 'mouse',
|
|
431
|
+
button: 0,
|
|
432
|
+
buttons,
|
|
433
|
+
clientX: persistRect.left + at[0],
|
|
434
|
+
clientY: persistRect.top + at[1]
|
|
435
|
+
}))
|
|
436
|
+
}
|
|
437
|
+
const persistDragId = pointerId += 1
|
|
438
|
+
persistPointer(persistDragId, 'pointerdown', dragStart, 1)
|
|
439
|
+
persistPointer(persistDragId, 'pointermove', [dragStart[0] + dragDelta[0] / 2, dragStart[1] + dragDelta[1] / 2], 1)
|
|
440
|
+
persistPointer(persistDragId, 'pointermove', [dragStart[0] + dragDelta[0], dragStart[1] + dragDelta[1]], 1)
|
|
441
|
+
persistPointer(persistDragId, 'pointerup', [dragStart[0] + dragDelta[0], dragStart[1] + dragDelta[1]], 0)
|
|
442
|
+
await settle(120)
|
|
443
|
+
const movedBox = persistView.getSelectionBox()
|
|
444
|
+
const persistedPoints = new Map(persistView.getData().points.map(point => [point.id, [point.x, point.y]]))
|
|
445
|
+
const near = (left, right) => Math.abs(left - right) <= 0.75
|
|
446
|
+
check('保留框拖动:框按屏幕增量整体平移,宽高不变', Boolean(movedBox) && near(movedBox.x, retainedBox.x + dragDelta[0]) && near(movedBox.y, retainedBox.y + dragDelta[1]) && near(movedBox.width, retainedBox.width) && near(movedBox.height, retainedBox.height), JSON.stringify({ before: retainedBox, after: movedBox }))
|
|
447
|
+
check('保留框拖动:框内两个节点一起按同一世界增量移动,选中集不变', ['pv-a', 'pv-b'].every(id => near(persistedPoints.get(id)[0], persistPointsBefore.get(id)[0] + dragWorldDelta[0]) && near(persistedPoints.get(id)[1], persistPointsBefore.get(id)[1] + dragWorldDelta[1])) && persistView.getSelectionData().pointCount === 2, JSON.stringify({ before: [...persistPointsBefore], after: [...persistedPoints], expected: dragWorldDelta }))
|
|
448
|
+
const persistLineMoved = persistView.getLine('pv-line')?.path ?? []
|
|
449
|
+
const persistChannelLineMoved = persistView.getLine('pv-ch-line')?.path ?? []
|
|
450
|
+
const persistChannelMoved = persistView.getChannel('pv-ch')?.skeleton ?? []
|
|
451
|
+
const shifted = (before, after) => before.length > 1 && after.length === before.length && after.every((point, index) => near(point[0], before[index][0] + dragWorldDelta[0]) && near(point[1], before[index][1] + dragWorldDelta[1]))
|
|
452
|
+
check('保留框拖动:独立线路、通道成员回线与通道骨架都跟着节点更新', shifted(persistLineBefore, persistLineMoved) && shifted(persistChannelLineBefore, persistChannelLineMoved) && shifted(persistChannelBefore, persistChannelMoved), JSON.stringify({ line: [persistLineBefore[0], persistLineMoved[0]], channel: [persistChannelBefore[0], persistChannelMoved[0]] }))
|
|
453
|
+
// 相机移动:div 框按世界锚点重新投影,和 canvas 顶点在同一帧走到新位置
|
|
454
|
+
const cameraBeforeBoxMove = persistView.getCamera()
|
|
455
|
+
const boxBeforeCameraMove = persistView.getSelectionBox()
|
|
456
|
+
const worldPan = [46, 22]
|
|
457
|
+
// 相机中心向 +x / +y 移动时,固定在世界坐标里的框在屏幕上向反方向走
|
|
458
|
+
const expectedPan = [-worldPan[0] * cameraBeforeBoxMove.scale, worldPan[1] * cameraBeforeBoxMove.scale]
|
|
459
|
+
persistView.setCamera({ center: [cameraBeforeBoxMove.center[0] + worldPan[0], cameraBeforeBoxMove.center[1] + worldPan[1]] })
|
|
460
|
+
await settle(100)
|
|
461
|
+
const boxAfterPan = persistView.getSelectionBox()
|
|
462
|
+
const domAfterPan = persistHost.querySelector('.gis-view__selection-box.persist-box-live')
|
|
463
|
+
const canvasAfterPan = persistView.canvas.getBoundingClientRect()
|
|
464
|
+
const domRectAfterPan = domAfterPan?.getBoundingClientRect()
|
|
465
|
+
check('保留框(div):平移地图时框跟着世界坐标移动,DOM 与 getSelectionBox() 同步且宽高不变',
|
|
466
|
+
Boolean(boxBeforeCameraMove && boxAfterPan && domRectAfterPan) && near(boxAfterPan.x, boxBeforeCameraMove.x + expectedPan[0]) && near(boxAfterPan.y, boxBeforeCameraMove.y + expectedPan[1]) && near(boxAfterPan.width, boxBeforeCameraMove.width) && near(boxAfterPan.height, boxBeforeCameraMove.height) && Math.abs(domRectAfterPan.left - canvasAfterPan.left - boxAfterPan.x) <= 1 && Math.abs(domRectAfterPan.top - canvasAfterPan.top - boxAfterPan.y) <= 1,
|
|
467
|
+
JSON.stringify({ before: boxBeforeCameraMove, after: boxAfterPan, expected: [boxBeforeCameraMove.x + expectedPan[0], boxBeforeCameraMove.y + expectedPan[1]], dom: domRectAfterPan ? [domRectAfterPan.left, domRectAfterPan.top, domRectAfterPan.width, domRectAfterPan.height] : null, canvas: [canvasAfterPan.left, canvasAfterPan.top] }))
|
|
468
|
+
persistView.setCamera({ scale: cameraBeforeBoxMove.scale * 1.5 })
|
|
469
|
+
await settle(100)
|
|
470
|
+
const boxAfterZoom = persistView.getSelectionBox()
|
|
471
|
+
const domAfterZoom = persistHost.querySelector('.gis-view__selection-box.persist-box-live')
|
|
472
|
+
check('保留框(div):缩放地图时框宽高按相机比例变化,DOM 尺寸与读数一致',
|
|
473
|
+
Boolean(boxAfterPan && boxAfterZoom && domAfterZoom) && near(boxAfterZoom.width, boxAfterPan.width * 1.5) && near(boxAfterZoom.height, boxAfterPan.height * 1.5) && near(domAfterZoom.getBoundingClientRect().width, boxAfterZoom.width) && near(domAfterZoom.getBoundingClientRect().height, boxAfterZoom.height),
|
|
474
|
+
JSON.stringify({ pan: boxAfterPan, zoom: boxAfterZoom }))
|
|
475
|
+
persistView.setCamera(cameraBeforeBoxMove)
|
|
476
|
+
await settle(100)
|
|
477
|
+
const selectedBeforeManualClose = persistIds()
|
|
478
|
+
persistView.clearSelectionBox()
|
|
479
|
+
check('clearSelectionBox():手动收起矩形,不改选中集', persistView.getSelectionBox() === null && persistIds() === selectedBeforeManualClose, persistView.getSelectionBox() === null ? persistIds() : '框还在')
|
|
480
|
+
persistView.selectInBox(persistPointBox(), { selectionKinds: ['point'] })
|
|
481
|
+
const persistBlankId = pointerId += 1
|
|
482
|
+
persistPointer(persistBlankId, 'pointerdown', [18, 18], 1)
|
|
483
|
+
persistPointer(persistBlankId, 'pointerup', [18, 18], 0)
|
|
484
|
+
await settle(80)
|
|
485
|
+
check('保留框:点击框外空白取消选中时自动收起', persistView.getSelectionBox() === null && persistView.getSelectionData().count === 0, JSON.stringify({ box: persistView.getSelectionBox(), ids: persistIds() }))
|
|
486
|
+
persistView.selectInBox(persistPointBox(), { selectionKinds: ['point'] })
|
|
487
|
+
persistView.selectFeatures('pv-a')
|
|
488
|
+
check('保留框:选中集变化时自动收起', persistView.getSelectionBox() === null && persistIds() === 'pv-a', JSON.stringify({ box: persistView.getSelectionBox(), ids: persistIds() }))
|
|
489
|
+
persistView.selectInBox(persistPointBox(), { selectionKinds: ['point'] })
|
|
490
|
+
persistView.updateOptions({ selectionBoxPersistent: false })
|
|
491
|
+
check('selectionBoxPersistent 运行时关闭:立即收起保留框', persistView.getSelectionBox() === null && persistView.getSelectionData().pointCount === 2, JSON.stringify({ box: persistView.getSelectionBox(), ids: persistIds() }))
|
|
492
|
+
persistView.destroy()
|
|
493
|
+
persistHost.remove()
|
|
494
|
+
|
|
374
495
|
/* ---------- 问题 1(本轮):接口取消选中 —— deselect(单个 / 一批 / 整条通道)与「取消全部」的老写法 ---------- */
|
|
375
496
|
const dzHost = document.createElement('div')
|
|
376
497
|
dzHost.style.cssText = 'width:320px;height:200px'
|
|
@@ -878,8 +999,189 @@
|
|
|
878
999
|
await triggerClick(0, 0)
|
|
879
1000
|
await triggerClick(0, 0)
|
|
880
1001
|
check('selectTrigger: dblClick + multiSelect:双击已选节点是取消', triggerIds() === 'trigger-b' && triggerClicks.at(-1)?.action?.type === 'toggleSelect', triggerIds())
|
|
1002
|
+
|
|
1003
|
+
/* toggleSelect:再次选中即取消,缺省关;单击 / 双击两种触发方式与 Ctrl 先选后点同一口径 */
|
|
1004
|
+
triggerView.updateOptions({ selectTrigger: 'click', multiSelect: false })
|
|
1005
|
+
triggerView.selectFeatures(null)
|
|
1006
|
+
await settle(360)
|
|
1007
|
+
triggerClicks.length = 0
|
|
1008
|
+
triggerDblClicks.length = 0
|
|
1009
|
+
triggerChanges.length = 0
|
|
1010
|
+
await triggerClick(0, 0)
|
|
1011
|
+
await settle(360)
|
|
1012
|
+
await triggerClick(0, 0)
|
|
1013
|
+
check('toggleSelect 缺省关:隔开两次普通单击仍保持选中', triggerIds() === 'trigger-a' && triggerChanges.length === 1 && triggerChanges.at(-1)?.action?.type === 'select', `${triggerIds()} / ${triggerChanges.at(-1)?.action?.type}`)
|
|
1014
|
+
|
|
1015
|
+
triggerView.updateOptions({ toggleSelect: true })
|
|
1016
|
+
await settle(360)
|
|
1017
|
+
const toggleOffClicks = triggerClicks.length
|
|
1018
|
+
const toggleOffChanges = triggerChanges.length
|
|
1019
|
+
await triggerClick(0, 0)
|
|
1020
|
+
check('toggleSelect:再次普通单击已选节点即取消,onClick 与 onSelectChange 都记 toggleSelect',
|
|
1021
|
+
triggerIds() === '' && triggerClicks.length === toggleOffClicks + 1 && triggerClicks.at(-1)?.action?.type === 'toggleSelect' &&
|
|
1022
|
+
triggerChanges.length === toggleOffChanges + 1 && triggerChanges.at(-1)?.action?.type === 'toggleSelect',
|
|
1023
|
+
`${triggerIds() || '空'} / ${triggerClicks.at(-1)?.action?.type} / ${triggerChanges.at(-1)?.action?.type}`)
|
|
1024
|
+
|
|
1025
|
+
triggerView.selectFeatures(null)
|
|
1026
|
+
await settle(360)
|
|
1027
|
+
triggerClicks.length = 0
|
|
1028
|
+
triggerChanges.length = 0
|
|
1029
|
+
await triggerClick(0, 0, { ctrlKey: true })
|
|
1030
|
+
const ctrlSelectedBeforePlainClick = triggerIds()
|
|
1031
|
+
await settle(360)
|
|
1032
|
+
await triggerClick(0, 0)
|
|
1033
|
+
check('toggleSelect:Ctrl 先选中后,普通单击同一节点即取消',
|
|
1034
|
+
ctrlSelectedBeforePlainClick === 'trigger-a' && triggerIds() === '' && triggerClicks.at(-1)?.action?.type === 'toggleSelect' && triggerChanges.at(-1)?.action?.type === 'toggleSelect',
|
|
1035
|
+
`${ctrlSelectedBeforePlainClick || '空'} → ${triggerIds() || '空'} / ${triggerClicks.at(-1)?.action?.type}`)
|
|
1036
|
+
|
|
1037
|
+
triggerView.selectFeatures(null)
|
|
1038
|
+
triggerView.updateOptions({ selectTrigger: 'dblClick' })
|
|
1039
|
+
await settle(360)
|
|
1040
|
+
triggerClicks.length = 0
|
|
1041
|
+
triggerDblClicks.length = 0
|
|
1042
|
+
triggerChanges.length = 0
|
|
1043
|
+
await triggerClick(0, 0)
|
|
1044
|
+
await triggerClick(0, 0)
|
|
1045
|
+
check('toggleSelect + selectTrigger: dblClick:双击选中只改一次选中集',
|
|
1046
|
+
triggerIds() === 'trigger-a' && triggerChanges.length === 1 && triggerChanges.at(-1)?.action?.type === 'select' && triggerClicks.length === 2 && triggerDblClicks.length === 1,
|
|
1047
|
+
`${triggerIds()} / change ${triggerChanges.length} / click ${triggerClicks.length} / dbl ${triggerDblClicks.length}`)
|
|
1048
|
+
await triggerClick(0, 0)
|
|
1049
|
+
await triggerClick(0, 0)
|
|
1050
|
+
check('toggleSelect + selectTrigger: dblClick:再次双击取消,且一轮双击只取反一次',
|
|
1051
|
+
triggerIds() === '' && triggerChanges.length === 2 && triggerChanges.at(-1)?.action?.type === 'toggleSelect' && triggerClicks.at(-1)?.action?.type === 'toggleSelect' && triggerClicks.length === 4 && triggerDblClicks.length === 2,
|
|
1052
|
+
`${triggerIds() || '空'} / change ${triggerChanges.length} / click ${triggerClicks.length} / dbl ${triggerDblClicks.length}`)
|
|
881
1053
|
triggerView.destroy()
|
|
882
1054
|
triggerHost.remove()
|
|
1055
|
+
|
|
1056
|
+
/* ---------- toggleSelect:独立线路 / 通道回线 / 通道分组 / 整条通道 ---------- */
|
|
1057
|
+
const toggleLineHost = document.createElement('div')
|
|
1058
|
+
toggleLineHost.style.cssText = 'width:520px;height:340px'
|
|
1059
|
+
document.body.append(toggleLineHost)
|
|
1060
|
+
const toggleLineClicks = []
|
|
1061
|
+
const toggleLineChanges = []
|
|
1062
|
+
const toggleLineView = createGisView(toggleLineHost, {
|
|
1063
|
+
points: [
|
|
1064
|
+
{ id: 'tl-a', x: 0, y: 0, size: 10 },
|
|
1065
|
+
{ id: 'tl-b', x: 200, y: 0, size: 10 },
|
|
1066
|
+
{ id: 'tl-c', x: 0, y: 80, size: 10 },
|
|
1067
|
+
{ id: 'tl-d', x: 200, y: 80, size: 10 }
|
|
1068
|
+
],
|
|
1069
|
+
lines: [
|
|
1070
|
+
{ id: 'tl-solo', path: [[0, -90], [200, -90]], width: 8 },
|
|
1071
|
+
{ id: 'tl-c1', from: 'tl-a', to: 'tl-b', width: 8 },
|
|
1072
|
+
{ id: 'tl-c2', from: 'tl-a', to: 'tl-b', width: 8 },
|
|
1073
|
+
{ id: 'tl-c3', from: 'tl-a', to: 'tl-b', width: 8 },
|
|
1074
|
+
{ id: 'tl-other', from: 'tl-c', to: 'tl-d', width: 8 }
|
|
1075
|
+
],
|
|
1076
|
+
channels: [
|
|
1077
|
+
{
|
|
1078
|
+
id: 'tl-ch',
|
|
1079
|
+
from: 'tl-a',
|
|
1080
|
+
to: 'tl-b',
|
|
1081
|
+
lineIds: ['tl-c1', 'tl-c2', 'tl-c3'],
|
|
1082
|
+
groups: [
|
|
1083
|
+
{ id: 'tl-g1', lineIds: ['tl-c1'] },
|
|
1084
|
+
{ id: 'tl-g2', lineIds: ['tl-c2', 'tl-c3'] }
|
|
1085
|
+
]
|
|
1086
|
+
},
|
|
1087
|
+
{ id: 'tl-ch2', from: 'tl-c', to: 'tl-d', lineIds: ['tl-other'] }
|
|
1088
|
+
],
|
|
1089
|
+
features: { select: true },
|
|
1090
|
+
toggleSelect: true,
|
|
1091
|
+
selectionKinds: 'all',
|
|
1092
|
+
showGrid: false,
|
|
1093
|
+
fitOnInit: false,
|
|
1094
|
+
initialView: { center: [100, 0], scale: 1 },
|
|
1095
|
+
width: 520,
|
|
1096
|
+
height: 340,
|
|
1097
|
+
onClick: payload => toggleLineClicks.push(payload),
|
|
1098
|
+
onSelectChange: payload => toggleLineChanges.push(payload)
|
|
1099
|
+
})
|
|
1100
|
+
await settle(120)
|
|
1101
|
+
const toggleLineIds = () => toggleLineView.getSelectionData().ids.map(String).join(',')
|
|
1102
|
+
const toggleLineTap = async (id) => {
|
|
1103
|
+
const path = toggleLineView.getLine(id)?.path ?? []
|
|
1104
|
+
const first = path[0]
|
|
1105
|
+
const last = path[path.length - 1]
|
|
1106
|
+
const middle = [(first[0] + last[0]) / 2, (first[1] + last[1]) / 2]
|
|
1107
|
+
const screen = toggleLineView.project(middle[0], middle[1])
|
|
1108
|
+
const bounds = toggleLineView.canvas.getBoundingClientRect()
|
|
1109
|
+
pointerId += 1
|
|
1110
|
+
const common = { bubbles: true, cancelable: true, pointerId, pointerType: 'mouse', button: 0, clientX: bounds.left + screen[0], clientY: bounds.top + screen[1] }
|
|
1111
|
+
toggleLineView.canvas.dispatchEvent(new PointerEvent('pointerdown', { ...common, buttons: 1 }))
|
|
1112
|
+
toggleLineView.canvas.dispatchEvent(new PointerEvent('pointerup', { ...common, buttons: 0 }))
|
|
1113
|
+
await settle(360)
|
|
1114
|
+
}
|
|
1115
|
+
const resetToggleLine = async (ids, channelSelectMode) => {
|
|
1116
|
+
toggleLineView.updateOptions({ channelSelectMode })
|
|
1117
|
+
toggleLineView.selectFeatures(ids)
|
|
1118
|
+
toggleLineClicks.length = 0
|
|
1119
|
+
toggleLineChanges.length = 0
|
|
1120
|
+
await settle(120)
|
|
1121
|
+
}
|
|
1122
|
+
await resetToggleLine(['tl-solo', 'tl-other'], 'line')
|
|
1123
|
+
await toggleLineTap('tl-solo')
|
|
1124
|
+
check('toggleSelect 独立线路:再次点击已选线路只取消它,其他选中项保留',
|
|
1125
|
+
toggleLineIds() === 'tl-other' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect' && toggleLineChanges.at(-1)?.action?.type === 'toggleSelect',
|
|
1126
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type} / ${toggleLineChanges.at(-1)?.action?.type}`)
|
|
1127
|
+
|
|
1128
|
+
await resetToggleLine(['tl-c1', 'tl-c2'], 'line')
|
|
1129
|
+
await toggleLineTap('tl-c1')
|
|
1130
|
+
check('toggleSelect + channelSelectMode: line:只取消命中的通道回线,同通道其他选中回线保留',
|
|
1131
|
+
toggleLineIds() === 'tl-c2' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1132
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1133
|
+
|
|
1134
|
+
await resetToggleLine(['tl-c1', 'tl-c2', 'tl-c3', 'tl-other'], 'line')
|
|
1135
|
+
await toggleLineTap('tl-c2')
|
|
1136
|
+
check('toggleSelect + channelSelectMode: line:已完整选中的通道仍整条取消,其他选中项保留',
|
|
1137
|
+
toggleLineIds() === 'tl-other' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1138
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1139
|
+
|
|
1140
|
+
await resetToggleLine(['tl-c2', 'tl-c3', 'tl-other'], 'line')
|
|
1141
|
+
await toggleLineTap('tl-c2')
|
|
1142
|
+
check('toggleSelect + channelSelectMode: line:已完整选中的分组整组取消,不会只删命中回线',
|
|
1143
|
+
toggleLineIds() === 'tl-other' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1144
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1145
|
+
|
|
1146
|
+
await resetToggleLine(['tl-c1', 'tl-c2', 'tl-c3'], 'group')
|
|
1147
|
+
await toggleLineTap('tl-c2')
|
|
1148
|
+
check('toggleSelect + channelSelectMode: group:取消命中的整组,其他分组不误伤',
|
|
1149
|
+
toggleLineIds() === 'tl-c1' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1150
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1151
|
+
|
|
1152
|
+
await resetToggleLine(['tl-c1', 'tl-c2'], 'group')
|
|
1153
|
+
await toggleLineTap('tl-c3')
|
|
1154
|
+
check('toggleSelect + channelSelectMode: group:分组只选中一部分时,点组内另一条也取消整组',
|
|
1155
|
+
toggleLineIds() === 'tl-c1' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1156
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1157
|
+
|
|
1158
|
+
await resetToggleLine(['tl-c1', 'tl-c2', 'tl-c3', 'tl-other'], 'channel')
|
|
1159
|
+
await toggleLineTap('tl-c3')
|
|
1160
|
+
check('toggleSelect + channelSelectMode: channel:取消命中的整条通道,其他通道不误伤',
|
|
1161
|
+
toggleLineIds() === 'tl-other' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1162
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1163
|
+
|
|
1164
|
+
await resetToggleLine(['tl-c1', 'tl-other'], 'channel')
|
|
1165
|
+
await toggleLineTap('tl-c2')
|
|
1166
|
+
check('toggleSelect + channelSelectMode: channel:通道只选中一部分时,点通道内另一条也取消整条',
|
|
1167
|
+
toggleLineIds() === 'tl-other' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1168
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1169
|
+
|
|
1170
|
+
// selectionKinds 只约束框选 / Ctrl 多选;普通单击的 toggleSelect 取消不受它限制(GisEdit 只开 point / lines 时仍要能收通道)
|
|
1171
|
+
toggleLineView.updateOptions({ selectionKinds: ['point', 'lines'] })
|
|
1172
|
+
await resetToggleLine(['tl-c1', 'tl-c2', 'tl-c3', 'tl-other'], 'channel')
|
|
1173
|
+
await toggleLineTap('tl-c2')
|
|
1174
|
+
check('toggleSelect 不受 selectionKinds 限制:配置未含 channel 时仍能整条取消通道',
|
|
1175
|
+
toggleLineIds() === 'tl-other' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1176
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1177
|
+
|
|
1178
|
+
await resetToggleLine(['tl-c2', 'tl-c3', 'tl-other'], 'line')
|
|
1179
|
+
await toggleLineTap('tl-c2')
|
|
1180
|
+
check('toggleSelect 不受 selectionKinds 限制:配置未含 channel 时仍能整组取消',
|
|
1181
|
+
toggleLineIds() === 'tl-other' && toggleLineClicks.at(-1)?.action?.type === 'toggleSelect',
|
|
1182
|
+
`${toggleLineIds() || '空'} / ${toggleLineClicks.at(-1)?.action?.type}`)
|
|
1183
|
+
toggleLineView.destroy()
|
|
1184
|
+
toggleLineHost.remove()
|
|
883
1185
|
}
|
|
884
1186
|
catch (error) {
|
|
885
1187
|
results.push('ERROR ' + (error && error.stack ? error.stack : error))
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
* 对应库里的 rules.js + condition.js,文档 5.1 / 5.2。
|
|
4
4
|
*
|
|
5
5
|
* 一条规则长这样:{ when: { field: 'voltage', op: '>=', value: 500 }, style: { color: '#e0592e' } }。
|
|
6
|
-
*
|
|
7
|
-
* 「状态样式(第 5 步)> 要素自身字段 > 批量规则 >
|
|
6
|
+
* 条件只编译一次、每条要素依次匹配、第一条命中的生效;这个最小教学版的样式优先级是
|
|
7
|
+
* 「状态样式(第 5 步)> 要素自身字段 > 批量规则 > 全局样式」。完整组件另有逐条 override 规则,
|
|
8
|
+
* 普通规则始终高于全局样式,override 规则写了的字段才压过要素自身字段。
|
|
8
9
|
*/
|
|
9
10
|
import { createPickView } from './step-05-pick.js'
|
|
10
11
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gis-view-webgl2",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "WebGL2 矢量视图:点、线与 GeoJSON /
|
|
3
|
+
"version": "0.1.13",
|
|
4
|
+
"description": "WebGL2 矢量视图:点、线与 GeoJSON / 图片背景层,可选鹰眼图与图例,点击 / 双击 / 拖拽可配置",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"dev": "vite",
|