uni-leaflet 1.1.0 → 1.2.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 CHANGED
@@ -14,6 +14,22 @@
14
14
  - **H5 端**:采用标准 Leaflet.js DOM 渲染,支持完整 GIS 坐标变换与图层生态。
15
15
  - **App 端 (iOS / Android)**:采用 Leaflet.js + Uni-App `renderjs` 架构,配合**手势互斥锁(Anti-Feedback Loop)**与**图层增量 Diff 缓存**,彻底杜绝手势抖动与白屏闪烁,体验媲美原生。
16
16
  - **微信小程序端**:采用底层 Canvas 2D 高性能离屏/视口瓦片调度,支持 DPR 高分屏自适应,内置 Canvas 原生矢量缩放控件(零 DOM 遮挡)。
17
+ - 🫧 **高性能海量点聚合 (Marker Clustering)**:
18
+ - 支持屏幕像素距离阈值自适应网格聚类,动态渲染精致圆形数字气泡徽标。
19
+ - 点击聚合点自动局部平滑缩放并展开下钻,超过最大缩放层级自动散开为独立散点。
20
+ - 👤 **精美头像/照片气泡与脉冲光晕动效 (Avatar Marker Bubbles)**:
21
+ - 内置开箱即用的头像气泡模板 (`type: 'avatar'`),支持圆形头像裁剪、自定义边框色与线宽。
22
+ - 支持右上角自定义 Emoji 或文字角标 (`badgeText` / `badgeColor`,如 `'❤️'`, `'VIP'`, `'👑'`)。
23
+ - 支持呼吸心跳脉冲光晕微动效 (`pulse: true`),全端流畅硬件加速渲染。
24
+ - ✈️ **二次贝塞尔弧线旅行航线与粒子流光动效 (Curved Routes & Flowing Dashlines)**:
25
+ - 路线支持开启贝塞尔平滑弧线 (`curved: true`),自动计算旅行航线/航迹的大圆拱起曲率。
26
+ - 支持从起点向终点持续流动的虚线粒子流光动效 (`flowing: true`),H5/App 端 GPU CSS 关键帧加速,小程序端 Canvas 2D 高效补帧。
27
+ - 🌐 **GeoJSON 矢量图层全端跨端渲染**:
28
+ - 支持标准 GeoJSON (FeatureCollection / Feature / Point / LineString / Polygon / MultiPolygon)。
29
+ - 微信小程序 Canvas 2D 与 H5 / App Leaflet 端全功能对齐,支持动态响应式更新、样式函数与要素点击事件。
30
+ - 🎯 **智能视野自适应包裹 (Auto fitBounds)**:
31
+ - 一键计算四至范围并平滑缓动平移缩放至最佳视口,支持设置内边距 (`padding`) 与最大层级 (`maxZoom`)。
32
+ - 支持一键快捷包裹全部要素 (`'all'`)、点 (`'markers'`)、线 (`'polylines'`)、面 (`'polygons'`)、行政区划 (`'geojson'`) 或任意自定义足迹坐标集合。
17
33
  - 🎨 **丰富多样的图标样式支持 (Marker Icons)**:
18
34
  - **Emoji 字符图标**:免网络请求,轻量生动(如 `🏛️`, `🚩`, `☕`)。
19
35
  - **PNG / JPG / 本地图片 / 网络 URL**:支持本地 `/static/icons/xxx.png` 相对路径、网络图片或 Base64 Data URI。
@@ -248,9 +264,24 @@ const markers = ref<MarkerOptions[]>([
248
264
  },
249
265
  label: { text: '国家大剧院 (内联SVG)', offset: [0, -26] },
250
266
  },
267
+ // E. 头像/照片气泡 (开箱即用头像气泡模板,带角标与心跳光晕动效)
268
+ {
269
+ id: 'm5_avatar',
270
+ latLng: [39.9195, 116.3972],
271
+ title: '旅行博主 小羽',
272
+ icon: {
273
+ type: 'avatar',
274
+ url: '/static/icons/pin-purple.png',
275
+ size: [32, 32], // 默认与其它点状要素一致 [32, 32],亦可自定义如 [48, 48]
276
+ badgeText: '❤️', // 角标 Emoji 或文字
277
+ pulse: true, // 呼吸脉冲光晕微动效
278
+ borderColor: '#f43f5e', // 外边框颜色
279
+ },
280
+ label: { text: '小羽 (❤️ 实时足迹)' },
281
+ },
251
282
  ]);
252
283
 
