gis-view-webgl2 0.1.2 → 0.1.3
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 +12 -8
- package/dist/gis-view.css +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +309 -59
- package/dist/index.js +1 -1
- package/docs/AI/345/257/271/350/257/235/346/261/207/346/200/273.md +117 -0
- package/docs/GisView.md +2 -2
- 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 +107 -0
- package/docs/api/index.html +1 -1
- package/docs/demos/ApiCoverageDemo.vue +644 -0
- package/docs/demos/ChannelDemo.vue +14 -12
- package/docs/demos/ClusterDemo.vue +251 -0
- package/docs/demos/CopyMergeDemo.vue +3 -4
- package/docs/demos/EventDemo.vue +3 -3
- package/docs/demos/GisViewDemo.vue +64 -8809
- package/docs/demos/api-index.js +48 -41
- package/docs/demos/demos.js +33 -16
- package/docs/demos/gis-view-demo/GisViewDemoCardHeader.vue +28 -0
- package/docs/demos/gis-view-demo/GisViewDemoDialogs.vue +82 -0
- package/docs/demos/gis-view-demo/GisViewDemoFooter.vue +16 -0
- package/docs/demos/gis-view-demo/GisViewDemoHeading.vue +16 -0
- package/docs/demos/gis-view-demo/GisViewDemoPanelStack.vue +71 -0
- package/docs/demos/gis-view-demo/GisViewDemoStage.vue +219 -0
- package/docs/demos/gis-view-demo/demo.css +1230 -0
- package/docs/demos/gis-view-demo/demoContext.js +12 -0
- package/docs/demos/gis-view-demo/panels/DataPanels.vue +303 -0
- package/docs/demos/gis-view-demo/panels/OpsPanels.vue +174 -0
- package/docs/demos/gis-view-demo/panels/StylePanels.vue +561 -0
- package/docs/demos/gis-view-demo/panels/ViewPanels.vue +396 -0
- package/docs/demos/gis-view-demo/useGisViewDemo.js +6188 -0
- package/docs/format/07-/344/272/213/344/273/266/350/275/275/350/215/267/344/270/216/347/244/272/344/276/213/346/225/260/346/215/256.md +3 -2
- package/docs/internals/16-03-/344/274/230/345/214/226/344/270/216/345/217/226/350/210/215-/344/270/213.md +1 -1
- package/docs/manual/02-/345/235/220/346/240/207/344/270/216/346/225/260/346/215/256.md +3 -2
- package/docs/manual/03-/351/205/215/347/275/256/351/241/271.md +87 -14
- package/docs/manual/04-/344/272/213/344/273/266/344/270/216/345/233/236/350/260/203.md +8 -8
- 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 +40 -3
- package/docs/manual/08-/351/200/211/344/270/255/344/270/216/346/213/220/347/202/271.md +26 -0
- package/docs/manual/09-/351/200/232/351/201/223/344/270/216/345/244/232/345/233/236/347/272/277.md +6 -2
- 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 +39 -7
- package/docs/manual/12-/346/240/207/347/255/276/344/270/216/350/243/205/351/245/260.md +13 -4
- package/docs/manual/13-/345/212/250/346/225/210/344/270/216/345/233/276/345/205/203.md +3 -0
- package/docs/manual/14-/350/247/206/345/233/276/344/270/216/347/255/233/351/200/211.md +35 -19
- package/docs/manual/15-/345/257/274/345/207/272/344/270/216/351/233/206/346/210/220.md +5 -0
- package/docs/manual/16-/345/256/236/344/276/213/346/226/271/346/263/225/346/200/273/350/241/250.md +12 -6
- package/docs/manual/17-/345/217/202/346/225/260/351/200/237/346/237/245.md +39 -22
- package/docs/manual/18-/351/200/220/346/226/271/346/263/225/347/244/272/344/276/213.md +52 -13
- package/docs/manual/20-/346/200/247/350/203/275/344/270/216/345/244/247/346/225/260/346/215/256.md +25 -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 +5 -5
- package/docs/steps/README.md +17 -11
- package/docs/steps/selftest-actions.html +3 -5
- package/docs/steps/selftest-demo.html +27 -18
- package/docs/steps/selftest-demos.html +387 -27
- package/docs/steps/selftest-docs.html +4 -4
- package/docs/steps/selftest-effects.html +200 -0
- package/docs/steps/selftest-features.html +512 -11
- package/docs/steps/selftest-labels.html +74 -0
- package/docs/steps/selftest-perf.html +293 -20
- package/docs/steps/selftest-scale.html +52 -29
- package/docs/steps/step-10-full.js +1 -1
- package/docs/steps/step-12-events.js +1 -1
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* `mergeChannelGroups(id, [要并的组], 目标组)` 再并回去。
|
|
8
8
|
* `addChannelGroup(id, [回线…], { id?, path? })` 一次把几条拆成单独一组(分组那一块的勾选就是它)。
|
|
9
9
|
* - 外观:通道宽度 / 内收比例 / 偏移方式(tangent 沿切向,inset 按比例内收)都是 updateOptions。
|
|
10
|
-
* -
|
|
10
|
+
* - 交互:右键一条通道回线弹出交互框(统一的 onContextMenu + payload.channelContext)—— 点分组切换当前分组并选中它的回线,
|
|
11
11
|
* 每条回线可以拆成新分组 / 并进别的分组(moveChannelLine),下面一排按钮走 runChannelAction。
|
|
12
12
|
* - 选中模式 / 删除:channelSelectMode 决定点一条回线是只选它自己、连所在分组还是连整条通道;
|
|
13
13
|
* Delete(或 requestRemove())先出删除载荷 —— 删什么、连带什么、能按「只删线路 / 整个分组 / 整条通道」哪个范围删,
|
|
@@ -303,7 +303,8 @@ const buildOptions = () => ({
|
|
|
303
303
|
showGrid: true,
|
|
304
304
|
fitPadding: 60,
|
|
305
305
|
// 开关缺省全关(见 features.js):这个演示要的是「拖节点改通道 + 右键通道回线出交互框 + Delete 删除」,逐项显式打开
|
|
306
|
-
|
|
306
|
+
// 通道路由与普通右键共用 contextMenu,不再另开一个功能开关
|
|
307
|
+
features: { pan: true, zoom: true, hover: true, select: true, contextMenu: true, keyboard: true, drag: true, remove: true },
|
|
307
308
|
// 通道选中模式:点一条回线选它自己 / 连所在分组 / 连整条通道
|
|
308
309
|
channelSelectMode: channelSelectMode.value,
|
|
309
310
|
// 分组拐点拖动:共用骨架 / 只移同组同序号拐点
|
|
@@ -315,7 +316,7 @@ const buildOptions = () => ({
|
|
|
315
316
|
// 这两条是假接口的入口:拐点只接 add / remove / end,分组只接整份声明;事件载荷本身已经够组请求
|
|
316
317
|
onWaypointChange: handleWaypointBackend,
|
|
317
318
|
onChannelGroupChange: handleChannelGroupBackend,
|
|
318
|
-
|
|
319
|
+
onContextMenu: handleContextMenu,
|
|
319
320
|
onReady: () => {
|
|
320
321
|
emit('notice', `${dataset.channels.length} 条通道 / ${dataset.channelLines.length} 回线`)
|
|
321
322
|
pushLog('就绪:拖通道端点节点、改宽度或拆一组试试')
|
|
@@ -626,7 +627,7 @@ const confirmGroupDialog = () => {
|
|
|
626
627
|
}
|
|
627
628
|
|
|
628
629
|
/**
|
|
629
|
-
* 右键交互框:右键一条通道回线时
|
|
630
|
+
* 右键交互框:右键一条通道回线时 onContextMenu(payload) 的 payload.channelContext 把「这条通道几组 / 当前是哪一组 /
|
|
630
631
|
* 这一组能做哪些交互」一次交出来,界面照着 groups 与 actions 渲染就行 —— 核心是四件事:
|
|
631
632
|
* 点分组 = setCurrentChannelGroup(顺带选中这一组的回线)、每条回线的下拉 + 分配 = moveChannelLine、
|
|
632
633
|
* 按钮 = runChannelAction、点空白处(回调收到 null)/ Esc / × 收框。
|
|
@@ -684,11 +685,12 @@ const syncMenu = () => {
|
|
|
684
685
|
syncMenuLineTargets()
|
|
685
686
|
}
|
|
686
687
|
|
|
687
|
-
/**
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
688
|
+
/** 右键回调:所有命中都走这一条,通道上下文挂在 payload.channelContext 上;点空白 / 节点时把框收掉 */
|
|
689
|
+
const handleContextMenu = (payload) => {
|
|
690
|
+
const channelContext = payload?.channelContext
|
|
691
|
+
channelMenu.value = channelContext ? { ...payload, ...channelContext } : null
|
|
692
|
+
if (channelContext) {
|
|
693
|
+
pushLog('右键 ' + channelContext.channelId + ':共 ' + channelContext.groupCount + ' 组,命中「' + channelContext.groupId + '」的 ' + channelContext.group.lineCount + ' 回线')
|
|
692
694
|
}
|
|
693
695
|
syncMenu()
|
|
694
696
|
}
|
|
@@ -1032,7 +1034,7 @@ onBeforeUnmount(() => {
|
|
|
1032
1034
|
</button>
|
|
1033
1035
|
</div>
|
|
1034
1036
|
<p class="dv-channel-menu__hint">
|
|
1035
|
-
命中回线 {{ channelMenu.lineId }} · 这些都在
|
|
1037
|
+
命中回线 {{ channelMenu.lineId }} · 这些都在 onContextMenu(payload).channelContext 里;点空白处 / Esc / × 收框
|
|
1036
1038
|
</p>
|
|
1037
1039
|
</div>
|
|
1038
1040
|
<div v-if="groupDialog" class="dv-group-dialog" data-demo="channel-group-dialog" @click.self="closeGroupDialog">
|
|
@@ -1257,8 +1259,8 @@ onBeforeUnmount(() => {
|
|
|
1257
1259
|
<h3>右键交互框<span class="dv-chip">{{ channelMenu ? '已打开' : '未打开' }}</span></h3>
|
|
1258
1260
|
<p class="dv-readout">{{ menuText }}</p>
|
|
1259
1261
|
<p class="dv-hint">
|
|
1260
|
-
右键一条通道回线:
|
|
1261
|
-
|
|
1262
|
+
右键一条通道回线:onContextMenu(payload) 在 payload.channelContext 里把「几组 / 当前哪一组 /
|
|
1263
|
+
这一组能做什么」一次交出来,框里照着 groups 与 actions 渲染;点空白处 / 节点时没有 channelContext,框自动收起。
|
|
1262
1264
|
</p>
|
|
1263
1265
|
</div>
|
|
1264
1266
|
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
/**
|
|
3
|
+
* 点聚合演示:同一份合成数据在多簇中心附近铺开,面板上调开关 / 半径 / 最少成员数 / 展开缩放。
|
|
4
|
+
*
|
|
5
|
+
* 聚合只改「展示什么、能点到什么」:原始 points、getData()、listPoints() 与业务导出都不变;
|
|
6
|
+
* 聚合圆替代密集成员参与绘制、拾取、快照与 SVG / PNG 导出。点中聚合圆只发 clusterClick,
|
|
7
|
+
* 不会同时发普通 click,也不会改动选中集。
|
|
8
|
+
*/
|
|
9
|
+
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
|
10
|
+
import { createGisView } from '../../src/index.js'
|
|
11
|
+
import { DEMO_CENTER, DEMO_RADIUS, createRandom } from './shared/demoData.js'
|
|
12
|
+
import './shared/demoStyle.css'
|
|
13
|
+
|
|
14
|
+
const emit = defineEmits(['notice'])
|
|
15
|
+
const container = ref(null)
|
|
16
|
+
let view = null
|
|
17
|
+
let statsDrawnAt = Number.NEGATIVE_INFINITY
|
|
18
|
+
|
|
19
|
+
const DATA_COUNTS = [2000, 12000, 60000, 100000]
|
|
20
|
+
const pointCountChoice = ref(12000)
|
|
21
|
+
const clusterOn = ref(true)
|
|
22
|
+
const radius = ref(60)
|
|
23
|
+
const minPoints = ref(2)
|
|
24
|
+
const maxZoom = ref(0)
|
|
25
|
+
const statsText = ref('—')
|
|
26
|
+
const clusterText = ref('—')
|
|
27
|
+
const eventText = ref('等待点击聚合圆')
|
|
28
|
+
const normalClicks = ref(0)
|
|
29
|
+
const clusterClicks = ref(0)
|
|
30
|
+
const lastCluster = ref(null)
|
|
31
|
+
const firstClusterClient = ref(null)
|
|
32
|
+
let dataset = { points: [] }
|
|
33
|
+
|
|
34
|
+
/** 多簇数据:每簇一个中心,正态近似(四个随机数求和)让簇内密度自然衰减。 */
|
|
35
|
+
const buildDataset = (count) => {
|
|
36
|
+
const random = createRandom(20261021)
|
|
37
|
+
const points = []
|
|
38
|
+
const centers = [
|
|
39
|
+
[-0.56, 0.34, 0.16],
|
|
40
|
+
[0.08, 0.56, 0.13],
|
|
41
|
+
[0.62, 0.22, 0.18],
|
|
42
|
+
[-0.32, -0.42, 0.15],
|
|
43
|
+
[0.48, -0.5, 0.14],
|
|
44
|
+
[0.02, -0.08, 0.1]
|
|
45
|
+
]
|
|
46
|
+
for (let index = 0; index < count; index += 1) {
|
|
47
|
+
const center = centers[index % centers.length]
|
|
48
|
+
const common = index < count * 0.08
|
|
49
|
+
const spread = common ? 1.6 : 0.68
|
|
50
|
+
const nx = (random() + random() + random() + random() - 2) * spread
|
|
51
|
+
const ny = (random() + random() + random() + random() - 2) * spread
|
|
52
|
+
points.push({
|
|
53
|
+
id: `cluster-point-${index}`,
|
|
54
|
+
x: Number((DEMO_CENTER[0] + (center[0] + nx * center[2]) * DEMO_RADIUS).toFixed(6)),
|
|
55
|
+
y: Number((DEMO_CENTER[1] + (center[1] + ny * center[2]) * DEMO_RADIUS).toFixed(6)),
|
|
56
|
+
name: `站点 ${index + 1}`,
|
|
57
|
+
zone_name: `分区 ${(index % centers.length) + 1}`,
|
|
58
|
+
vn_kv: [1050, 525, 230, 15.7][index % 4],
|
|
59
|
+
max_loading_rate: Number((0.35 + random() * 1.1).toFixed(3))
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
return { points }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const clusterOptions = () => clusterOn.value
|
|
66
|
+
? { enabled: true, radius: radius.value, minPoints: minPoints.value, maxZoom: maxZoom.value }
|
|
67
|
+
: false
|
|
68
|
+
|
|
69
|
+
const buildOptions = () => ({
|
|
70
|
+
points: dataset.points,
|
|
71
|
+
pointStyle: { size: 4, color: '#ffffff', strokeColor: '#4c8dff', strokeWidth: 1 },
|
|
72
|
+
showGrid: true,
|
|
73
|
+
gridColor: 'rgba(126, 168, 214, 0.18)',
|
|
74
|
+
fitPadding: 42,
|
|
75
|
+
denseMode: 'off',
|
|
76
|
+
features: 'view',
|
|
77
|
+
pointCluster: clusterOptions(),
|
|
78
|
+
onReady: () => {
|
|
79
|
+
emit('notice', `点聚合演示已加载 ${dataset.points.length.toLocaleString()} 个点`)
|
|
80
|
+
queueMicrotask(syncClusters)
|
|
81
|
+
},
|
|
82
|
+
onClick: () => {
|
|
83
|
+
normalClicks.value += 1
|
|
84
|
+
eventText.value = '普通 click:聚合圆的点击不会再走到这里'
|
|
85
|
+
},
|
|
86
|
+
onClusterClick: payload => {
|
|
87
|
+
clusterClicks.value += 1
|
|
88
|
+
lastCluster.value = {
|
|
89
|
+
id: payload.id,
|
|
90
|
+
count: payload.count,
|
|
91
|
+
pointIds: [...payload.pointIds],
|
|
92
|
+
bounds: { ...payload.bounds },
|
|
93
|
+
coordinate: [...payload.coordinate]
|
|
94
|
+
}
|
|
95
|
+
const ids = payload.pointIds.slice(0, 6).join('、') + (payload.pointIds.length > 6 ? '…' : '')
|
|
96
|
+
eventText.value = `clusterClick:${payload.count} 点 · ${ids} · 包围盒 ${payload.bounds.minX.toFixed(3)}, ${payload.bounds.minY.toFixed(3)} ~ ${payload.bounds.maxX.toFixed(3)}, ${payload.bounds.maxY.toFixed(3)}`
|
|
97
|
+
},
|
|
98
|
+
onStats: next => {
|
|
99
|
+
const now = performance.now()
|
|
100
|
+
if (now - statsDrawnAt < 250) {
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
statsDrawnAt = now
|
|
104
|
+
statsText.value = `${next.pointCount.toLocaleString()} 原始点 · ${next.clusterCount.toLocaleString()} 个聚合圆(${next.clusterPointCount.toLocaleString()} 点)· 展示 ${(next.pointCount - next.clusterPointCount + next.clusterCount).toLocaleString()} 项 · 本帧 ${next.frameMs.toFixed(2)} ms`
|
|
105
|
+
clusterText.value = `聚合耗时 ${next.clusterMs.toFixed(2)} ms · ${view?.getPointCluster()?.active ? '当前缩放仍在聚合' : '当前缩放已展开原始点'}`
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
/** getClusters() 会按当前数据顺序展开单成员;这里只取第一个多成员组,给自测定位点击位置。 */
|
|
110
|
+
const syncClusters = async (options = {}) => {
|
|
111
|
+
if (!view) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
if (options.render !== false) {
|
|
115
|
+
await view.whenRendered()
|
|
116
|
+
}
|
|
117
|
+
const clusters = view.getClusters().filter(item => item.count > 1)
|
|
118
|
+
const first = clusters[0]
|
|
119
|
+
firstClusterClient.value = first ? view.screenToClient(first.screen[0], first.screen[1]) : null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const applyCluster = async () => {
|
|
123
|
+
if (!view) {
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
view.updateOptions({ pointCluster: clusterOptions() })
|
|
127
|
+
await syncClusters()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const applyData = async () => {
|
|
131
|
+
if (!view) {
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
dataset = buildDataset(pointCountChoice.value)
|
|
135
|
+
view.updateData(dataset.points, [])
|
|
136
|
+
view.fitView(42)
|
|
137
|
+
lastCluster.value = null
|
|
138
|
+
await syncClusters()
|
|
139
|
+
emit('notice', `已换成 ${dataset.points.length.toLocaleString()} 个点`)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const fitAll = () => {
|
|
143
|
+
view?.fitView(42)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const zoomLastCluster = () => {
|
|
147
|
+
if (lastCluster.value?.bounds) {
|
|
148
|
+
view?.zoomToBounds(lastCluster.value.bounds, 54)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const countText = computed(() => `${normalClicks.value} 次普通点击 / ${clusterClicks.value} 次聚合点击`)
|
|
153
|
+
|
|
154
|
+
onMounted(() => {
|
|
155
|
+
dataset = buildDataset(pointCountChoice.value)
|
|
156
|
+
view = createGisView(container.value, buildOptions())
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
onBeforeUnmount(() => {
|
|
160
|
+
view?.destroy()
|
|
161
|
+
view = null
|
|
162
|
+
})
|
|
163
|
+
</script>
|
|
164
|
+
|
|
165
|
+
<template>
|
|
166
|
+
<section class='dv-demo'>
|
|
167
|
+
<div class='dv-stage' data-demo='cluster-stage'>
|
|
168
|
+
<div ref='container' class='dv-canvas' data-demo='cluster-canvas' />
|
|
169
|
+
<div class='dv-toolbar'>
|
|
170
|
+
<button type='button' data-demo='cluster-fit' @click='fitAll'>适应全部</button>
|
|
171
|
+
<button type='button' data-demo='cluster-zoom' :disabled='!lastCluster' @click='zoomLastCluster'>放大到最近聚合</button>
|
|
172
|
+
</div>
|
|
173
|
+
<span
|
|
174
|
+
class='cl-anchor'
|
|
175
|
+
data-demo='cluster-first-client'
|
|
176
|
+
:data-client-x='firstClusterClient ? firstClusterClient[0] : undefined'
|
|
177
|
+
:data-client-y='firstClusterClient ? firstClusterClient[1] : undefined'
|
|
178
|
+
/>
|
|
179
|
+
</div>
|
|
180
|
+
<aside class='dv-panel'>
|
|
181
|
+
<div class='dv-group'>
|
|
182
|
+
<h3>聚合配置</h3>
|
|
183
|
+
<div class='dv-row'>
|
|
184
|
+
<label><input v-model='clusterOn' data-demo='cluster-toggle' type='checkbox' @change='applyCluster' /> 开启聚合</label>
|
|
185
|
+
</div>
|
|
186
|
+
<div class='dv-row'>
|
|
187
|
+
<label>半径
|
|
188
|
+
<input v-model.number='radius' data-demo='cluster-radius' type='range' min='16' max='180' step='4' @change='applyCluster' />
|
|
189
|
+
<span class='cl-value'>{{ radius }}px</span>
|
|
190
|
+
</label>
|
|
191
|
+
</div>
|
|
192
|
+
<div class='dv-row'>
|
|
193
|
+
<label>最少点数
|
|
194
|
+
<select v-model.number='minPoints' data-demo='cluster-min-points' @change='applyCluster'>
|
|
195
|
+
<option :value='2'>2</option>
|
|
196
|
+
<option :value='3'>3</option>
|
|
197
|
+
<option :value='5'>5</option>
|
|
198
|
+
<option :value='8'>8</option>
|
|
199
|
+
<option :value='12'>12</option>
|
|
200
|
+
</select>
|
|
201
|
+
</label>
|
|
202
|
+
<label>展开缩放
|
|
203
|
+
<select v-model.number='maxZoom' data-demo='cluster-max-zoom' @change='applyCluster'>
|
|
204
|
+
<option :value='0'>不限</option>
|
|
205
|
+
<option :value='1.5'>1.5×</option>
|
|
206
|
+
<option :value='2'>2×</option>
|
|
207
|
+
<option :value='4'>4×</option>
|
|
208
|
+
<option :value='8'>8×</option>
|
|
209
|
+
</select>
|
|
210
|
+
</label>
|
|
211
|
+
</div>
|
|
212
|
+
<p class='dv-hint'>半径是屏幕像素;maxZoom 是相机比例相对基准比例的倍数,超过后展开原始点。聚合期间发散初始化暂停,缩放 / 数据 / 选中变化才重算。</p>
|
|
213
|
+
</div>
|
|
214
|
+
<div class='dv-group'>
|
|
215
|
+
<h3>数据规模</h3>
|
|
216
|
+
<div class='dv-row'>
|
|
217
|
+
<label>点数
|
|
218
|
+
<select v-model.number='pointCountChoice' data-demo='cluster-count'>
|
|
219
|
+
<option v-for='count in DATA_COUNTS' :key='count' :value='count'>{{ count.toLocaleString() }}</option>
|
|
220
|
+
</select>
|
|
221
|
+
</label>
|
|
222
|
+
<button type='button' class='dv-button' data-demo='cluster-rebuild' @click='applyData'>重建</button>
|
|
223
|
+
</div>
|
|
224
|
+
<p class='dv-hint'>十万点仍按整份网格 O(n) 建组;平移不重算,拖动 / 选中中的点强制保持单成员。</p>
|
|
225
|
+
</div>
|
|
226
|
+
<div class='dv-group'>
|
|
227
|
+
<h3>读数</h3>
|
|
228
|
+
<p class='dv-readout' data-demo='cluster-stats'>{{ statsText }}</p>
|
|
229
|
+
<p class='dv-readout' data-demo='cluster-summary'>{{ clusterText }}</p>
|
|
230
|
+
<p class='dv-readout' data-demo='cluster-click-count'>{{ countText }}</p>
|
|
231
|
+
<p class='dv-readout' data-demo='cluster-event'>{{ eventText }}</p>
|
|
232
|
+
<p class='dv-hint'>点击聚合圆应只增加「聚合点击」计数;普通 click 不会被聚合圆触发,选中集也保持不变。</p>
|
|
233
|
+
</div>
|
|
234
|
+
</aside>
|
|
235
|
+
</section>
|
|
236
|
+
</template>
|
|
237
|
+
|
|
238
|
+
<style scoped>
|
|
239
|
+
.cl-anchor {
|
|
240
|
+
position: absolute;
|
|
241
|
+
width: 1px;
|
|
242
|
+
height: 1px;
|
|
243
|
+
overflow: hidden;
|
|
244
|
+
pointer-events: none;
|
|
245
|
+
}
|
|
246
|
+
.cl-value {
|
|
247
|
+
min-width: 38px;
|
|
248
|
+
color: #526b8d;
|
|
249
|
+
font-size: 11px;
|
|
250
|
+
}
|
|
251
|
+
</style>
|
|
@@ -984,10 +984,9 @@ onMounted(() => {
|
|
|
984
984
|
merge: true,
|
|
985
985
|
keyboard: true,
|
|
986
986
|
waypoint: false,
|
|
987
|
-
remove: false,
|
|
988
|
-
channelDraft: false,
|
|
989
|
-
|
|
990
|
-
contextMenu: false
|
|
987
|
+
remove: false,
|
|
988
|
+
channelDraft: false,
|
|
989
|
+
contextMenu: false
|
|
991
990
|
},
|
|
992
991
|
copy: {
|
|
993
992
|
enabled: true,
|
package/docs/demos/EventDemo.vue
CHANGED
|
@@ -89,14 +89,14 @@ const dataLabel = (value) => {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
* 要 DOM
|
|
92
|
+
* 要 DOM 事件的三个事件(pointerDown / pointerUp / contextMenu)在总线上
|
|
93
93
|
* 载荷是 `{ payload, event }`(与回调的两个参数一一对应),其余事件载荷就是回调里那一份 —— 这里统一拆开。
|
|
94
94
|
*/
|
|
95
|
-
const EVENT_PAYLOAD_EVENTS = new Set(['pointerDown', 'pointerUp', 'contextMenu'
|
|
95
|
+
const EVENT_PAYLOAD_EVENTS = new Set(['pointerDown', 'pointerUp', 'contextMenu'])
|
|
96
96
|
const unwrapPayload = (raw, name) => (EVENT_PAYLOAD_EVENTS.has(name) && raw && typeof raw === 'object' && 'payload' in raw ? raw.payload : raw)
|
|
97
97
|
|
|
98
98
|
/** 会带 action 的事件名:* 订阅里从载荷上认出「这一次是什么操作」 */
|
|
99
|
-
const ACTION_EVENTS = new Set(['click', 'dblClick', 'pointerDown', 'pointerUp', 'contextMenu', '
|
|
99
|
+
const ACTION_EVENTS = new Set(['click', 'dblClick', 'pointerDown', 'pointerUp', 'contextMenu', 'labelClick', 'labelDrag', 'labelContextMenu', 'labelDblClick', 'selectChange', 'waypointChange'])
|
|
100
100
|
|
|
101
101
|
/** 最近一次操作的判读:面板上的「动作判读」直接读它 */
|
|
102
102
|
const lastAction = ref('—')
|