|
|
@@ -11,7 +11,6 @@ import {
|
|
11
|
11
|
resolveGisMapStyle,
|
|
12
|
12
|
createGisTransformRequest
|
|
13
|
13
|
} from '@/utils/gisLoader'
|
|
14
|
|
-import { getMapPixelRatio } from '@/utils/screenFit'
|
|
15
|
14
|
|
|
16
|
15
|
const props = defineProps({
|
|
17
|
16
|
center: {
|
|
|
@@ -44,6 +43,16 @@ let map = null
|
|
44
|
43
|
let cmmapglRef = null
|
|
45
|
44
|
let zoomLogTimer = null
|
|
46
|
45
|
let lastMouseZoomLogAt = 0
|
|
|
46
|
+let clampingZoom = false
|
|
|
47
|
+let unbindWheelZoom = null
|
|
|
48
|
+
|
|
|
49
|
+/** 滚轮缩放防抖:停止滚动后再合并应用,减少瓦片 canceled */
|
|
|
50
|
+const WHEEL_ZOOM_DEBOUNCE_MS = 150
|
|
|
51
|
+const WHEEL_ZOOM_RATE = 0.0022
|
|
|
52
|
+
|
|
|
53
|
+let wheelDeltaAccum = 0
|
|
|
54
|
+let wheelDebounceTimer = null
|
|
|
55
|
+let lastWheelAround = null
|
|
47
|
56
|
|
|
48
|
57
|
function formatLngLat(lngLat) {
|
|
49
|
58
|
if (!lngLat) {
|
|
|
@@ -80,7 +89,10 @@ function bindZoomDebugListeners() {
|
|
80
|
89
|
return
|
|
81
|
90
|
}
|
|
82
|
91
|
map.on('load', () => logMapState('load'))
|
|
83
|
|
- map.on('zoomend', () => logMapState('zoomend'))
|
|
|
92
|
+ map.on('zoomend', () => {
|
|
|
93
|
+ snapZoomToLimits()
|
|
|
94
|
+ logMapState('zoomend')
|
|
|
95
|
+ })
|
|
84
|
96
|
map.on('moveend', () => logMapState('moveend'))
|
|
85
|
97
|
map.on('wheel', () => {
|
|
86
|
98
|
if (zoomLogTimer) {
|
|
|
@@ -106,7 +118,14 @@ function destroyMap() {
|
|
106
|
118
|
clearTimeout(zoomLogTimer)
|
|
107
|
119
|
zoomLogTimer = null
|
|
108
|
120
|
}
|
|
|
121
|
+ if (typeof unbindWheelZoom === 'function') {
|
|
|
122
|
+ unbindWheelZoom()
|
|
|
123
|
+ unbindWheelZoom = null
|
|
|
124
|
+ }
|
|
|
125
|
+ wheelDeltaAccum = 0
|
|
|
126
|
+ lastWheelAround = null
|
|
109
|
127
|
lastMouseZoomLogAt = 0
|
|
|
128
|
+ clampingZoom = false
|
|
110
|
129
|
if (map) {
|
|
111
|
130
|
map.remove()
|
|
112
|
131
|
map = null
|
|
|
@@ -127,13 +146,173 @@ function resolveZoom() {
|
|
127
|
146
|
return getGisDefaultZoom()
|
|
128
|
147
|
}
|
|
129
|
148
|
|
|
130
|
|
-function applyMapSharpness() {
|
|
|
149
|
+function applyMapLimits() {
|
|
|
150
|
+ if (!map) {
|
|
|
151
|
+ return
|
|
|
152
|
+ }
|
|
|
153
|
+ const minZ = getGisMinZoom()
|
|
|
154
|
+ const maxZ = getGisMaxZoom()
|
|
|
155
|
+ if (typeof map.setMinZoom === 'function') {
|
|
|
156
|
+ map.setMinZoom(minZ)
|
|
|
157
|
+ }
|
|
|
158
|
+ if (typeof map.setMaxZoom === 'function') {
|
|
|
159
|
+ map.setMaxZoom(maxZ)
|
|
|
160
|
+ }
|
|
|
161
|
+}
|
|
|
162
|
+
|
|
|
163
|
+/** 任何越界立即拉回(不用 0.01 容差,否则 17.005 时图层会被 style maxzoom 隐藏) */
|
|
|
164
|
+function snapZoomToLimits() {
|
|
|
165
|
+ if (!map || clampingZoom) {
|
|
|
166
|
+ return
|
|
|
167
|
+ }
|
|
|
168
|
+ const minZ = getGisMinZoom()
|
|
|
169
|
+ const maxZ = getGisMaxZoom()
|
|
|
170
|
+ const z = map.getZoom()
|
|
|
171
|
+ if (!Number.isFinite(z)) {
|
|
|
172
|
+ return
|
|
|
173
|
+ }
|
|
|
174
|
+ if (z <= maxZ && z >= minZ) {
|
|
|
175
|
+ return
|
|
|
176
|
+ }
|
|
|
177
|
+ const next = Math.min(maxZ, Math.max(minZ, z))
|
|
|
178
|
+ clampingZoom = true
|
|
|
179
|
+ try {
|
|
|
180
|
+ map.jumpTo({ zoom: next })
|
|
|
181
|
+ } finally {
|
|
|
182
|
+ clampingZoom = false
|
|
|
183
|
+ }
|
|
|
184
|
+}
|
|
|
185
|
+
|
|
|
186
|
+function bindZoomClampListener() {
|
|
|
187
|
+ if (!map) {
|
|
|
188
|
+ return
|
|
|
189
|
+ }
|
|
|
190
|
+ map.on('zoom', () => {
|
|
|
191
|
+ if (clampingZoom || !map) {
|
|
|
192
|
+ return
|
|
|
193
|
+ }
|
|
|
194
|
+ const maxZ = getGisMaxZoom()
|
|
|
195
|
+ const minZ = getGisMinZoom()
|
|
|
196
|
+ const z = map.getZoom()
|
|
|
197
|
+ if (!Number.isFinite(z) || (z <= maxZ && z >= minZ)) {
|
|
|
198
|
+ return
|
|
|
199
|
+ }
|
|
|
200
|
+ snapZoomToLimits()
|
|
|
201
|
+ })
|
|
|
202
|
+}
|
|
|
203
|
+
|
|
|
204
|
+/** 滚轮防抖缩放:拦截原生 scrollZoom,停止滚动后一次性应用累计 delta */
|
|
|
205
|
+function bindDebouncedWheelZoom() {
|
|
|
206
|
+ const el = containerRef.value
|
|
|
207
|
+ if (!el || !map) {
|
|
|
208
|
+ return () => {}
|
|
|
209
|
+ }
|
|
|
210
|
+
|
|
|
211
|
+ const flushWheelZoom = () => {
|
|
|
212
|
+ wheelDebounceTimer = null
|
|
|
213
|
+ if (!map || wheelDeltaAccum === 0) {
|
|
|
214
|
+ return
|
|
|
215
|
+ }
|
|
|
216
|
+ const minZ = getGisMinZoom()
|
|
|
217
|
+ const maxZ = getGisMaxZoom()
|
|
|
218
|
+ const z = map.getZoom()
|
|
|
219
|
+ if (!Number.isFinite(z)) {
|
|
|
220
|
+ wheelDeltaAccum = 0
|
|
|
221
|
+ return
|
|
|
222
|
+ }
|
|
|
223
|
+
|
|
|
224
|
+ const delta = wheelDeltaAccum
|
|
|
225
|
+ wheelDeltaAccum = 0
|
|
|
226
|
+
|
|
|
227
|
+ let targetZ = z - delta * WHEEL_ZOOM_RATE
|
|
|
228
|
+ targetZ = Math.min(maxZ, Math.max(minZ, targetZ))
|
|
|
229
|
+ if (Math.abs(targetZ - z) < 0.001) {
|
|
|
230
|
+ return
|
|
|
231
|
+ }
|
|
|
232
|
+
|
|
|
233
|
+ const around = lastWheelAround || (typeof map.getCenter === 'function' ? map.getCenter() : null)
|
|
|
234
|
+ lastWheelAround = null
|
|
|
235
|
+
|
|
|
236
|
+ const nearLimit = targetZ >= maxZ - 0.05 || targetZ <= minZ + 0.05
|
|
|
237
|
+ if (nearLimit && typeof map.jumpTo === 'function') {
|
|
|
238
|
+ map.jumpTo(around ? { zoom: targetZ, around } : { zoom: targetZ })
|
|
|
239
|
+ return
|
|
|
240
|
+ }
|
|
|
241
|
+
|
|
|
242
|
+ if (around && typeof map.easeTo === 'function') {
|
|
|
243
|
+ map.easeTo({
|
|
|
244
|
+ zoom: targetZ,
|
|
|
245
|
+ around,
|
|
|
246
|
+ duration: 180,
|
|
|
247
|
+ essential: true
|
|
|
248
|
+ })
|
|
|
249
|
+ } else if (typeof map.setZoom === 'function') {
|
|
|
250
|
+ map.setZoom(targetZ)
|
|
|
251
|
+ }
|
|
|
252
|
+ }
|
|
|
253
|
+
|
|
|
254
|
+ const onWheel = (e) => {
|
|
|
255
|
+ if (!map || !props.interactive) {
|
|
|
256
|
+ return
|
|
|
257
|
+ }
|
|
|
258
|
+
|
|
|
259
|
+ e.preventDefault()
|
|
|
260
|
+ e.stopImmediatePropagation()
|
|
|
261
|
+
|
|
|
262
|
+ const minZ = getGisMinZoom()
|
|
|
263
|
+ const maxZ = getGisMaxZoom()
|
|
|
264
|
+ const z = map.getZoom()
|
|
|
265
|
+ if (!Number.isFinite(z)) {
|
|
|
266
|
+ return
|
|
|
267
|
+ }
|
|
|
268
|
+ if (e.deltaY < 0 && z >= maxZ - 0.01) {
|
|
|
269
|
+ return
|
|
|
270
|
+ }
|
|
|
271
|
+ if (e.deltaY > 0 && z <= minZ + 0.01) {
|
|
|
272
|
+ return
|
|
|
273
|
+ }
|
|
|
274
|
+
|
|
|
275
|
+ const rect = el.getBoundingClientRect()
|
|
|
276
|
+ const x = e.clientX - rect.left
|
|
|
277
|
+ const y = e.clientY - rect.top
|
|
|
278
|
+ if (typeof map.unproject === 'function') {
|
|
|
279
|
+ lastWheelAround = map.unproject([x, y])
|
|
|
280
|
+ }
|
|
|
281
|
+
|
|
|
282
|
+ wheelDeltaAccum += e.deltaY
|
|
|
283
|
+ if (wheelDebounceTimer) {
|
|
|
284
|
+ clearTimeout(wheelDebounceTimer)
|
|
|
285
|
+ }
|
|
|
286
|
+ wheelDebounceTimer = setTimeout(flushWheelZoom, WHEEL_ZOOM_DEBOUNCE_MS)
|
|
|
287
|
+ }
|
|
|
288
|
+
|
|
|
289
|
+ el.addEventListener('wheel', onWheel, { passive: false, capture: true })
|
|
|
290
|
+ return () => {
|
|
|
291
|
+ if (wheelDebounceTimer) {
|
|
|
292
|
+ clearTimeout(wheelDebounceTimer)
|
|
|
293
|
+ wheelDebounceTimer = null
|
|
|
294
|
+ }
|
|
|
295
|
+ wheelDeltaAccum = 0
|
|
|
296
|
+ lastWheelAround = null
|
|
|
297
|
+ el.removeEventListener('wheel', onWheel, { capture: true })
|
|
|
298
|
+ }
|
|
|
299
|
+}
|
|
|
300
|
+
|
|
|
301
|
+function configureScrollZoom() {
|
|
|
302
|
+ if (!map?.scrollZoom) {
|
|
|
303
|
+ return
|
|
|
304
|
+ }
|
|
|
305
|
+ if (typeof map.scrollZoom.disable === 'function') {
|
|
|
306
|
+ map.scrollZoom.disable()
|
|
|
307
|
+ }
|
|
|
308
|
+}
|
|
|
309
|
+
|
|
|
310
|
+function resizeMap() {
|
|
131
|
311
|
if (!map) {
|
|
132
|
312
|
return
|
|
133
|
313
|
}
|
|
134
|
|
- const ratio = getMapPixelRatio()
|
|
135
|
314
|
if (typeof map.setPixelRatio === 'function') {
|
|
136
|
|
- map.setPixelRatio(ratio)
|
|
|
315
|
+ map.setPixelRatio(1)
|
|
137
|
316
|
}
|
|
138
|
317
|
if (typeof map.resize === 'function') {
|
|
139
|
318
|
map.resize()
|
|
|
@@ -147,6 +326,9 @@ async function initMap() {
|
|
147
|
326
|
}
|
|
148
|
327
|
try {
|
|
149
|
328
|
const cmmapgl = await loadGisSdk()
|
|
|
329
|
+ if (typeof cmmapgl.setMaxParallelImageRequests === 'function') {
|
|
|
330
|
+ cmmapgl.setMaxParallelImageRequests(64)
|
|
|
331
|
+ }
|
|
150
|
332
|
cmmapglRef = cmmapgl
|
|
151
|
333
|
const token = getGisAccessToken()
|
|
152
|
334
|
if (token) {
|
|
|
@@ -163,8 +345,8 @@ async function initMap() {
|
|
163
|
345
|
interactive: props.interactive,
|
|
164
|
346
|
accessToken: token || undefined,
|
|
165
|
347
|
collectResourceTiming: false,
|
|
166
|
|
- pixelRatio: getMapPixelRatio(),
|
|
167
|
|
- transformRequest: createGisTransformRequest(getMapPixelRatio)
|
|
|
348
|
+ pixelRatio: 1,
|
|
|
349
|
+ transformRequest: createGisTransformRequest()
|
|
168
|
350
|
})
|
|
169
|
351
|
if (props.showNav) {
|
|
170
|
352
|
map.addControl(new cmmapgl.NavigationControl(), 'top-right')
|
|
|
@@ -173,8 +355,14 @@ async function initMap() {
|
|
173
|
355
|
map.addControl(new cmmapgl.ScaleControl({ unit: 'metric' }), 'bottom-left')
|
|
174
|
356
|
}
|
|
175
|
357
|
bindZoomDebugListeners()
|
|
|
358
|
+ bindZoomClampListener()
|
|
|
359
|
+ unbindWheelZoom = bindDebouncedWheelZoom()
|
|
|
360
|
+ configureScrollZoom()
|
|
176
|
361
|
map.on('load', () => {
|
|
177
|
|
- applyMapSharpness()
|
|
|
362
|
+ applyMapLimits()
|
|
|
363
|
+ configureScrollZoom()
|
|
|
364
|
+ snapZoomToLimits()
|
|
|
365
|
+ resizeMap()
|
|
178
|
366
|
emit('ready', map)
|
|
179
|
367
|
})
|
|
180
|
368
|
map.on('click', (e) => emit('click', e))
|
|
|
@@ -185,10 +373,6 @@ async function initMap() {
|
|
185
|
373
|
}
|
|
186
|
374
|
}
|
|
187
|
375
|
|
|
188
|
|
-function resizeMap() {
|
|
189
|
|
- applyMapSharpness()
|
|
190
|
|
-}
|
|
191
|
|
-
|
|
192
|
376
|
watch(
|
|
193
|
377
|
() => [props.center, props.zoom],
|
|
194
|
378
|
() => {
|