253
- // 2. 路线折线 (带虚线与中点 Label)
284
+ // 2. 路线折线 (支持弧线旅行航线、流动虚线动效与中点 Label)
254
285
  const polylines = ref<PolylineOptions[]>([
255
286
  {
256
287
  id: 'l1',
@@ -263,6 +294,21 @@ const polylines = ref<PolylineOptions[]>([
263
294
  width: 5,
264
295
  label: '中轴线景观路线',
265
296
  },
297
+ {
298
+ id: 'l2',
299
+ latLngs: [
300
+ [39.9042, 116.4074], // 北京
301
+ [31.2304, 121.4737], // 上海
302
+ ],
303
+ color: '#06b6d4',
304
+ width: 4,
305
+ curved: true, // 开启二次贝塞尔弧线
306
+ curvature: 0.25, // 弧线曲率 (正数上弧,负数下弧)
307
+ flowing: true, // 开启粒子流光/流动蚂蚁线
308
+ flowSpeed: 1.5, // 流动速度
309
+ dashArray: [10, 8], // 虚线分段
310
+ label: '京沪航线 (弧线流光)',
311
+ },
266
312
  ]);
267
313
 
268
314
  // 3. 多边形区域 (带质心 Label)
@@ -301,6 +347,100 @@ function onOverlayClick(e: OverlayClickEvent) {
301
347
 
302
348
  ---
303
349
 
350
+ ### 5. 海量点聚合 (Marker Clustering)
351
+
352
+ 当页面存在成百上千个点要素时,开启 `enable-cluster` 可大幅提升渲染性能并避免视觉杂乱。聚合气泡不仅支持自适应计算数量,还支持点击局部展开下钻:
353
+
354
+ ```vue
355
+ <template>
356
+ <UniLeafletMap
357
+ :center="[39.9042, 116.4074]"
358
+ :zoom="12"
359
+ :markers="markers"
360
+ :enable-cluster="true"
361
+ :cluster-radius="60"
362
+ :cluster-max-zoom="17"
363
+ @cluster-click="onClusterClick"
364
+ @marker-click="onMarkerClick"
365
+ />
366
+ </template>
367
+
368
+ <script setup lang="ts">
369
+ import { ref } from 'vue';
370
+ import { UniLeafletMap } from 'uni-leaflet';
371
+ import type { MarkerOptions, ClusterGroup } from 'uni-leaflet';
372
+
373
+ const markers = ref<MarkerOptions[]>([
374
+ // 传入数十至成千上万个 Marker 均可流畅运行
375
+ { id: 'p1', latLng: [39.9042, 116.4074], title: '站点 1' },
376
+ { id: 'p2', latLng: [39.9055, 116.4085], title: '站点 2' },
377
+ { id: 'p3', latLng: [39.9080, 116.4110], title: '站点 3' },
378
+ ]);
379
+
380
+ function onClusterClick(cluster: ClusterGroup) {
381
+ console.log('点击了聚合气泡,包含点数:', cluster.count, '范围:', cluster.bounds);
382
+ }
383
+
384
+ function onMarkerClick(marker: MarkerOptions) {
385
+ console.log('点击了独立点:', marker.title);
386
+ }
387
+ </script>
388
+ ```
389
+
390
+ ---
391
+
392
+ ### 6. GeoJSON 矢量图层与行政区划边界
393
+
394
+ 支持直接载入中国行政区划、省市区边界或自定义地理要素 GeoJSON。全端(H5 / App / 微信小程序)无缝支持,支持动态样式函数与点击事件:
395
+
396
+ ```vue
397
+ <template>
398
+ <UniLeafletMap
399
+ ref="mapRef"
400
+ :geojson="districtGeoJson"
401
+ :geojson-style="getDistrictStyle"
402
+ @geojson-click="onGeoJsonClick"
403
+ />
404
+ </template>
405
+
406
+ <script setup lang="ts">
407
+ import { ref, onMounted } from 'vue';
408
+ import { UniLeafletMap } from 'uni-leaflet';
409
+ import type { GeoJsonStyleOptions } from 'uni-leaflet';
410
+
411
+ const mapRef = ref<InstanceType<typeof UniLeafletMap> | null>(null);
412
+ const districtGeoJson = ref<any>(null);
413
+
414
+ // 动态自定义样式配置或样式函数
415
+ function getDistrictStyle(feature: any): GeoJsonStyleOptions {
416
+ const isSelected = feature.properties?.name === '朝阳区';
417
+ return {
418
+ color: isSelected ? '#ef4444' : '#3b82f6',
419
+ weight: isSelected ? 3 : 1.5,
420
+ fillColor: isSelected ? '#ef4444' : '#60a5fa',
421
+ fillOpacity: isSelected ? 0.35 : 0.12,
422
+ };
423
+ }
424
+
425
+ function onGeoJsonClick(feature: any) {
426
+ uni.showToast({
427
+ title: `选中区域: ${feature.properties?.name || '未知区域'}`,
428
+ icon: 'none',
429
+ });
430
+ }
431
+
432
+ onMounted(async () => {
433
+ // 模拟异步拉取行政区划 GeoJSON 数据
434
+ // const res = await uni.request({ url: 'https://geo.datav.aliyun.com/areas_v3/bound/110000_full.json' });
435
+ // districtGeoJson.value = res.data;
436
+ // 载入完成后一键自适应缩放以包裹整个行政边界:
437
+ // mapRef.value?.fitBounds('geojson', { padding: [40, 40] });
438
+ });
439
+ </script>
440
+ ```
441
+
442
+ ---
443
+
304
444
  ## 📖 API 参考
305
445
 
306
446
  ### Props
@@ -319,6 +459,12 @@ function onOverlayClick(e: OverlayClickEvent) {
319
459
  | `polygons` | `PolygonOptions[]` | `[]` | 多边形面数组 |
320
460
  | `circles` | `CircleOptions[]` | `[]` | 圆形范围数组 |
321
461
  | `overlays` | `MapOverlays` | `undefined` | 综合覆盖物对象 |
462
+ | `geojson` | `any` | `null` | 标准 GeoJSON 数据对象 (支持 FeatureCollection / Feature / 几何对象),自动渲染城市或行政区边界 |
463
+ | `geojson-style` | `GeoJsonStyleOptions \| ((feature: any) => GeoJsonStyleOptions)` | `undefined` | GeoJSON 矢量样式配置 (可设置描边色 `color`、线宽 `weight`、填充色 `fillColor`、填充透明度 `fillOpacity`、虚线 `dashArray` 等) |
464
+ | `enable-cluster` | `boolean` | `false` | 是否开启点聚合功能。开启后在当前缩放级别下像素距离接近的点自动合并为圆形数字气泡 |
465
+ | `cluster-radius` | `number` | `60` | 点聚合的屏幕像素距离阈值 (px) |
466
+ | `cluster-max-zoom` | `number` | `18` | 最大聚合层级,超过此缩放层级不再聚合,显示为独立散点 |
467
+ | `cluster-options` | `ClusterOptions` | `undefined` | 高级点聚合配置对象 (包含 `radius`, `maxZoom`, `zoomOnClick` 等) |
322
468
  | `width` | `string` | `'100%'` | 地图宽度 |
323
469
  | `height` | `string` | `'100%'` | 地图高度 |
324
470
  | `show-controls` | `boolean` | `true` | 是否显示右上方缩放控件按钮(H5/App 为悬浮组件,小程序为 Canvas 2D 顶层渲染) |
@@ -329,13 +475,91 @@ function onOverlayClick(e: OverlayClickEvent) {
329
475
 
330
476
  | 字段 | 类型 | 默认值 | 说明 |
331
477
  | :--- | :--- | :--- | :--- |
478
+ | `type` | `'default' \| 'image' \| 'emoji' \| 'svg' \| 'html' \| 'avatar'` | `'default'` | 图标模板类型。设置为 `'avatar'` 时自动启用头像/照片气泡快捷模板 |
479
+ | `url` | `string` | `undefined` | 图片/头像缩略图路径、网络 URL 或 Base64 Data URI,如 `'/static/icons/pin-blue.png'` |
332
480
  | `text` | `string` | `undefined` | Emoji 字符或文字图标,如 `'🏛️'`, `'🚩'`, `'☕'` |
333
- | `url` | `string` | `undefined` | PNG / JPG / SVG 文件路径、网络 URL 或 Base64 Data URI,如 `'/static/icons/pin-blue.png'`、`'/static/icons/camera.svg'` |
334
481
  | `svg` | `string` | `undefined` | 原始 SVG XML 字符串,支持动态渐变与任意矢量图形,如 `'<svg viewBox="0 0 36 36">...</svg>'` |
335
482
  | `html` | `string` | `undefined` | 自定义 HTML 结构 (仅 H5 与 App renderjs 生效) |
336
483
  | `color` | `string` | `'#ef4444'` | 默认矢量 Pin 图钉的主题填充色 |
337
- | `size` | `[number, number]` | `[32, 32]` | 图标渲染尺寸 `[宽, 高]` (px) |
338
- | `anchor` | `[number, number]` | 居中或底部 | 图标锚点 `[x, y]`。图钉尖部对齐常用 `[width/2, height]`,圆形徽章居中对齐常用 `[width/2, height/2]` |
484
+ | `size` | `[number, number]` | `[32, 32]` | 图标渲染尺寸 `[宽, 高]` (px),气泡头像默认也与其它点状要素一致为 `[32, 32]` |
485
+ | `anchor` | `[number, number]` | 居中或底部 | 图标锚点 `[x, y]` |
486
+ | `badgeText` | `string` | `undefined` | 头像气泡右上角角标文本或 Emoji,如 `'❤️'`, `'VIP'`, `'👑'` |
487
+ | `badgeColor` | `string` | `'#ffffff'` (白底) | 角标背景填充色,默认采用精致白底,也可自定义设置 |
488
+ | `pulse` | `boolean` | `false` | 头像气泡是否开启呼吸心跳脉冲光晕微动效 |
489
+ | `pulseColor` | `string` | 与 `borderColor` 一致 | 脉冲光晕波纹颜色 |
490
+ | `borderColor` | `string` | `'#3b82f6'` | 头像气泡外圆环边框颜色 |
491
+ | `borderWidth` | `number` | `3` | 头像气泡外圆环边框宽度 (px) |
492
+
493
+ ---
494
+
495
+ ### PolylineOptions (折线路线配置)
496
+
497
+ | 字段 | 类型 | 默认值 | 说明 |
498
+ | :--- | :--- | :--- | :--- |
499
+ | `id` | `string \| number` | `undefined` | 路线唯一标识 |
500
+ | `latLngs` | `LatLngTuple[]` | **必填** | 折线坐标点数组 `[[lat, lng], ...]` |
501
+ | `color` | `string` | `'#3b82f6'` | 路线颜色 |
502
+ | `width` | `number` | `4` | 路线线宽 (px) |
503
+ | `opacity` | `number` | `1.0` | 线条透明度 (0 ~ 1) |
504
+ | `dashArray` | `number[]` | `undefined` | 虚线分段配置,例如 `[10, 8]` |
505
+ | `dashOffset` | `number` | `0` | 静态虚线偏移量 |
506
+ | `curved` | `boolean` | `false` | 是否开启二次贝塞尔平滑弧线(旅行航线/航迹线) |
507
+ | `curvature` | `number` | `0.2` | 弧线曲率系数(正数向左/上一侧拱起,负数向右/下一侧拱起) |
508
+ | `flowing` | `boolean` | `false` | 是否开启粒子流光/虚线流动动效 |
509
+ | `flowSpeed` | `number` | `1.0` | 流动速度倍率(数值越大流动越快,负数反向流动) |
510
+ | `label` | `string \| MarkerLabelOptions` | `undefined` | 路线中点徽标文字配置 |
511
+
512
+ ---
513
+
514
+ ### PolygonOptions (多边形面配置)
515
+
516
+ | 字段 | 类型 | 默认值 | 说明 |
517
+ | :--- | :--- | :--- | :--- |
518
+ | `id` | `string \| number` | `undefined` | 多边形唯一标识 |
519
+ | `latLngs` | `LatLngTuple[]` | **必填** | 外轮廓顶点坐标数组 `[[lat, lng], ...]` |
520
+ | `color` | `string` | `'#3b82f6'` | 边界线条颜色 |
521
+ | `width` | `number` | `2` | 边界线宽 (px) |
522
+ | `fillColor` | `string` | `'#3b82f6'` | 填充色 (支持十六进制、`rgba(...)`) |
523
+ | `fillOpacity` | `number` | `0.2` | 填充透明度 (0 ~ 1) |
524
+ | `label` | `string \| MarkerLabelOptions` | `undefined` | 几何质心中心徽标文字配置 |
525
+
526
+ ---
527
+
528
+ ### CircleOptions (圆形范围配置)
529
+
530
+ | 字段 | 类型 | 默认值 | 说明 |
531
+ | :--- | :--- | :--- | :--- |
532
+ | `id` | `string \| number` | `undefined` | 圆形唯一标识 |
533
+ | `latLng` | `LatLngTuple` | **必填** | 圆心坐标 `[lat, lng]` |
534
+ | `radius` | `number` | **必填** | 真实地理半径 (单位:米) |
535
+ | `color` | `string` | `'#10b981'` | 边框线条颜色 |
536
+ | `width` | `number` | `2` | 边框线宽 (px) |
537
+ | `fillColor` | `string` | `'#10b981'` | 填充色 |
538
+ | `fillOpacity` | `number` | `0.2` | 填充透明度 (0 ~ 1) |
539
+ | `label` | `string \| MarkerLabelOptions` | `undefined` | 圆心徽标文字配置 |
540
+
541
+ ---
542
+
543
+ ### ClusterOptions (海量点聚合高级配置)
544
+
545
+ | 字段 | 类型 | 默认值 | 说明 |
546
+ | :--- | :--- | :--- | :--- |
547
+ | `enable` | `boolean` | `true` | 是否启用点聚合 |
548
+ | `clusterRadius` | `number` | `60` | 点聚合的屏幕像素网格半径阈值 (px) |
549
+ | `clusterMaxZoom` | `number` | `17` | 最大聚合缩放级别,达到或超过该层级自动展开为单点 |
550
+
551
+ ---
552
+
553
+ ### GeoJsonStyleOptions (GeoJSON 样式配置)
554
+
555
+ | 字段 | 类型 | 默认值 | 说明 |
556
+ | :--- | :--- | :--- | :--- |
557
+ | `color` | `string` | `'#3b82f6'` | 矢量边框描边颜色 |
558
+ | `weight` | `number` | `2` | 边框线宽 (px) |
559
+ | `opacity` | `number` | `0.8` | 边框透明度 |
560
+ | `fillColor` | `string` | `'#3b82f6'` | 面要素填充颜色 |
561
+ | `fillOpacity` | `number` | `0.15` | 面要素填充透明度 |
562
+ | `dashArray` | `number[]` | `undefined` | 虚线线型配置,例如 `[6, 6]` |
339
563
 
340
564
  ---
341
565
 
@@ -351,6 +575,8 @@ function onOverlayClick(e: OverlayClickEvent) {
351
575
  | `@zoomend` | `(zoom: number) => void` | 地图缩放结束时触发 |
352
576
  | `@overlay-click` | `(event: OverlayClickEvent) => void` | 点击任意点、线、面、圆要素时触发 |
353
577
  | `@marker-click` | `(marker: MarkerOptions) => void` | 点击点图标时触发 |
578
+ | `@cluster-click` | `(cluster: ClusterGroup) => void` | 点击聚合气泡时触发 (默认自动缩放并局部展开包裹聚合点) |
579
+ | `@geojson-click` | `(feature: any) => void` | 点击 GeoJSON 矢量要素时触发 |
354
580
  | `@polyline-click`| `(polyline: PolylineOptions) => void`| 点击折线时触发 |
355
581
  | `@polygon-click` | `(polygon: PolygonOptions) => void` | 点击多边形时触发 |
356
582
  | `@circle-click` | `(circle: CircleOptions) => void` | 点击圆形时触发 |
@@ -366,18 +592,91 @@ function onOverlayClick(e: OverlayClickEvent) {
366
592
  | `zoomIn` | `()` | 放大一级 |
367
593
  | `zoomOut` | `()` | 缩小一级 |
368
594
  | `panTo` | `(center: LatLngTuple, duration?: number)` | 平滑缓动平移至指定坐标 |
595
+ | `fitBounds` | `(bounds: LatLngBoundsExpression \| LatLngTuple[] \| MapBounds, options?: { padding?: [number, number]; maxZoom?: number; animate?: boolean })` | 自动缩放视野以包裹指定坐标点/足迹点集合,支持设置内边距与最大缩放层级 |
369
596
  | `setTileUrl` | `(url: string, subdomains?: string[])` | 切换单底图源 |
370
597
  | `setLayers` | `(layers: TileLayerConfig[])` | 动态切换多图层源 |
371
598
  | `setMarkers` | `(markers: MarkerOptions[])` | 动态更新点标注 |
372
599
  | `setPolylines`| `(polylines: PolylineOptions[])` | 动态更新折线 |
373
600
  | `setPolygons` | `(polygons: PolygonOptions[])` | 动态更新多边形 |
374
601
  | `setCircles` | `(circles: CircleOptions[])` | 动态更新圆形 |
375
- | `clearOverlays`| `()` | 清空所有覆盖物 |
602
+ | `setGeoJSON` | `(data: any, style?: GeoJsonStyle)` | 动态更新/替换 GeoJSON 矢量图层数据与样式 |
603
+ | `clearOverlays`| `()` | 清空所有覆盖物与矢量图层 |
376
604
  | `resize` | `(width?: number, height?: number)` | 重新计算视口尺寸与 DPR 适配 |
377
605
  | `getCenter` | `()` | 获取当前中心点 `[lat, lng]` |
378
606
  | `getZoom` | `()` | 获取当前缩放层级 |
379
607
  | `getNativeInstance` | `()` | 获取底层原生实例(H5/App 为 Leaflet `L.map`,小程序为 `canvas` 节点) |
380
608
 
609
+ #### 🎯 自动视野缩放包裹指定要素 (fitBounds 示例)
610
+
611
+ ```vue
612
+ <template>
613
+ <UniLeafletMap
614
+ ref="mapRef"
615
+ :markers="markers"
616
+ :polylines="polylines"
617
+ :polygons="polygons"
618
+ :geojson="cityGeoJson"
619
+ />
620
+ </template>
621
+
622
+ <script setup lang="ts">
623
+ import { ref } from 'vue';
624
+ import UniLeafletMap from '@/components/uni-leaflet/UniLeafletMap.vue';
625
+
626
+ const mapRef = ref<InstanceType<typeof UniLeafletMap> | null>(null);
627
+
628
+ // 1. 快捷包裹【城市行政区划 GeoJSON 边界】(传入 'geojson' 或直接传入 GeoJSON 数据)
629
+ mapRef.value?.fitBounds('geojson', { padding: [50, 50] });
630
+
631
+ // 2. 快捷包裹【点】(传入 'markers' / 'points',或传入点标记数组/经纬度数组)
632
+ mapRef.value?.fitBounds('markers', { padding: [40, 40] });
633
+
634
+ // 3. 快捷包裹【线】(传入 'polylines' / 'lines',或传入折线对象列表)
635
+ mapRef.value?.fitBounds('polylines', { padding: [40, 40] });
636
+
637
+ // 4. 快捷包裹【面】(传入 'polygons',或传入多边形对象列表)
638
+ mapRef.value?.fitBounds('polygons', { padding: [40, 40] });
639
+
640
+ // 5. 包裹【全部当前显示要素】(传入 'all' 或直接不传参)
641
+ mapRef.value?.fitBounds('all', { padding: [50, 50] });
642
+
643
+ // 5. 传入自定义足迹点经纬度数组
644
+ const trackPoints = [
645
+ [39.9042, 116.4074],
646
+ [39.9163, 116.3972],
647
+ [39.9289, 116.3883],
648
+ ];
649
+ mapRef.value?.fitBounds(trackPoints, {
650
+ padding: [40, 40],
651
+ maxZoom: 16,
652
+ animate: true,
653
+ });
654
+ </script>
655
+ ```
656
+
657
+ #### ✈️ 弧线旅行航线与流动虚线动效
658
+
659
+ 支持将两点或多点折线基于二次贝塞尔曲线生成平滑的大圆/旅行航线弧度,并开启从起点向终点持续流动的虚线粒子感动效。全端统一实现(H5/App 端采用高效 GPU CSS `@keyframes` 动画,小程序端采用 Canvas 2D `lineDashOffset` 按需动画循环):
660
+
661
+ ```ts
662
+ interface PolylineOptions {
663
+ id?: string | number;
664
+ latLngs: LatLngTuple[];
665
+ color?: string;
666
+ width?: number;
667
+ opacity?: number;
668
+ dashArray?: number[]; // 虚线分段配置,例如 [10, 8]
669
+ dashOffset?: number; // 静态虚线偏移量
670
+ curved?: boolean; // 是否开启二次贝塞尔平滑弧线 (默认 false)
671
+ curvature?: number; // 弧线曲率 (默认 0.2,正数上弧/左凸,负数下弧/右凸)
672
+ flowing?: boolean; // 是否开启粒子流光/流动虚线动效 (默认 false)
673
+ flowSpeed?: number; // 流动速度倍率 (默认 1.0,支持正负数控制流动方向)
674
+ label?: string | MarkerLabelOptions; // 路线中点徽标文字
675
+ }
676
+ ```
677
+
678
+ > **提示**:当设置 `curved: true` 时,调用 `fitBounds('polylines')` 会自动包含二次贝塞尔弧顶的实际最高/最低经纬度,确保完整包裹航线全貌。
679
+
381
680
  ---
382
681
 
383
682
  ## 📄 开源协议