uni-leaflet 1.1.0
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 +385 -0
- package/UniLeafletMap.vue +752 -0
- package/app-render.d.ts +2 -0
- package/app-render.js +577 -0
- package/engine/canvas-tile-engine.ts +1252 -0
- package/engine/factory.ts +101 -0
- package/engine/h5-map-adapter.ts +504 -0
- package/engine/tile-manager.ts +192 -0
- package/index.ts +11 -0
- package/package.json +44 -0
- package/types.ts +159 -0
- package/uni-leaflet-1.0.0.tgz +0 -0
- package/utils/crs.ts +60 -0
- package/utils/tile.ts +118 -0
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<view
|
|
3
|
+
class="uni-leaflet-wrapper"
|
|
4
|
+
:style="{ width: props.width, height: props.height }"
|
|
5
|
+
@wheel.prevent.stop="handleWheel"
|
|
6
|
+
@mousewheel.prevent.stop="handleWheel"
|
|
7
|
+
>
|
|
8
|
+
<!-- #ifdef H5 -->
|
|
9
|
+
<div ref="h5ContainerRef" class="uni-leaflet-h5-container"></div>
|
|
10
|
+
<!-- #endif -->
|
|
11
|
+
|
|
12
|
+
<!-- #ifdef APP-PLUS -->
|
|
13
|
+
<view
|
|
14
|
+
:id="appMapId"
|
|
15
|
+
class="uni-leaflet-h5-container"
|
|
16
|
+
:change:prop="leafletRender.updateMapProps"
|
|
17
|
+
:prop="mapPropsPayload"
|
|
18
|
+
></view>
|
|
19
|
+
<!-- #endif -->
|
|
20
|
+
|
|
21
|
+
<!-- #ifdef MP -->
|
|
22
|
+
<canvas
|
|
23
|
+
type="2d"
|
|
24
|
+
:id="canvasId"
|
|
25
|
+
:canvas-id="canvasId"
|
|
26
|
+
class="uni-leaflet-canvas"
|
|
27
|
+
disable-scroll="true"
|
|
28
|
+
@touchstart="handleTouchStart"
|
|
29
|
+
@touchmove="handleTouchMove"
|
|
30
|
+
@touchend="handleTouchEnd"
|
|
31
|
+
@touchcancel="handleTouchEnd"
|
|
32
|
+
@wheel.prevent.stop="handleWheel"
|
|
33
|
+
@mousewheel.prevent.stop="handleWheel"
|
|
34
|
+
></canvas>
|
|
35
|
+
<!-- #endif -->
|
|
36
|
+
|
|
37
|
+
<!-- Floating Zoom & Action Controls for H5 and App-Plus (Mini-Program renders natively on Canvas 2D) -->
|
|
38
|
+
<!-- #ifndef MP -->
|
|
39
|
+
<view v-if="props.showControls" class="uni-leaflet-controls">
|
|
40
|
+
<view class="control-btn" hover-class="btn-hover" @click.stop="handleZoomIn">
|
|
41
|
+
<text class="btn-text">+</text>
|
|
42
|
+
</view>
|
|
43
|
+
<view class="control-divider"></view>
|
|
44
|
+
<view class="control-btn" hover-class="btn-hover" @click.stop="handleZoomOut">
|
|
45
|
+
<text class="btn-text">-</text>
|
|
46
|
+
</view>
|
|
47
|
+
</view>
|
|
48
|
+
<!-- #endif -->
|
|
49
|
+
</view>
|
|
50
|
+
</template>
|
|
51
|
+
|
|
52
|
+
<script lang="ts">
|
|
53
|
+
export default {
|
|
54
|
+
methods: {
|
|
55
|
+
onRender_MapMove(this: any, val: any) {
|
|
56
|
+
const center = val?.center || val;
|
|
57
|
+
const zoom = val?.zoom;
|
|
58
|
+
if (center) {
|
|
59
|
+
this.$emit('update:center', center);
|
|
60
|
+
this.$emit('move', { center, zoom });
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
onRender_MapMoveEnd(this: any, val: any) {
|
|
64
|
+
const center = val?.center || val;
|
|
65
|
+
const zoom = val?.zoom;
|
|
66
|
+
if (center) {
|
|
67
|
+
this.$emit('update:center', center);
|
|
68
|
+
this.$emit('moveend', { center, zoom });
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
onRender_MapZoom(this: any, z: any) {
|
|
72
|
+
this.$emit('update:zoom', z);
|
|
73
|
+
this.$emit('zoom', z);
|
|
74
|
+
},
|
|
75
|
+
onRender_MapZoomEnd(this: any, z: any) {
|
|
76
|
+
this.$emit('update:zoom', z);
|
|
77
|
+
this.$emit('zoomend', z);
|
|
78
|
+
},
|
|
79
|
+
onRender_MapClick(this: any, val: any) {
|
|
80
|
+
const latLng = val?.latLng || val;
|
|
81
|
+
const point = val?.point || { x: 0, y: 0 };
|
|
82
|
+
this.$emit('click', { latLng, point });
|
|
83
|
+
},
|
|
84
|
+
onRender_OverlayClick(this: any, val: any) {
|
|
85
|
+
this.$emit('overlay-click', val);
|
|
86
|
+
if (val && val.type === 'marker') this.$emit('marker-click', val.data);
|
|
87
|
+
if (val && val.type === 'polyline') this.$emit('polyline-click', val.data);
|
|
88
|
+
if (val && val.type === 'polygon') this.$emit('polygon-click', val.data);
|
|
89
|
+
if (val && val.type === 'circle') this.$emit('circle-click', val.data);
|
|
90
|
+
},
|
|
91
|
+
onRender_Ready(this: any) {
|
|
92
|
+
this.$emit('ready', this);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
</script>
|
|
97
|
+
|
|
98
|
+
<script setup lang="ts">
|
|
99
|
+
import {
|
|
100
|
+
ref,
|
|
101
|
+
computed,
|
|
102
|
+
onMounted,
|
|
103
|
+
onUnmounted,
|
|
104
|
+
getCurrentInstance,
|
|
105
|
+
watch,
|
|
106
|
+
type PropType,
|
|
107
|
+
} from 'vue';
|
|
108
|
+
import type {
|
|
109
|
+
IMapEngine,
|
|
110
|
+
LatLngTuple,
|
|
111
|
+
Point,
|
|
112
|
+
TileLayerConfig,
|
|
113
|
+
MarkerOptions,
|
|
114
|
+
PolylineOptions,
|
|
115
|
+
PolygonOptions,
|
|
116
|
+
CircleOptions,
|
|
117
|
+
MapOverlays,
|
|
118
|
+
OverlayClickEvent,
|
|
119
|
+
} from './types';
|
|
120
|
+
import { createMapEngine } from './engine/factory';
|
|
121
|
+
|
|
122
|
+
const props = defineProps({
|
|
123
|
+
center: {
|
|
124
|
+
type: Array as unknown as PropType<LatLngTuple>,
|
|
125
|
+
default: () => [39.9042, 116.4074],
|
|
126
|
+
},
|
|
127
|
+
zoom: {
|
|
128
|
+
type: Number,
|
|
129
|
+
default: 13,
|
|
130
|
+
},
|
|
131
|
+
minZoom: {
|
|
132
|
+
type: Number,
|
|
133
|
+
default: 3,
|
|
134
|
+
},
|
|
135
|
+
maxZoom: {
|
|
136
|
+
type: Number,
|
|
137
|
+
default: 18,
|
|
138
|
+
},
|
|
139
|
+
tileUrl: {
|
|
140
|
+
type: String,
|
|
141
|
+
default: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
142
|
+
},
|
|
143
|
+
subdomains: {
|
|
144
|
+
type: Array as PropType<string[]>,
|
|
145
|
+
default: () => ['a', 'b', 'c'],
|
|
146
|
+
},
|
|
147
|
+
layers: {
|
|
148
|
+
type: Array as PropType<Array<string | TileLayerConfig>>,
|
|
149
|
+
default: () => undefined,
|
|
150
|
+
},
|
|
151
|
+
markers: {
|
|
152
|
+
type: Array as PropType<MarkerOptions[]>,
|
|
153
|
+
default: () => undefined,
|
|
154
|
+
},
|
|
155
|
+
polylines: {
|
|
156
|
+
type: Array as PropType<PolylineOptions[]>,
|
|
157
|
+
default: () => undefined,
|
|
158
|
+
},
|
|
159
|
+
polygons: {
|
|
160
|
+
type: Array as PropType<PolygonOptions[]>,
|
|
161
|
+
default: () => undefined,
|
|
162
|
+
},
|
|
163
|
+
circles: {
|
|
164
|
+
type: Array as PropType<CircleOptions[]>,
|
|
165
|
+
default: () => undefined,
|
|
166
|
+
},
|
|
167
|
+
overlays: {
|
|
168
|
+
type: Object as PropType<MapOverlays>,
|
|
169
|
+
default: () => undefined,
|
|
170
|
+
},
|
|
171
|
+
width: {
|
|
172
|
+
type: String,
|
|
173
|
+
default: '100%',
|
|
174
|
+
},
|
|
175
|
+
height: {
|
|
176
|
+
type: String,
|
|
177
|
+
default: '100%',
|
|
178
|
+
},
|
|
179
|
+
showControls: {
|
|
180
|
+
type: Boolean,
|
|
181
|
+
default: true,
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const emit = defineEmits<{
|
|
186
|
+
(e: 'update:center', val: LatLngTuple): void;
|
|
187
|
+
(e: 'update:zoom', val: number): void;
|
|
188
|
+
(e: 'ready', engine: IMapEngine): void;
|
|
189
|
+
(e: 'move', val: { center: LatLngTuple; zoom: number }): void;
|
|
190
|
+
(e: 'moveend', val: { center: LatLngTuple; zoom: number }): void;
|
|
191
|
+
(e: 'zoom', val: number): void;
|
|
192
|
+
(e: 'zoomend', val: number): void;
|
|
193
|
+
(e: 'click', val: { latLng: LatLngTuple; point: Point }): void;
|
|
194
|
+
(e: 'marker-click', val: MarkerOptions): void;
|
|
195
|
+
(e: 'polyline-click', val: PolylineOptions): void;
|
|
196
|
+
(e: 'polygon-click', val: PolygonOptions): void;
|
|
197
|
+
(e: 'circle-click', val: CircleOptions): void;
|
|
198
|
+
(e: 'overlay-click', val: OverlayClickEvent): void;
|
|
199
|
+
}>();
|
|
200
|
+
|
|
201
|
+
const canvasId = `uni_leaflet_canvas_${Math.random().toString(36).slice(2, 9)}`;
|
|
202
|
+
const appMapId = `uni_leaflet_app_${Math.random().toString(36).slice(2, 9)}`;
|
|
203
|
+
const instance = getCurrentInstance();
|
|
204
|
+
const h5ContainerRef = ref<HTMLElement | null>(null);
|
|
205
|
+
|
|
206
|
+
// Unified Engine Instance
|
|
207
|
+
let engine: IMapEngine | null = null;
|
|
208
|
+
let isInternalUpdating = false;
|
|
209
|
+
|
|
210
|
+
// Compute payload for renderjs on App-Plus
|
|
211
|
+
const mapPropsPayload = computed(() => ({
|
|
212
|
+
appMapId,
|
|
213
|
+
center: props.center,
|
|
214
|
+
zoom: props.zoom,
|
|
215
|
+
minZoom: props.minZoom,
|
|
216
|
+
maxZoom: props.maxZoom,
|
|
217
|
+
tileUrl: props.tileUrl,
|
|
218
|
+
subdomains: props.subdomains,
|
|
219
|
+
layers: props.layers,
|
|
220
|
+
markers: props.markers,
|
|
221
|
+
polylines: props.polylines,
|
|
222
|
+
polygons: props.polygons,
|
|
223
|
+
circles: props.circles,
|
|
224
|
+
overlays: props.overlays,
|
|
225
|
+
}));
|
|
226
|
+
|
|
227
|
+
// Touch & Mouse Wheel handlers for Canvas/Mini-program
|
|
228
|
+
function handleTouchStart(e: any) {
|
|
229
|
+
if (engine && 'handleTouchStart' in engine) {
|
|
230
|
+
(engine as any).handleTouchStart(e);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function handleTouchMove(e: any) {
|
|
235
|
+
if (engine && 'handleTouchMove' in engine) {
|
|
236
|
+
(engine as any).handleTouchMove(e);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function handleTouchEnd(e: any) {
|
|
241
|
+
if (engine && 'handleTouchEnd' in engine) {
|
|
242
|
+
(engine as any).handleTouchEnd(e);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function handleWheel(e: any) {
|
|
247
|
+
if (e && typeof e.preventDefault === 'function') {
|
|
248
|
+
e.preventDefault();
|
|
249
|
+
}
|
|
250
|
+
if (e && typeof e.stopPropagation === 'function') {
|
|
251
|
+
e.stopPropagation();
|
|
252
|
+
}
|
|
253
|
+
if (engine && 'handleWheel' in engine) {
|
|
254
|
+
(engine as any).handleWheel(e);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Unified Zoom Controls
|
|
259
|
+
function handleZoomIn() {
|
|
260
|
+
if (engine) {
|
|
261
|
+
engine.zoomIn();
|
|
262
|
+
} else {
|
|
263
|
+
const curZoom = props.zoom;
|
|
264
|
+
const nextZoom = Math.min(props.maxZoom, Math.round(curZoom + 1));
|
|
265
|
+
emit('update:zoom', nextZoom);
|
|
266
|
+
emit('zoom', nextZoom);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function handleZoomOut() {
|
|
271
|
+
if (engine) {
|
|
272
|
+
engine.zoomOut();
|
|
273
|
+
} else {
|
|
274
|
+
const curZoom = props.zoom;
|
|
275
|
+
const nextZoom = Math.max(props.minZoom, Math.round(curZoom - 1));
|
|
276
|
+
emit('update:zoom', nextZoom);
|
|
277
|
+
emit('zoom', nextZoom);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Unified Watchers
|
|
282
|
+
watch(
|
|
283
|
+
() => props.center,
|
|
284
|
+
(newCenter) => {
|
|
285
|
+
if (!newCenter || isInternalUpdating || !engine) return;
|
|
286
|
+
const curCenter = engine.getCenter();
|
|
287
|
+
if (
|
|
288
|
+
Math.abs(curCenter[0] - newCenter[0]) > 1e-6 ||
|
|
289
|
+
Math.abs(curCenter[1] - newCenter[1]) > 1e-6
|
|
290
|
+
) {
|
|
291
|
+
engine.setCenter(newCenter);
|
|
292
|
+
}
|
|
293
|
+
},
|
|
294
|
+
{ deep: true }
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
watch(
|
|
298
|
+
() => props.zoom,
|
|
299
|
+
(newZoom) => {
|
|
300
|
+
if (newZoom === undefined || isInternalUpdating || !engine) return;
|
|
301
|
+
if (Math.abs(engine.getZoom() - newZoom) > 0.01) {
|
|
302
|
+
engine.setZoom(newZoom);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
watch(
|
|
308
|
+
() => props.layers,
|
|
309
|
+
(newLayers) => {
|
|
310
|
+
if (newLayers && engine) {
|
|
311
|
+
engine.setLayers(newLayers);
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
{ deep: true }
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
watch(
|
|
318
|
+
[() => props.tileUrl, () => props.subdomains],
|
|
319
|
+
([newUrl, newSubdomains]) => {
|
|
320
|
+
if (!newUrl || !engine || props.layers) return;
|
|
321
|
+
engine.setTileUrl(newUrl, newSubdomains as string[]);
|
|
322
|
+
},
|
|
323
|
+
{ deep: true }
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
watch(
|
|
327
|
+
() => props.showControls,
|
|
328
|
+
(newVal) => {
|
|
329
|
+
if (engine && typeof (engine as any).setShowControls === 'function') {
|
|
330
|
+
(engine as any).setShowControls(newVal);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
// Overlays Watchers
|
|
336
|
+
function syncCombinedOverlays() {
|
|
337
|
+
if (!engine) return;
|
|
338
|
+
const combined: MapOverlays = {
|
|
339
|
+
...(props.overlays || {}),
|
|
340
|
+
};
|
|
341
|
+
if (props.markers !== undefined) combined.markers = props.markers;
|
|
342
|
+
if (props.polylines !== undefined) combined.polylines = props.polylines;
|
|
343
|
+
if (props.polygons !== undefined) combined.polygons = props.polygons;
|
|
344
|
+
if (props.circles !== undefined) combined.circles = props.circles;
|
|
345
|
+
engine.setOverlays(combined);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
watch(
|
|
349
|
+
[
|
|
350
|
+
() => props.overlays,
|
|
351
|
+
() => props.markers,
|
|
352
|
+
() => props.polylines,
|
|
353
|
+
() => props.polygons,
|
|
354
|
+
() => props.circles,
|
|
355
|
+
],
|
|
356
|
+
() => {
|
|
357
|
+
syncCombinedOverlays();
|
|
358
|
+
},
|
|
359
|
+
{ deep: true }
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
// Map Event dispatching (shared across H5, App renderjs, Canvas)
|
|
363
|
+
function onMapMove(centerOrPayload: any, zoom?: number) {
|
|
364
|
+
let center: LatLngTuple;
|
|
365
|
+
let z: number;
|
|
366
|
+
if (Array.isArray(centerOrPayload)) {
|
|
367
|
+
center = centerOrPayload as LatLngTuple;
|
|
368
|
+
z = zoom ?? props.zoom;
|
|
369
|
+
} else if (centerOrPayload && centerOrPayload.center) {
|
|
370
|
+
center = centerOrPayload.center;
|
|
371
|
+
z = centerOrPayload.zoom ?? props.zoom;
|
|
372
|
+
} else {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
isInternalUpdating = true;
|
|
376
|
+
emit('update:center', center);
|
|
377
|
+
emit('update:zoom', z);
|
|
378
|
+
emit('move', { center, zoom: z });
|
|
379
|
+
setTimeout(() => {
|
|
380
|
+
isInternalUpdating = false;
|
|
381
|
+
}, 0);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function onMapMoveEnd(centerOrPayload: any, zoom?: number) {
|
|
385
|
+
let center: LatLngTuple;
|
|
386
|
+
let z: number;
|
|
387
|
+
if (Array.isArray(centerOrPayload)) {
|
|
388
|
+
center = centerOrPayload as LatLngTuple;
|
|
389
|
+
z = zoom ?? props.zoom;
|
|
390
|
+
} else if (centerOrPayload && centerOrPayload.center) {
|
|
391
|
+
center = centerOrPayload.center;
|
|
392
|
+
z = centerOrPayload.zoom ?? props.zoom;
|
|
393
|
+
} else {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
isInternalUpdating = true;
|
|
397
|
+
emit('update:center', center);
|
|
398
|
+
emit('update:zoom', z);
|
|
399
|
+
emit('moveend', { center, zoom: z });
|
|
400
|
+
setTimeout(() => {
|
|
401
|
+
isInternalUpdating = false;
|
|
402
|
+
}, 50);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function onMapZoom(zoom: number) {
|
|
406
|
+
isInternalUpdating = true;
|
|
407
|
+
emit('update:zoom', zoom);
|
|
408
|
+
emit('zoom', zoom);
|
|
409
|
+
setTimeout(() => {
|
|
410
|
+
isInternalUpdating = false;
|
|
411
|
+
}, 0);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function onMapZoomEnd(zoom: number) {
|
|
415
|
+
isInternalUpdating = true;
|
|
416
|
+
emit('update:zoom', zoom);
|
|
417
|
+
emit('zoomend', zoom);
|
|
418
|
+
setTimeout(() => {
|
|
419
|
+
isInternalUpdating = false;
|
|
420
|
+
}, 50);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function onMapClick(latLngOrPayload: any, point?: Point) {
|
|
424
|
+
let latLng: LatLngTuple;
|
|
425
|
+
let pt: Point;
|
|
426
|
+
if (Array.isArray(latLngOrPayload)) {
|
|
427
|
+
latLng = latLngOrPayload as LatLngTuple;
|
|
428
|
+
pt = point || { x: 0, y: 0 };
|
|
429
|
+
} else if (latLngOrPayload && latLngOrPayload.latLng) {
|
|
430
|
+
latLng = latLngOrPayload.latLng;
|
|
431
|
+
pt = latLngOrPayload.point || { x: 0, y: 0 };
|
|
432
|
+
} else {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
emit('click', { latLng, point: pt });
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function onOverlayClick(event: OverlayClickEvent) {
|
|
439
|
+
emit('overlay-click', event);
|
|
440
|
+
if (event.type === 'marker') emit('marker-click', event.data as MarkerOptions);
|
|
441
|
+
if (event.type === 'polyline') emit('polyline-click', event.data as PolylineOptions);
|
|
442
|
+
if (event.type === 'polygon') emit('polygon-click', event.data as PolygonOptions);
|
|
443
|
+
if (event.type === 'circle') emit('circle-click', event.data as CircleOptions);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Handler when renderjs is ready on App-Plus
|
|
447
|
+
function onRenderjsReady() {
|
|
448
|
+
console.log('App renderjs Leaflet map is ready');
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Lifecycle Hooks
|
|
452
|
+
onMounted(async () => {
|
|
453
|
+
const initialOverlays: MapOverlays = {
|
|
454
|
+
...(props.overlays || {}),
|
|
455
|
+
};
|
|
456
|
+
if (props.markers) initialOverlays.markers = props.markers;
|
|
457
|
+
if (props.polylines) initialOverlays.polylines = props.polylines;
|
|
458
|
+
if (props.polygons) initialOverlays.polygons = props.polygons;
|
|
459
|
+
if (props.circles) initialOverlays.circles = props.circles;
|
|
460
|
+
|
|
461
|
+
const commonOptions = {
|
|
462
|
+
center: props.center,
|
|
463
|
+
zoom: props.zoom,
|
|
464
|
+
minZoom: props.minZoom,
|
|
465
|
+
maxZoom: props.maxZoom,
|
|
466
|
+
tileUrl: props.tileUrl,
|
|
467
|
+
subdomains: props.subdomains,
|
|
468
|
+
layers: props.layers,
|
|
469
|
+
overlays: initialOverlays,
|
|
470
|
+
showControls: props.showControls,
|
|
471
|
+
onMove: onMapMove,
|
|
472
|
+
onMoveEnd: onMapMoveEnd,
|
|
473
|
+
onZoom: onMapZoom,
|
|
474
|
+
onZoomEnd: onMapZoomEnd,
|
|
475
|
+
onClick: onMapClick,
|
|
476
|
+
onOverlayClick: onOverlayClick,
|
|
477
|
+
};
|
|
478
|
+
|
|
479
|
+
// #ifdef H5
|
|
480
|
+
if (h5ContainerRef.value) {
|
|
481
|
+
engine = await createMapEngine({
|
|
482
|
+
...commonOptions,
|
|
483
|
+
container: h5ContainerRef.value,
|
|
484
|
+
});
|
|
485
|
+
emit('ready', engine);
|
|
486
|
+
}
|
|
487
|
+
// #endif
|
|
488
|
+
|
|
489
|
+
// #ifdef MP
|
|
490
|
+
const setupCanvas = async (data: any) => {
|
|
491
|
+
const canvas = data.node;
|
|
492
|
+
const ctx = canvas.getContext('2d');
|
|
493
|
+
const width = data.width || 300;
|
|
494
|
+
const height = data.height || 300;
|
|
495
|
+
let dpr = 1;
|
|
496
|
+
try {
|
|
497
|
+
dpr = uni.getSystemInfoSync().pixelRatio || 1;
|
|
498
|
+
} catch (e) {}
|
|
499
|
+
|
|
500
|
+
engine = await createMapEngine({
|
|
501
|
+
...commonOptions,
|
|
502
|
+
canvas,
|
|
503
|
+
ctx,
|
|
504
|
+
width,
|
|
505
|
+
height,
|
|
506
|
+
dpr,
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
emit('ready', engine);
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
const initCanvas = (retryCount = 0) => {
|
|
513
|
+
const comp = instance?.proxy || (instance as any);
|
|
514
|
+
const query = uni.createSelectorQuery().in(comp);
|
|
515
|
+
(query.select('#' + canvasId) as any)
|
|
516
|
+
.fields({ node: true, size: true })
|
|
517
|
+
.exec(async (res: any) => {
|
|
518
|
+
const data = Array.isArray(res) ? res[0] : res;
|
|
519
|
+
if (!data || !data.node) {
|
|
520
|
+
const fallbackQuery = uni.createSelectorQuery().in(comp);
|
|
521
|
+
(fallbackQuery.select('.uni-leaflet-canvas') as any)
|
|
522
|
+
.fields({ node: true, size: true })
|
|
523
|
+
.exec(async (fbRes: any) => {
|
|
524
|
+
const fbData = Array.isArray(fbRes) ? fbRes[0] : fbRes;
|
|
525
|
+
if (fbData && fbData.node) {
|
|
526
|
+
await setupCanvas(fbData);
|
|
527
|
+
} else if (retryCount < 5) {
|
|
528
|
+
setTimeout(() => initCanvas(retryCount + 1), 80);
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
await setupCanvas(data);
|
|
534
|
+
});
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
setTimeout(() => initCanvas(0), 50);
|
|
538
|
+
// #endif
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
onUnmounted(() => {
|
|
542
|
+
engine?.destroy();
|
|
543
|
+
engine = null;
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
// Unified Exposed methods
|
|
548
|
+
defineExpose({
|
|
549
|
+
setCenter(center: LatLngTuple, animate = true) {
|
|
550
|
+
engine?.setCenter(center, animate);
|
|
551
|
+
},
|
|
552
|
+
setZoom(zoom: number) {
|
|
553
|
+
engine?.setZoom(zoom);
|
|
554
|
+
},
|
|
555
|
+
zoomIn() {
|
|
556
|
+
engine?.zoomIn();
|
|
557
|
+
},
|
|
558
|
+
zoomOut() {
|
|
559
|
+
engine?.zoomOut();
|
|
560
|
+
},
|
|
561
|
+
panTo(center: LatLngTuple, duration = 300) {
|
|
562
|
+
engine?.panTo(center, duration);
|
|
563
|
+
},
|
|
564
|
+
setTileUrl(url: string, subdomains?: string[]) {
|
|
565
|
+
engine?.setTileUrl(url, subdomains);
|
|
566
|
+
},
|
|
567
|
+
setLayers(layers: Array<string | TileLayerConfig>) {
|
|
568
|
+
engine?.setLayers(layers);
|
|
569
|
+
},
|
|
570
|
+
setOverlays(overlays: MapOverlays) {
|
|
571
|
+
engine?.setOverlays(overlays);
|
|
572
|
+
},
|
|
573
|
+
setMarkers(markers: MarkerOptions[]) {
|
|
574
|
+
engine?.setMarkers(markers);
|
|
575
|
+
},
|
|
576
|
+
setPolylines(polylines: PolylineOptions[]) {
|
|
577
|
+
engine?.setPolylines(polylines);
|
|
578
|
+
},
|
|
579
|
+
setPolygons(polygons: PolygonOptions[]) {
|
|
580
|
+
engine?.setPolygons(polygons);
|
|
581
|
+
},
|
|
582
|
+
setCircles(circles: CircleOptions[]) {
|
|
583
|
+
engine?.setCircles(circles);
|
|
584
|
+
},
|
|
585
|
+
clearOverlays() {
|
|
586
|
+
engine?.clearOverlays();
|
|
587
|
+
},
|
|
588
|
+
resize(width?: number, height?: number) {
|
|
589
|
+
engine?.resize(width, height);
|
|
590
|
+
},
|
|
591
|
+
getCenter(): LatLngTuple {
|
|
592
|
+
return engine ? engine.getCenter() : props.center;
|
|
593
|
+
},
|
|
594
|
+
getZoom(): number {
|
|
595
|
+
return engine ? engine.getZoom() : props.zoom;
|
|
596
|
+
},
|
|
597
|
+
getNativeInstance(): any {
|
|
598
|
+
return engine && typeof engine.getNativeInstance === 'function'
|
|
599
|
+
? engine.getNativeInstance()
|
|
600
|
+
: null;
|
|
601
|
+
},
|
|
602
|
+
});
|
|
603
|
+
</script>
|
|
604
|
+
|
|
605
|
+
<!-- #ifdef APP-PLUS -->
|
|
606
|
+
<script module="leafletRender" lang="renderjs" src="./app-render.js"></script>
|
|
607
|
+
<!-- #endif -->
|
|
608
|
+
|
|
609
|
+
<style scoped>
|
|
610
|
+
.uni-leaflet-wrapper {
|
|
611
|
+
position: relative;
|
|
612
|
+
width: 100%;
|
|
613
|
+
height: 100%;
|
|
614
|
+
overflow: hidden;
|
|
615
|
+
background-color: #f2efe9;
|
|
616
|
+
user-select: none;
|
|
617
|
+
-webkit-user-select: none;
|
|
618
|
+
touch-action: none;
|
|
619
|
+
overscroll-behavior: contain;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
.uni-leaflet-h5-container {
|
|
623
|
+
position: absolute;
|
|
624
|
+
left: 0;
|
|
625
|
+
top: 0;
|
|
626
|
+
width: 100%;
|
|
627
|
+
height: 100%;
|
|
628
|
+
z-index: 1;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.uni-leaflet-canvas {
|
|
632
|
+
position: absolute;
|
|
633
|
+
left: 0;
|
|
634
|
+
top: 0;
|
|
635
|
+
width: 100%;
|
|
636
|
+
height: 100%;
|
|
637
|
+
display: block;
|
|
638
|
+
z-index: 1;
|
|
639
|
+
touch-action: none;
|
|
640
|
+
overscroll-behavior: contain;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.uni-leaflet-controls {
|
|
644
|
+
position: absolute;
|
|
645
|
+
top: 24rpx;
|
|
646
|
+
right: 24rpx;
|
|
647
|
+
z-index: 9999 !important;
|
|
648
|
+
display: flex;
|
|
649
|
+
flex-direction: column;
|
|
650
|
+
background: #ffffff !important;
|
|
651
|
+
background-color: #ffffff !important;
|
|
652
|
+
border-radius: 16rpx;
|
|
653
|
+
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15), 0 2rpx 6rpx rgba(0, 0, 0, 0.08);
|
|
654
|
+
border: 1rpx solid #e2e8f0;
|
|
655
|
+
overflow: hidden;
|
|
656
|
+
pointer-events: auto;
|
|
657
|
+
width: 72rpx;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.control-btn {
|
|
661
|
+
width: 72rpx;
|
|
662
|
+
height: 72rpx;
|
|
663
|
+
line-height: 72rpx;
|
|
664
|
+
text-align: center;
|
|
665
|
+
font-size: 40rpx;
|
|
666
|
+
font-weight: bold;
|
|
667
|
+
color: #334155;
|
|
668
|
+
display: flex;
|
|
669
|
+
align-items: center;
|
|
670
|
+
justify-content: center;
|
|
671
|
+
cursor: pointer;
|
|
672
|
+
background-color: #ffffff !important;
|
|
673
|
+
transition: all 0.15s ease;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
.btn-hover {
|
|
677
|
+
background-color: #f1f5f9 !important;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.btn-text {
|
|
681
|
+
font-size: 40rpx;
|
|
682
|
+
font-weight: bold;
|
|
683
|
+
color: #334155;
|
|
684
|
+
line-height: 72rpx;
|
|
685
|
+
text-align: center;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
.control-divider {
|
|
689
|
+
width: 72rpx;
|
|
690
|
+
height: 1rpx;
|
|
691
|
+
background-color: #e2e8f0;
|
|
692
|
+
}
|
|
693
|
+
</style>
|
|
694
|
+
|
|
695
|
+
<style>
|
|
696
|
+
/* #ifndef MP */
|
|
697
|
+
@import 'leaflet/dist/leaflet.css';
|
|
698
|
+
/* #endif */
|
|
699
|
+
|
|
700
|
+
/* Global Leaflet Tooltip Styling */
|
|
701
|
+
.leaflet-tooltip.uni-overlay-label {
|
|
702
|
+
position: absolute !important;
|
|
703
|
+
background-color: rgba(255, 255, 255, 0.96) !important;
|
|
704
|
+
border: 1px solid #cbd5e1 !important;
|
|
705
|
+
border-radius: 6px !important;
|
|
706
|
+
padding: 3px 8px !important;
|
|
707
|
+
font-size: 12px !important;
|
|
708
|
+
font-weight: 600 !important;
|
|
709
|
+
color: #1e293b !important;
|
|
710
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12) !important;
|
|
711
|
+
white-space: nowrap !important;
|
|
712
|
+
pointer-events: none !important;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
.leaflet-tooltip-top.uni-overlay-label:before,
|
|
716
|
+
.leaflet-tooltip-top.uni-overlay-label:after {
|
|
717
|
+
content: "" !important;
|
|
718
|
+
position: absolute !important;
|
|
719
|
+
left: 50% !important;
|
|
720
|
+
transform: translateX(-50%) !important;
|
|
721
|
+
width: 0 !important;
|
|
722
|
+
height: 0 !important;
|
|
723
|
+
border-style: solid !important;
|
|
724
|
+
pointer-events: none !important;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/* Outer border triangle */
|
|
728
|
+
.leaflet-tooltip-top.uni-overlay-label:before {
|
|
729
|
+
bottom: -6px !important;
|
|
730
|
+
border-width: 6px 5px 0 5px !important;
|
|
731
|
+
border-color: #cbd5e1 transparent transparent transparent !important;
|
|
732
|
+
z-index: 1 !important;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/* Inner fill triangle */
|
|
736
|
+
.leaflet-tooltip-top.uni-overlay-label:after {
|
|
737
|
+
bottom: -5px !important;
|
|
738
|
+
border-width: 5px 4px 0 4px !important;
|
|
739
|
+
border-color: rgba(255, 255, 255, 0.96) transparent transparent transparent !important;
|
|
740
|
+
z-index: 2 !important;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
.uni-emoji-marker-icon,
|
|
744
|
+
.uni-custom-svg-icon,
|
|
745
|
+
.uni-custom-html-icon,
|
|
746
|
+
.uni-custom-img-icon,
|
|
747
|
+
.uni-custom-icon,
|
|
748
|
+
.uni-default-pin {
|
|
749
|
+
background: transparent !important;
|
|
750
|
+
border: none !important;
|
|
751
|
+
}
|
|
752
|
+
</style>
|