expo-gaode-map 2.2.30-next.0 → 2.2.30

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.
Files changed (82) hide show
  1. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +4 -2
  2. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +117 -57
  3. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +8 -15
  4. package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +20 -6
  5. package/android/src/main/java/expo/modules/gaodemap/overlays/ClusterView.kt +24 -13
  6. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerBitmapRenderer.kt +351 -0
  7. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +94 -310
  8. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +3 -3
  9. package/build/ExpoGaodeMapModule.d.ts +13 -5
  10. package/build/ExpoGaodeMapModule.d.ts.map +1 -1
  11. package/build/ExpoGaodeMapModule.js +166 -34
  12. package/build/ExpoGaodeMapModule.js.map +1 -1
  13. package/build/ExpoGaodeMapView.d.ts.map +1 -1
  14. package/build/ExpoGaodeMapView.js +12 -0
  15. package/build/ExpoGaodeMapView.js.map +1 -1
  16. package/build/components/AreaMaskOverlay.d.ts +5 -0
  17. package/build/components/AreaMaskOverlay.d.ts.map +1 -0
  18. package/build/components/AreaMaskOverlay.js +20 -0
  19. package/build/components/AreaMaskOverlay.js.map +1 -0
  20. package/build/components/FoldableMapView.d.ts.map +1 -1
  21. package/build/components/FoldableMapView.js +115 -104
  22. package/build/components/FoldableMapView.js.map +1 -1
  23. package/build/components/RouteOverlay.d.ts +5 -0
  24. package/build/components/RouteOverlay.d.ts.map +1 -0
  25. package/build/components/RouteOverlay.js +20 -0
  26. package/build/components/RouteOverlay.js.map +1 -0
  27. package/build/components/overlays/Cluster.d.ts.map +1 -1
  28. package/build/components/overlays/Cluster.js +12 -0
  29. package/build/components/overlays/Cluster.js.map +1 -1
  30. package/build/components/overlays/Marker.d.ts.map +1 -1
  31. package/build/components/overlays/Marker.js +86 -3
  32. package/build/components/overlays/Marker.js.map +1 -1
  33. package/build/hooks/useRoutePlayback.d.ts +4 -0
  34. package/build/hooks/useRoutePlayback.d.ts.map +1 -0
  35. package/build/hooks/useRoutePlayback.js +310 -0
  36. package/build/hooks/useRoutePlayback.js.map +1 -0
  37. package/build/index.d.ts +4 -1
  38. package/build/index.d.ts.map +1 -1
  39. package/build/index.js +4 -2
  40. package/build/index.js.map +1 -1
  41. package/build/types/common.types.d.ts +29 -5
  42. package/build/types/common.types.d.ts.map +1 -1
  43. package/build/types/common.types.js +5 -5
  44. package/build/types/common.types.js.map +1 -1
  45. package/build/types/index.d.ts +3 -2
  46. package/build/types/index.d.ts.map +1 -1
  47. package/build/types/index.js.map +1 -1
  48. package/build/types/location.types.d.ts +28 -0
  49. package/build/types/location.types.d.ts.map +1 -1
  50. package/build/types/location.types.js +5 -0
  51. package/build/types/location.types.js.map +1 -1
  52. package/build/types/map-view.types.d.ts +22 -22
  53. package/build/types/map-view.types.d.ts.map +1 -1
  54. package/build/types/map-view.types.js.map +1 -1
  55. package/build/types/native-module.types.d.ts +2 -2
  56. package/build/types/native-module.types.d.ts.map +1 -1
  57. package/build/types/native-module.types.js.map +1 -1
  58. package/build/types/overlays.types.d.ts +14 -0
  59. package/build/types/overlays.types.d.ts.map +1 -1
  60. package/build/types/overlays.types.js.map +1 -1
  61. package/build/types/route-playback.types.d.ts +118 -0
  62. package/build/types/route-playback.types.d.ts.map +1 -0
  63. package/build/types/route-playback.types.js +2 -0
  64. package/build/types/route-playback.types.js.map +1 -0
  65. package/build/utils/RouteUtils.d.ts +8 -0
  66. package/build/utils/RouteUtils.d.ts.map +1 -0
  67. package/build/utils/RouteUtils.js +140 -0
  68. package/build/utils/RouteUtils.js.map +1 -0
  69. package/ios/ExpoGaodeMapModule.swift +41 -22
  70. package/ios/ExpoGaodeMapView.swift +236 -241
  71. package/ios/ExpoGaodeMapViewModule.swift +16 -11
  72. package/ios/managers/UIManager.swift +5 -4
  73. package/ios/modules/LocationManager.swift +32 -9
  74. package/ios/overlays/ClusterView.swift +114 -12
  75. package/ios/overlays/ClusterViewModule.swift +5 -1
  76. package/ios/overlays/MarkerView.swift +195 -18
  77. package/ios/overlays/MarkerViewModule.swift +7 -7
  78. package/package.json +6 -6
  79. package/build/utils/throttle.d.ts +0 -10
  80. package/build/utils/throttle.d.ts.map +0 -1
  81. package/build/utils/throttle.js +0 -19
  82. package/build/utils/throttle.js.map +0 -1
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ import { normalizeLatLngList } from '../utils/GeoUtils';
3
+ import { parseMultiRingPolyline } from '../utils/RouteUtils';
4
+ import { Polygon } from './overlays';
5
+ export function AreaMaskOverlay({ rings, polygonProps }) {
6
+ // 支持直接传多环坐标,或传高德返回的 polyline 字符串。
7
+ // 最终都落到 Polygon 的“带孔多边形”能力上。
8
+ const normalizedPoints = React.useMemo(() => {
9
+ if (typeof rings === 'string') {
10
+ return parseMultiRingPolyline(rings).rings;
11
+ }
12
+ return normalizeLatLngList(rings);
13
+ }, [rings]);
14
+ if (!normalizedPoints.length) {
15
+ return null;
16
+ }
17
+ return (<Polygon points={normalizedPoints} fillColor={polygonProps?.fillColor ?? 'rgba(15, 23, 42, 0.45)'} strokeColor={polygonProps?.strokeColor ?? 'rgba(15, 23, 42, 0.8)'} strokeWidth={polygonProps?.strokeWidth ?? 1} {...polygonProps}/>);
18
+ }
19
+ export default AreaMaskOverlay;
20
+ //# sourceMappingURL=AreaMaskOverlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AreaMaskOverlay.js","sourceRoot":"","sources":["../../src/components/AreaMaskOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErC,MAAM,UAAU,eAAe,CAAC,EAAE,KAAK,EAAE,YAAY,EAAwB;IAC3E,kCAAkC;IAClC,6BAA6B;IAC7B,MAAM,gBAAgB,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE;QAC1C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC9B,OAAO,sBAAsB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;QAC7C,CAAC;QAED,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CACL,CAAC,OAAO,CACN,MAAM,CAAC,CAAC,gBAAgB,CAAC,CACzB,SAAS,CAAC,CAAC,YAAY,EAAE,SAAS,IAAI,wBAAwB,CAAC,CAC/D,WAAW,CAAC,CAAC,YAAY,EAAE,WAAW,IAAI,uBAAuB,CAAC,CAClE,WAAW,CAAC,CAAC,YAAY,EAAE,WAAW,IAAI,CAAC,CAAC,CAC5C,IAAI,YAAY,CAAC,EACjB,CACH,CAAC;AACJ,CAAC;AAED,eAAe,eAAe,CAAC","sourcesContent":["import * as React from 'react';\n\nimport type { AreaMaskOverlayProps } from '../types/route-playback.types';\nimport { normalizeLatLngList } from '../utils/GeoUtils';\nimport { parseMultiRingPolyline } from '../utils/RouteUtils';\nimport { Polygon } from './overlays';\n\nexport function AreaMaskOverlay({ rings, polygonProps }: AreaMaskOverlayProps) {\n // 支持直接传多环坐标,或传高德返回的 polyline 字符串。\n // 最终都落到 Polygon 的“带孔多边形”能力上。\n const normalizedPoints = React.useMemo(() => {\n if (typeof rings === 'string') {\n return parseMultiRingPolyline(rings).rings;\n }\n\n return normalizeLatLngList(rings);\n }, [rings]);\n\n if (!normalizedPoints.length) {\n return null;\n }\n\n return (\n <Polygon\n points={normalizedPoints}\n fillColor={polygonProps?.fillColor ?? 'rgba(15, 23, 42, 0.45)'}\n strokeColor={polygonProps?.strokeColor ?? 'rgba(15, 23, 42, 0.8)'}\n strokeWidth={polygonProps?.strokeWidth ?? 1}\n {...polygonProps}\n />\n );\n}\n\nexport default AreaMaskOverlay;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"FoldableMapView.d.ts","sourceRoot":"","sources":["../../src/components/FoldableMapView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAI3D,OAAO,EAAoB,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oBAAoB;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe;IACf,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IACvE,eAAe;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,cAAc;IACd,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAwI1D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,EACnC,MAAM,CAAC,EAAE,cAAc;;;;EAkExB"}
1
+ {"version":3,"file":"FoldableMapView.d.ts","sourceRoot":"","sources":["../../src/components/FoldableMapView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA+C,MAAM,OAAO,CAAC;AAIpE,OAAO,EAAoB,UAAU,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEpD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,oBAAoB;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iBAAiB;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,qBAAqB;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe;IACf,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IACvE,eAAe;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,cAAc;IACd,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AA2ED,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CA6F1D,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,UAAU,CAAC,EACnC,MAAM,CAAC,EAAE,cAAc;;;;EAgExB"}
@@ -1,108 +1,127 @@
1
- import React, { useEffect, useRef, useState } from 'react';
1
+ import React, { useEffect, useMemo, useRef, useState } from 'react';
2
2
  import { Platform } from 'react-native';
3
3
  import ExpoGaodeMapView from '../ExpoGaodeMapView';
4
4
  import { PlatformDetector, FoldState } from '../utils/PlatformDetector';
5
+ const DEFAULT_FOLDABLE_CONFIG = {
6
+ autoAdjustZoom: true,
7
+ unfoldedZoomDelta: 1,
8
+ keepCenterOnFold: true,
9
+ onFoldStateChange: () => { },
10
+ debug: false,
11
+ };
12
+ function clampZoomLevel(zoom) {
13
+ return Math.max(3, Math.min(20, zoom));
14
+ }
15
+ function createFoldableConfig(config) {
16
+ return {
17
+ ...DEFAULT_FOLDABLE_CONFIG,
18
+ ...config,
19
+ };
20
+ }
21
+ async function applyFoldStateCameraAdjustment(mapRef, oldState, newState, config, debugPrefix) {
22
+ if (!mapRef.current || !config.autoAdjustZoom) {
23
+ return;
24
+ }
25
+ const currentCamera = await mapRef.current.getCameraPosition();
26
+ if (!currentCamera) {
27
+ if (config.debug) {
28
+ console.warn(`[${debugPrefix}] 无法获取相机位置`);
29
+ }
30
+ return;
31
+ }
32
+ const isUnfolding = newState === FoldState.UNFOLDED && oldState === FoldState.FOLDED;
33
+ const isFolding = newState === FoldState.FOLDED && oldState === FoldState.UNFOLDED;
34
+ if (config.debug) {
35
+ console.log(`[${debugPrefix}] 折叠状态变化:`, {
36
+ oldState,
37
+ newState,
38
+ isUnfolding,
39
+ isFolding,
40
+ currentZoom: currentCamera.zoom,
41
+ });
42
+ }
43
+ if (!isUnfolding && !isFolding) {
44
+ return;
45
+ }
46
+ const currentZoom = currentCamera.zoom ?? 15;
47
+ const zoomDelta = isUnfolding ? config.unfoldedZoomDelta : -config.unfoldedZoomDelta;
48
+ const nextZoom = clampZoomLevel(currentZoom + zoomDelta);
49
+ if (config.debug) {
50
+ console.log(`[${debugPrefix}] 调整缩放:`, {
51
+ oldZoom: currentZoom,
52
+ newZoom: nextZoom,
53
+ delta: zoomDelta,
54
+ });
55
+ }
56
+ await mapRef.current.moveCamera({
57
+ target: config.keepCenterOnFold ? currentCamera.target : undefined,
58
+ zoom: nextZoom,
59
+ }, 300);
60
+ }
5
61
  export const FoldableMapView = ({ foldableConfig, ...mapProps }) => {
6
62
  const mapRef = useRef(null);
7
63
  const [currentFoldState, setCurrentFoldState] = useState(FoldState.UNKNOWN);
8
64
  const [deviceInfo, setDeviceInfo] = useState(PlatformDetector.getDeviceInfo());
9
- const config = {
10
- autoAdjustZoom: true,
11
- unfoldedZoomDelta: 1,
12
- keepCenterOnFold: true,
13
- onFoldStateChange: () => { },
14
- debug: false,
15
- ...foldableConfig,
16
- };
65
+ const config = useMemo(() => createFoldableConfig(foldableConfig), [foldableConfig]);
66
+ const configRef = useRef(config);
67
+ const foldStateRef = useRef(currentFoldState);
68
+ useEffect(() => {
69
+ configRef.current = config;
70
+ }, [config]);
71
+ useEffect(() => {
72
+ foldStateRef.current = currentFoldState;
73
+ }, [currentFoldState]);
74
+ useEffect(() => {
75
+ const latestDeviceInfo = PlatformDetector.getDeviceInfo();
76
+ setDeviceInfo(latestDeviceInfo);
77
+ }, []);
17
78
  useEffect(() => {
18
79
  // 仅在 Android 折叠屏设备上启用
19
80
  if (Platform.OS !== 'android' || !deviceInfo.isFoldable) {
20
- if (config.debug) {
81
+ if (configRef.current.debug) {
21
82
  console.log('[FoldableMapView] 非折叠屏设备,跳过适配');
22
83
  }
23
84
  return;
24
85
  }
25
- if (config.debug) {
86
+ const initialState = PlatformDetector.getFoldState();
87
+ foldStateRef.current = initialState;
88
+ setCurrentFoldState(initialState);
89
+ if (configRef.current.debug) {
26
90
  console.log('[FoldableMapView] 初始化折叠屏适配');
27
91
  console.log('设备信息:', deviceInfo);
28
- console.log('初始折叠状态:', currentFoldState);
92
+ console.log('初始折叠状态:', initialState);
29
93
  }
30
94
  // 监听屏幕尺寸变化
31
95
  const removeListener = PlatformDetector.addDimensionChangeListener(async (newInfo) => {
32
96
  const newFoldState = PlatformDetector.getFoldState();
33
- if (config.debug) {
97
+ const previousFoldState = foldStateRef.current;
98
+ const latestConfig = configRef.current;
99
+ if (latestConfig.debug) {
34
100
  console.log('[FoldableMapView] 屏幕尺寸变化');
35
101
  console.log('新设备信息:', newInfo);
36
102
  console.log('新折叠状态:', newFoldState);
37
103
  }
38
104
  // 折叠状态变化时的处理
39
- if (newFoldState !== currentFoldState && currentFoldState !== FoldState.UNKNOWN) {
40
- await handleFoldStateChange(currentFoldState, newFoldState, newInfo);
105
+ if (newFoldState !== previousFoldState && previousFoldState !== FoldState.UNKNOWN) {
106
+ try {
107
+ await applyFoldStateCameraAdjustment(mapRef, previousFoldState, newFoldState, latestConfig, 'FoldableMapView');
108
+ }
109
+ catch (error) {
110
+ if (latestConfig.debug) {
111
+ console.error('[FoldableMapView] 处理折叠状态变化失败:', error);
112
+ }
113
+ }
41
114
  }
115
+ foldStateRef.current = newFoldState;
42
116
  setCurrentFoldState(newFoldState);
43
117
  setDeviceInfo(newInfo);
44
118
  // 触发回调
45
- config.onFoldStateChange(newFoldState, newInfo);
119
+ latestConfig.onFoldStateChange(newFoldState, newInfo);
46
120
  });
47
- // 设置初始状态
48
- const initialState = PlatformDetector.getFoldState();
49
- setCurrentFoldState(initialState);
50
121
  return () => {
51
122
  removeListener();
52
123
  };
53
- }, []);
54
- /**
55
- * 处理折叠状态变化
56
- */
57
- const handleFoldStateChange = async (oldState, newState, newInfo) => {
58
- if (!mapRef.current) {
59
- return;
60
- }
61
- try {
62
- // 获取当前地图状态
63
- const currentCamera = await mapRef.current.getCameraPosition?.();
64
- if (!currentCamera) {
65
- if (config.debug) {
66
- console.warn('[FoldableMapView] 无法获取相机位置');
67
- }
68
- return;
69
- }
70
- const isUnfolding = newState === FoldState.UNFOLDED && oldState === FoldState.FOLDED;
71
- const isFolding = newState === FoldState.FOLDED && oldState === FoldState.UNFOLDED;
72
- if (config.debug) {
73
- console.log('[FoldableMapView] 折叠状态变化:', {
74
- oldState,
75
- newState,
76
- isUnfolding,
77
- isFolding,
78
- currentZoom: currentCamera.zoom,
79
- });
80
- }
81
- // 展开时增加缩放级别,折叠时减少
82
- if (config.autoAdjustZoom && (isUnfolding || isFolding)) {
83
- const currentZoom = currentCamera.zoom ?? 15;
84
- const zoomDelta = isUnfolding ? config.unfoldedZoomDelta : -config.unfoldedZoomDelta;
85
- const newZoom = Math.max(3, Math.min(20, currentZoom + zoomDelta));
86
- if (config.debug) {
87
- console.log('[FoldableMapView] 调整缩放:', {
88
- oldZoom: currentZoom,
89
- newZoom,
90
- delta: zoomDelta,
91
- });
92
- }
93
- // 保持中心点,只调整缩放
94
- await mapRef.current.moveCamera({
95
- target: config.keepCenterOnFold ? currentCamera.target : undefined,
96
- zoom: newZoom,
97
- }, 300);
98
- }
99
- }
100
- catch (error) {
101
- if (config.debug) {
102
- console.error('[FoldableMapView] 处理折叠状态变化失败:');
103
- }
104
- }
105
- };
124
+ }, [deviceInfo.isFoldable]);
106
125
  return (<ExpoGaodeMapView ref={mapRef} {...mapProps}/>);
107
126
  };
108
127
  /**
@@ -113,53 +132,45 @@ export const FoldableMapView = ({ foldableConfig, ...mapProps }) => {
113
132
  export function useFoldableMap(mapRef, config) {
114
133
  const [foldState, setFoldState] = useState(FoldState.UNKNOWN);
115
134
  const [deviceInfo, setDeviceInfo] = useState(PlatformDetector.getDeviceInfo());
116
- const mergedConfig = {
117
- autoAdjustZoom: true,
118
- unfoldedZoomDelta: 1,
119
- keepCenterOnFold: true,
120
- onFoldStateChange: () => { },
121
- debug: false,
122
- ...config,
123
- };
135
+ const mergedConfig = useMemo(() => createFoldableConfig(config), [config]);
136
+ const foldStateRef = useRef(foldState);
137
+ const configRef = useRef(mergedConfig);
138
+ useEffect(() => {
139
+ foldStateRef.current = foldState;
140
+ }, [foldState]);
141
+ useEffect(() => {
142
+ configRef.current = mergedConfig;
143
+ }, [mergedConfig]);
124
144
  useEffect(() => {
125
145
  if (Platform.OS !== 'android' || !deviceInfo.isFoldable) {
126
146
  return;
127
147
  }
148
+ const initialState = PlatformDetector.getFoldState();
149
+ foldStateRef.current = initialState;
150
+ setFoldState(initialState);
128
151
  const removeListener = PlatformDetector.addDimensionChangeListener(async (newInfo) => {
129
152
  const newFoldState = PlatformDetector.getFoldState();
130
- if (newFoldState !== foldState && foldState !== FoldState.UNKNOWN) {
131
- // 处理折叠状态变化
132
- if (mapRef.current && mergedConfig.autoAdjustZoom) {
133
- try {
134
- const currentCamera = await mapRef.current.getCameraPosition();
135
- if (currentCamera) {
136
- const currentZoom = currentCamera.zoom ?? 15;
137
- const isUnfolding = newFoldState === FoldState.UNFOLDED && foldState === FoldState.FOLDED;
138
- const zoomDelta = isUnfolding ? mergedConfig.unfoldedZoomDelta : -mergedConfig.unfoldedZoomDelta;
139
- const newZoom = Math.max(3, Math.min(20, currentZoom + zoomDelta));
140
- await mapRef.current.moveCamera({
141
- target: mergedConfig.keepCenterOnFold ? currentCamera.target : undefined,
142
- zoom: newZoom,
143
- }, 300);
144
- }
145
- }
146
- catch (error) {
147
- if (mergedConfig.debug) {
148
- console.error('[useFoldableMap] 调整失败:');
149
- }
153
+ const previousFoldState = foldStateRef.current;
154
+ const latestConfig = configRef.current;
155
+ if (newFoldState !== previousFoldState && previousFoldState !== FoldState.UNKNOWN) {
156
+ try {
157
+ await applyFoldStateCameraAdjustment(mapRef, previousFoldState, newFoldState, latestConfig, 'useFoldableMap');
158
+ }
159
+ catch (error) {
160
+ if (latestConfig.debug) {
161
+ console.error('[useFoldableMap] 调整失败:', error);
150
162
  }
151
163
  }
152
164
  }
165
+ foldStateRef.current = newFoldState;
153
166
  setFoldState(newFoldState);
154
167
  setDeviceInfo(newInfo);
155
- mergedConfig.onFoldStateChange(newFoldState, newInfo);
168
+ latestConfig.onFoldStateChange(newFoldState, newInfo);
156
169
  });
157
- const initialState = PlatformDetector.getFoldState();
158
- setFoldState(initialState);
159
170
  return () => {
160
171
  removeListener();
161
172
  };
162
- }, [foldState, deviceInfo.isFoldable]);
173
+ }, [deviceInfo.isFoldable, mapRef]);
163
174
  return {
164
175
  foldState,
165
176
  deviceInfo,
@@ -1 +1 @@
1
- {"version":3,"file":"FoldableMapView.js","sourceRoot":"","sources":["../../src/components/FoldableMapView.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { Platform } from 'react-native';\nimport ExpoGaodeMapView from '../ExpoGaodeMapView';\n\nimport { PlatformDetector, DeviceInfo, FoldState } from '../utils/PlatformDetector';\nimport { MapViewProps, MapViewRef } from '../types';\n\n/**\n * 折叠屏适配配置\n */\nexport interface FoldableConfig {\n /** 折叠时是否自动调整缩放级别 */\n autoAdjustZoom?: boolean;\n /** 展开时的缩放级别增量 */\n unfoldedZoomDelta?: number;\n /** 是否在折叠/展开时保持中心点 */\n keepCenterOnFold?: boolean;\n /** 折叠状态变化回调 */\n onFoldStateChange?: (state: FoldState, deviceInfo: DeviceInfo) => void;\n /** 是否启用调试日志 */\n debug?: boolean;\n}\n\n/**\n * 折叠屏地图视图组件\n * \n * 自动适配折叠屏设备的展开/折叠状态变化\n */\nexport interface FoldableMapViewProps extends MapViewProps {\n /** 折叠屏适配配置 */\n foldableConfig?: FoldableConfig;\n}\n\nexport const FoldableMapView: React.FC<FoldableMapViewProps> = ({\n foldableConfig,\n ...mapProps\n}) => {\n const mapRef = useRef<MapViewRef>(null);\n const [currentFoldState, setCurrentFoldState] = useState<FoldState>(FoldState.UNKNOWN);\n const [deviceInfo, setDeviceInfo] = useState<DeviceInfo>(PlatformDetector.getDeviceInfo());\n\n const config: Required<FoldableConfig> = {\n autoAdjustZoom: true,\n unfoldedZoomDelta: 1,\n keepCenterOnFold: true,\n onFoldStateChange: () => {},\n debug: false,\n ...foldableConfig,\n };\n\n useEffect(() => {\n // 仅在 Android 折叠屏设备上启用\n if (Platform.OS !== 'android' || !deviceInfo.isFoldable) {\n if (config.debug) {\n console.log('[FoldableMapView] 非折叠屏设备,跳过适配');\n }\n return;\n }\n\n if (config.debug) {\n console.log('[FoldableMapView] 初始化折叠屏适配');\n console.log('设备信息:', deviceInfo);\n console.log('初始折叠状态:', currentFoldState);\n }\n\n // 监听屏幕尺寸变化\n const removeListener = PlatformDetector.addDimensionChangeListener(\n async (newInfo: DeviceInfo) => {\n const newFoldState = PlatformDetector.getFoldState();\n \n if (config.debug) {\n console.log('[FoldableMapView] 屏幕尺寸变化');\n console.log('新设备信息:', newInfo);\n console.log('新折叠状态:', newFoldState);\n }\n\n // 折叠状态变化时的处理\n if (newFoldState !== currentFoldState && currentFoldState !== FoldState.UNKNOWN) {\n await handleFoldStateChange(currentFoldState, newFoldState, newInfo);\n }\n\n setCurrentFoldState(newFoldState);\n setDeviceInfo(newInfo);\n \n // 触发回调\n config.onFoldStateChange(newFoldState, newInfo);\n }\n );\n\n // 设置初始状态\n const initialState = PlatformDetector.getFoldState();\n setCurrentFoldState(initialState);\n\n return () => {\n removeListener();\n };\n }, []);\n\n /**\n * 处理折叠状态变化\n */\n const handleFoldStateChange = async (\n oldState: FoldState,\n newState: FoldState,\n newInfo: DeviceInfo\n ) => {\n if (!mapRef.current) {\n return;\n }\n\n try {\n // 获取当前地图状态\n const currentCamera = await mapRef.current.getCameraPosition?.();\n \n if (!currentCamera) {\n if (config.debug) {\n console.warn('[FoldableMapView] 无法获取相机位置');\n }\n return;\n }\n\n const isUnfolding = newState === FoldState.UNFOLDED && oldState === FoldState.FOLDED;\n const isFolding = newState === FoldState.FOLDED && oldState === FoldState.UNFOLDED;\n\n if (config.debug) {\n console.log('[FoldableMapView] 折叠状态变化:', {\n oldState,\n newState,\n isUnfolding,\n isFolding,\n currentZoom: currentCamera.zoom,\n });\n }\n\n // 展开时增加缩放级别,折叠时减少\n if (config.autoAdjustZoom && (isUnfolding || isFolding)) {\n const currentZoom = currentCamera.zoom ?? 15;\n const zoomDelta = isUnfolding ? config.unfoldedZoomDelta : -config.unfoldedZoomDelta;\n const newZoom = Math.max(3, Math.min(20, currentZoom + zoomDelta));\n\n if (config.debug) {\n console.log('[FoldableMapView] 调整缩放:', {\n oldZoom: currentZoom,\n newZoom,\n delta: zoomDelta,\n });\n }\n\n // 保持中心点,只调整缩放\n await mapRef.current.moveCamera({\n target: config.keepCenterOnFold ? currentCamera.target : undefined,\n zoom: newZoom,\n }, 300);\n }\n } catch (error) {\n if (config.debug) {\n console.error('[FoldableMapView] 处理折叠状态变化失败:');\n }\n }\n };\n\n \n\n return (\n <ExpoGaodeMapView\n ref={mapRef}\n {...mapProps}\n />\n );\n};\n\n/**\n * 折叠屏适配 Hook\n * \n * 用于在现有地图组件中添加折叠屏适配功能\n */\nexport function useFoldableMap(\n mapRef: React.RefObject<MapViewRef>,\n config?: FoldableConfig\n) {\n const [foldState, setFoldState] = useState<FoldState>(FoldState.UNKNOWN);\n const [deviceInfo, setDeviceInfo] = useState<DeviceInfo>(PlatformDetector.getDeviceInfo());\n\n const mergedConfig: Required<FoldableConfig> = {\n autoAdjustZoom: true,\n unfoldedZoomDelta: 1,\n keepCenterOnFold: true,\n onFoldStateChange: () => {},\n debug: false,\n ...config,\n };\n\n useEffect(() => {\n if (Platform.OS !== 'android' || !deviceInfo.isFoldable) {\n return;\n }\n\n const removeListener = PlatformDetector.addDimensionChangeListener(\n async (newInfo: DeviceInfo) => {\n const newFoldState = PlatformDetector.getFoldState();\n \n if (newFoldState !== foldState && foldState !== FoldState.UNKNOWN) {\n // 处理折叠状态变化\n if (mapRef.current && mergedConfig.autoAdjustZoom) {\n try {\n const currentCamera = await mapRef.current.getCameraPosition();\n if (currentCamera) {\n const currentZoom = currentCamera.zoom ?? 15;\n const isUnfolding = newFoldState === FoldState.UNFOLDED && foldState === FoldState.FOLDED;\n const zoomDelta = isUnfolding ? mergedConfig.unfoldedZoomDelta : -mergedConfig.unfoldedZoomDelta;\n const newZoom = Math.max(3, Math.min(20, currentZoom + zoomDelta));\n\n await mapRef.current.moveCamera({\n target: mergedConfig.keepCenterOnFold ? currentCamera.target : undefined,\n zoom: newZoom,\n }, 300);\n }\n } catch (error) {\n if (mergedConfig.debug) {\n console.error('[useFoldableMap] 调整失败:');\n }\n }\n }\n }\n\n setFoldState(newFoldState);\n setDeviceInfo(newInfo);\n mergedConfig.onFoldStateChange(newFoldState, newInfo);\n }\n );\n\n const initialState = PlatformDetector.getFoldState();\n setFoldState(initialState);\n\n return () => {\n removeListener();\n };\n }, [foldState, deviceInfo.isFoldable]);\n\n return {\n foldState,\n deviceInfo,\n isFoldable: deviceInfo.isFoldable,\n };\n}"]}
1
+ {"version":3,"file":"FoldableMapView.js","sourceRoot":"","sources":["../../src/components/FoldableMapView.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { Platform } from 'react-native';\nimport ExpoGaodeMapView from '../ExpoGaodeMapView';\n\nimport { PlatformDetector, DeviceInfo, FoldState } from '../utils/PlatformDetector';\nimport { MapViewProps, MapViewRef } from '../types';\n\n/**\n * 折叠屏适配配置\n */\nexport interface FoldableConfig {\n /** 折叠时是否自动调整缩放级别 */\n autoAdjustZoom?: boolean;\n /** 展开时的缩放级别增量 */\n unfoldedZoomDelta?: number;\n /** 是否在折叠/展开时保持中心点 */\n keepCenterOnFold?: boolean;\n /** 折叠状态变化回调 */\n onFoldStateChange?: (state: FoldState, deviceInfo: DeviceInfo) => void;\n /** 是否启用调试日志 */\n debug?: boolean;\n}\n\n/**\n * 折叠屏地图视图组件\n * \n * 自动适配折叠屏设备的展开/折叠状态变化\n */\nexport interface FoldableMapViewProps extends MapViewProps {\n /** 折叠屏适配配置 */\n foldableConfig?: FoldableConfig;\n}\n\nconst DEFAULT_FOLDABLE_CONFIG: Required<FoldableConfig> = {\n autoAdjustZoom: true,\n unfoldedZoomDelta: 1,\n keepCenterOnFold: true,\n onFoldStateChange: () => {},\n debug: false,\n};\n\nfunction clampZoomLevel(zoom: number): number {\n return Math.max(3, Math.min(20, zoom));\n}\n\nfunction createFoldableConfig(config?: FoldableConfig): Required<FoldableConfig> {\n return {\n ...DEFAULT_FOLDABLE_CONFIG,\n ...config,\n };\n}\n\nasync function applyFoldStateCameraAdjustment(\n mapRef: React.RefObject<MapViewRef | null>,\n oldState: FoldState,\n newState: FoldState,\n config: Required<FoldableConfig>,\n debugPrefix: 'FoldableMapView' | 'useFoldableMap'\n): Promise<void> {\n if (!mapRef.current || !config.autoAdjustZoom) {\n return;\n }\n\n const currentCamera = await mapRef.current.getCameraPosition();\n if (!currentCamera) {\n if (config.debug) {\n console.warn(`[${debugPrefix}] 无法获取相机位置`);\n }\n return;\n }\n\n const isUnfolding = newState === FoldState.UNFOLDED && oldState === FoldState.FOLDED;\n const isFolding = newState === FoldState.FOLDED && oldState === FoldState.UNFOLDED;\n\n if (config.debug) {\n console.log(`[${debugPrefix}] 折叠状态变化:`, {\n oldState,\n newState,\n isUnfolding,\n isFolding,\n currentZoom: currentCamera.zoom,\n });\n }\n\n if (!isUnfolding && !isFolding) {\n return;\n }\n\n const currentZoom = currentCamera.zoom ?? 15;\n const zoomDelta = isUnfolding ? config.unfoldedZoomDelta : -config.unfoldedZoomDelta;\n const nextZoom = clampZoomLevel(currentZoom + zoomDelta);\n\n if (config.debug) {\n console.log(`[${debugPrefix}] 调整缩放:`, {\n oldZoom: currentZoom,\n newZoom: nextZoom,\n delta: zoomDelta,\n });\n }\n\n await mapRef.current.moveCamera({\n target: config.keepCenterOnFold ? currentCamera.target : undefined,\n zoom: nextZoom,\n }, 300);\n}\n\nexport const FoldableMapView: React.FC<FoldableMapViewProps> = ({\n foldableConfig,\n ...mapProps\n}) => {\n const mapRef = useRef<MapViewRef>(null);\n const [currentFoldState, setCurrentFoldState] = useState<FoldState>(FoldState.UNKNOWN);\n const [deviceInfo, setDeviceInfo] = useState<DeviceInfo>(PlatformDetector.getDeviceInfo());\n const config = useMemo(() => createFoldableConfig(foldableConfig), [foldableConfig]);\n const configRef = useRef(config);\n const foldStateRef = useRef(currentFoldState);\n\n useEffect(() => {\n configRef.current = config;\n }, [config]);\n\n useEffect(() => {\n foldStateRef.current = currentFoldState;\n }, [currentFoldState]);\n\n useEffect(() => {\n const latestDeviceInfo = PlatformDetector.getDeviceInfo();\n setDeviceInfo(latestDeviceInfo);\n }, []);\n\n useEffect(() => {\n // 仅在 Android 折叠屏设备上启用\n if (Platform.OS !== 'android' || !deviceInfo.isFoldable) {\n if (configRef.current.debug) {\n console.log('[FoldableMapView] 非折叠屏设备,跳过适配');\n }\n return;\n }\n\n const initialState = PlatformDetector.getFoldState();\n foldStateRef.current = initialState;\n setCurrentFoldState(initialState);\n\n if (configRef.current.debug) {\n console.log('[FoldableMapView] 初始化折叠屏适配');\n console.log('设备信息:', deviceInfo);\n console.log('初始折叠状态:', initialState);\n }\n\n // 监听屏幕尺寸变化\n const removeListener = PlatformDetector.addDimensionChangeListener(\n async (newInfo: DeviceInfo) => {\n const newFoldState = PlatformDetector.getFoldState();\n const previousFoldState = foldStateRef.current;\n const latestConfig = configRef.current;\n \n if (latestConfig.debug) {\n console.log('[FoldableMapView] 屏幕尺寸变化');\n console.log('新设备信息:', newInfo);\n console.log('新折叠状态:', newFoldState);\n }\n\n // 折叠状态变化时的处理\n if (newFoldState !== previousFoldState && previousFoldState !== FoldState.UNKNOWN) {\n try {\n await applyFoldStateCameraAdjustment(\n mapRef,\n previousFoldState,\n newFoldState,\n latestConfig,\n 'FoldableMapView'\n );\n } catch (error) {\n if (latestConfig.debug) {\n console.error('[FoldableMapView] 处理折叠状态变化失败:', error);\n }\n }\n }\n\n foldStateRef.current = newFoldState;\n setCurrentFoldState(newFoldState);\n setDeviceInfo(newInfo);\n \n // 触发回调\n latestConfig.onFoldStateChange(newFoldState, newInfo);\n }\n );\n\n return () => {\n removeListener();\n };\n }, [deviceInfo.isFoldable]);\n\n return (\n <ExpoGaodeMapView\n ref={mapRef}\n {...mapProps}\n />\n );\n};\n\n/**\n * 折叠屏适配 Hook\n * \n * 用于在现有地图组件中添加折叠屏适配功能\n */\nexport function useFoldableMap(\n mapRef: React.RefObject<MapViewRef>,\n config?: FoldableConfig\n) {\n const [foldState, setFoldState] = useState<FoldState>(FoldState.UNKNOWN);\n const [deviceInfo, setDeviceInfo] = useState<DeviceInfo>(PlatformDetector.getDeviceInfo());\n const mergedConfig = useMemo(() => createFoldableConfig(config), [config]);\n const foldStateRef = useRef(foldState);\n const configRef = useRef(mergedConfig);\n\n useEffect(() => {\n foldStateRef.current = foldState;\n }, [foldState]);\n\n useEffect(() => {\n configRef.current = mergedConfig;\n }, [mergedConfig]);\n\n useEffect(() => {\n if (Platform.OS !== 'android' || !deviceInfo.isFoldable) {\n return;\n }\n\n const initialState = PlatformDetector.getFoldState();\n foldStateRef.current = initialState;\n setFoldState(initialState);\n\n const removeListener = PlatformDetector.addDimensionChangeListener(\n async (newInfo: DeviceInfo) => {\n const newFoldState = PlatformDetector.getFoldState();\n const previousFoldState = foldStateRef.current;\n const latestConfig = configRef.current;\n \n if (newFoldState !== previousFoldState && previousFoldState !== FoldState.UNKNOWN) {\n try {\n await applyFoldStateCameraAdjustment(\n mapRef,\n previousFoldState,\n newFoldState,\n latestConfig,\n 'useFoldableMap'\n );\n } catch (error) {\n if (latestConfig.debug) {\n console.error('[useFoldableMap] 调整失败:', error);\n }\n }\n }\n\n foldStateRef.current = newFoldState;\n setFoldState(newFoldState);\n setDeviceInfo(newInfo);\n latestConfig.onFoldStateChange(newFoldState, newInfo);\n }\n );\n\n return () => {\n removeListener();\n };\n }, [deviceInfo.isFoldable, mapRef]);\n\n return {\n foldState,\n deviceInfo,\n isFoldable: deviceInfo.isFoldable,\n };\n}\n"]}
@@ -0,0 +1,5 @@
1
+ import * as React from 'react';
2
+ import type { RouteOverlayProps } from '../types/route-playback.types';
3
+ export declare function RouteOverlay({ points, showStartMarker, showEndMarker, polylineProps, startMarkerProps, endMarkerProps, }: RouteOverlayProps): React.JSX.Element | null;
4
+ export default RouteOverlay;
5
+ //# sourceMappingURL=RouteOverlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouteOverlay.d.ts","sourceRoot":"","sources":["../../src/components/RouteOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAIvE,wBAAgB,YAAY,CAAC,EAC3B,MAAM,EACN,eAAsB,EACtB,aAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,cAAc,GACf,EAAE,iBAAiB,4BAuCnB;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ import { normalizeLatLngList } from '../utils/GeoUtils';
3
+ import { Marker, Polyline } from './overlays';
4
+ export function RouteOverlay({ points, showStartMarker = true, showEndMarker = true, polylineProps, startMarkerProps, endMarkerProps, }) {
5
+ // 统一封装“主路径 + 起点 + 终点”的常见展示组合,
6
+ // 这样业务侧不需要每次手动拼装 3 个覆盖物。
7
+ const normalizedPoints = React.useMemo(() => normalizeLatLngList(points), [points]);
8
+ if (normalizedPoints.length === 0) {
9
+ return null;
10
+ }
11
+ const start = normalizedPoints[0];
12
+ const end = normalizedPoints[normalizedPoints.length - 1];
13
+ return (<>
14
+ <Polyline points={normalizedPoints} strokeWidth={polylineProps?.strokeWidth ?? 6} strokeColor={polylineProps?.strokeColor ?? '#2563eb'} {...polylineProps}/>
15
+ {showStartMarker ? (<Marker position={start} title={startMarkerProps?.title ?? '起点'} {...startMarkerProps}/>) : null}
16
+ {showEndMarker ? (<Marker position={end} title={endMarkerProps?.title ?? '终点'} {...endMarkerProps}/>) : null}
17
+ </>);
18
+ }
19
+ export default RouteOverlay;
20
+ //# sourceMappingURL=RouteOverlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RouteOverlay.js","sourceRoot":"","sources":["../../src/components/RouteOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,UAAU,YAAY,CAAC,EAC3B,MAAM,EACN,eAAe,GAAG,IAAI,EACtB,aAAa,GAAG,IAAI,EACpB,aAAa,EACb,gBAAgB,EAChB,cAAc,GACI;IAClB,8BAA8B;IAC9B,yBAAyB;IACzB,MAAM,gBAAgB,GAAG,KAAK,CAAC,OAAO,CACpC,GAAG,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC,EACjC,CAAC,MAAM,CAAC,CACT,CAAC;IAEF,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,KAAK,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE1D,OAAO,CACL,EACE;MAAA,CAAC,QAAQ,CACP,MAAM,CAAC,CAAC,gBAAgB,CAAC,CACzB,WAAW,CAAC,CAAC,aAAa,EAAE,WAAW,IAAI,CAAC,CAAC,CAC7C,WAAW,CAAC,CAAC,aAAa,EAAE,WAAW,IAAI,SAAS,CAAC,CACrD,IAAI,aAAa,CAAC,EAEpB;MAAA,CAAC,eAAe,CAAC,CAAC,CAAC,CACjB,CAAC,MAAM,CACL,QAAQ,CAAC,CAAC,KAAK,CAAC,CAChB,KAAK,CAAC,CAAC,gBAAgB,EAAE,KAAK,IAAI,IAAI,CAAC,CACvC,IAAI,gBAAgB,CAAC,EACrB,CACH,CAAC,CAAC,CAAC,IAAI,CACR;MAAA,CAAC,aAAa,CAAC,CAAC,CAAC,CACf,CAAC,MAAM,CACL,QAAQ,CAAC,CAAC,GAAG,CAAC,CACd,KAAK,CAAC,CAAC,cAAc,EAAE,KAAK,IAAI,IAAI,CAAC,CACrC,IAAI,cAAc,CAAC,EACnB,CACH,CAAC,CAAC,CAAC,IAAI,CACV;IAAA,GAAG,CACJ,CAAC;AACJ,CAAC;AAED,eAAe,YAAY,CAAC","sourcesContent":["import * as React from 'react';\n\nimport type { RouteOverlayProps } from '../types/route-playback.types';\nimport { normalizeLatLngList } from '../utils/GeoUtils';\nimport { Marker, Polyline } from './overlays';\n\nexport function RouteOverlay({\n points,\n showStartMarker = true,\n showEndMarker = true,\n polylineProps,\n startMarkerProps,\n endMarkerProps,\n}: RouteOverlayProps) {\n // 统一封装“主路径 + 起点 + 终点”的常见展示组合,\n // 这样业务侧不需要每次手动拼装 3 个覆盖物。\n const normalizedPoints = React.useMemo(\n () => normalizeLatLngList(points),\n [points]\n );\n\n if (normalizedPoints.length === 0) {\n return null;\n }\n\n const start = normalizedPoints[0];\n const end = normalizedPoints[normalizedPoints.length - 1];\n\n return (\n <>\n <Polyline\n points={normalizedPoints}\n strokeWidth={polylineProps?.strokeWidth ?? 6}\n strokeColor={polylineProps?.strokeColor ?? '#2563eb'}\n {...polylineProps}\n />\n {showStartMarker ? (\n <Marker\n position={start}\n title={startMarkerProps?.title ?? '起点'}\n {...startMarkerProps}\n />\n ) : null}\n {showEndMarker ? (\n <Marker\n position={end}\n title={endMarkerProps?.title ?? '终点'}\n {...endMarkerProps}\n />\n ) : null}\n </>\n );\n}\n\nexport default RouteOverlay;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"Cluster.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/Cluster.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD;;;;;GAKG;AACH,iBAAS,OAAO,CAAC,KAAK,EAAE,YAAY,qBAGnC;;AAqCD,wBAAkD"}
1
+ {"version":3,"file":"Cluster.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/Cluster.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAKhD;;;;;GAKG;AACH,iBAAS,OAAO,CAAC,KAAK,EAAE,YAAY,qBAGnC;;AAoDD,wBAAkD"}
@@ -24,10 +24,22 @@ function arePropsEqual(prevProps, nextProps) {
24
24
  if (prevProps.radius !== nextProps.radius) {
25
25
  return false;
26
26
  }
27
+ // 比较基础图标
28
+ if (prevProps.icon !== nextProps.icon) {
29
+ return false;
30
+ }
27
31
  // 比较 minClusterSize
28
32
  if (prevProps.minClusterSize !== nextProps.minClusterSize) {
29
33
  return false;
30
34
  }
35
+ // 比较聚合样式
36
+ if (prevProps.clusterStyle !== nextProps.clusterStyle) {
37
+ return false;
38
+ }
39
+ // 比较聚合文本样式
40
+ if (prevProps.clusterTextStyle !== nextProps.clusterTextStyle) {
41
+ return false;
42
+ }
31
43
  // 比较 clusterBuckets
32
44
  if (prevProps.clusterBuckets !== nextProps.clusterBuckets) {
33
45
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"Cluster.js","sourceRoot":"","sources":["../../../src/components/overlays/Cluster.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,2BAA2B,EAAE,MAAM,mCAAmC,CAAC;AAEhF,MAAM,gBAAgB,GAAG,2BAA2B,CAAe,aAAa,CAAC,CAAC;AAElF;;;;;GAKG;AACH,SAAS,OAAO,CAAC,KAAmB;IAClC,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,SAAuB,EAAE,SAAuB;IACrE,uBAAuB;IACvB,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,YAAY;IACZ,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oBAAoB;IACpB,IAAI,SAAS,CAAC,cAAc,KAAK,SAAS,CAAC,cAAc,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oBAAoB;IACpB,IAAI,SAAS,CAAC,cAAc,KAAK,SAAS,CAAC,cAAc,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,uBAAuB;IACvB,IAAI,SAAS,CAAC,cAAc,KAAK,SAAS,CAAC,cAAc,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,eAAe;IACf,OAAO,IAAI,CAAC;AACd,CAAC;AAED,WAAW;AACX,eAAe,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC","sourcesContent":["import * as React from 'react';\nimport type { ClusterProps } from '../../types';\nimport { createLazyNativeViewManager } from '../../utils/lazyNativeViewManager';\n\nconst getNativeCluster = createLazyNativeViewManager<ClusterProps>('ClusterView');\n\n/**\n * 高德地图点聚合组件\n *\n * @param props 点聚合组件的属性配置\n * @returns 渲染原生点聚合组件\n */\nfunction Cluster(props: ClusterProps) {\n const NativeCluster = React.useMemo(() => getNativeCluster(), []);\n return <NativeCluster {...props} />;\n}\n\n/**\n * 🔑 性能优化:浅比较关键属性\n * 只检查最常变化的属性,避免深度比较开销\n */\nfunction arePropsEqual(prevProps: ClusterProps, nextProps: ClusterProps): boolean {\n // 比较 points 数组引用(最常变化)\n if (prevProps.points !== nextProps.points) {\n return false;\n }\n \n // 比较 radius\n if (prevProps.radius !== nextProps.radius) {\n return false;\n }\n \n // 比较 minClusterSize\n if (prevProps.minClusterSize !== nextProps.minClusterSize) {\n return false;\n }\n \n // 比较 clusterBuckets\n if (prevProps.clusterBuckets !== nextProps.clusterBuckets) {\n return false;\n }\n \n // 比较 onClusterPress 回调\n if (prevProps.onClusterPress !== nextProps.onClusterPress) {\n return false;\n }\n \n // 其他属性相同,不重新渲染\n return true;\n}\n\n// 导出优化后的组件\nexport default React.memo(Cluster, arePropsEqual);\n"]}
1
+ {"version":3,"file":"Cluster.js","sourceRoot":"","sources":["../../../src/components/overlays/Cluster.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,EAAE,2BAA2B,EAAE,MAAM,mCAAmC,CAAC;AAEhF,MAAM,gBAAgB,GAAG,2BAA2B,CAAe,aAAa,CAAC,CAAC;AAElF;;;;;GAKG;AACH,SAAS,OAAO,CAAC,KAAmB;IAClC,MAAM,aAAa,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,gBAAgB,EAAE,EAAE,EAAE,CAAC,CAAC;IAClE,OAAO,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,EAAG,CAAC;AACtC,CAAC;AAED;;;GAGG;AACH,SAAS,aAAa,CAAC,SAAuB,EAAE,SAAuB;IACrE,uBAAuB;IACvB,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,YAAY;IACZ,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;QAC1C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS;IACT,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,IAAI,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oBAAoB;IACpB,IAAI,SAAS,CAAC,cAAc,KAAK,SAAS,CAAC,cAAc,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,SAAS;IACT,IAAI,SAAS,CAAC,YAAY,KAAK,SAAS,CAAC,YAAY,EAAE,CAAC;QACtD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,WAAW;IACX,IAAI,SAAS,CAAC,gBAAgB,KAAK,SAAS,CAAC,gBAAgB,EAAE,CAAC;QAC9D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,oBAAoB;IACpB,IAAI,SAAS,CAAC,cAAc,KAAK,SAAS,CAAC,cAAc,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,uBAAuB;IACvB,IAAI,SAAS,CAAC,cAAc,KAAK,SAAS,CAAC,cAAc,EAAE,CAAC;QAC1D,OAAO,KAAK,CAAC;IACf,CAAC;IAED,eAAe;IACf,OAAO,IAAI,CAAC;AACd,CAAC;AAED,WAAW;AACX,eAAe,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC","sourcesContent":["import * as React from 'react';\nimport type { ClusterProps } from '../../types';\nimport { createLazyNativeViewManager } from '../../utils/lazyNativeViewManager';\n\nconst getNativeCluster = createLazyNativeViewManager<ClusterProps>('ClusterView');\n\n/**\n * 高德地图点聚合组件\n *\n * @param props 点聚合组件的属性配置\n * @returns 渲染原生点聚合组件\n */\nfunction Cluster(props: ClusterProps) {\n const NativeCluster = React.useMemo(() => getNativeCluster(), []);\n return <NativeCluster {...props} />;\n}\n\n/**\n * 🔑 性能优化:浅比较关键属性\n * 只检查最常变化的属性,避免深度比较开销\n */\nfunction arePropsEqual(prevProps: ClusterProps, nextProps: ClusterProps): boolean {\n // 比较 points 数组引用(最常变化)\n if (prevProps.points !== nextProps.points) {\n return false;\n }\n \n // 比较 radius\n if (prevProps.radius !== nextProps.radius) {\n return false;\n }\n\n // 比较基础图标\n if (prevProps.icon !== nextProps.icon) {\n return false;\n }\n \n // 比较 minClusterSize\n if (prevProps.minClusterSize !== nextProps.minClusterSize) {\n return false;\n }\n\n // 比较聚合样式\n if (prevProps.clusterStyle !== nextProps.clusterStyle) {\n return false;\n }\n\n // 比较聚合文本样式\n if (prevProps.clusterTextStyle !== nextProps.clusterTextStyle) {\n return false;\n }\n \n // 比较 clusterBuckets\n if (prevProps.clusterBuckets !== nextProps.clusterBuckets) {\n return false;\n }\n \n // 比较 onClusterPress 回调\n if (prevProps.onClusterPress !== nextProps.onClusterPress) {\n return false;\n }\n \n // 其他属性相同,不重新渲染\n return true;\n}\n\n// 导出优化后的组件\nexport default React.memo(Cluster, arePropsEqual);\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"Marker.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/Marker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAa/C;;;;;;;;;GASG;AACH,iBAAS,MAAM,CAAC,KAAK,EAAE,WAAW,qBAsEjC;;AAsDD,wBAAiD"}
1
+ {"version":3,"file":"Marker.d.ts","sourceRoot":"","sources":["../../../src/components/overlays/Marker.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA6C/C;;;;;;;;;GASG;AACH,iBAAS,MAAM,CAAC,KAAK,EAAE,WAAW,qBAyJjC;;AAsDD,wBAAiD"}
@@ -1,9 +1,30 @@
1
1
  import * as React from 'react';
2
2
  import { Platform, StyleSheet, View } from 'react-native';
3
+ import ExpoGaodeMapModule from '../../ExpoGaodeMapModule';
3
4
  import { normalizeLatLng, normalizeLatLngList } from '../../utils/GeoUtils';
4
5
  import { createLazyNativeViewManager } from '../../utils/lazyNativeViewManager';
5
6
  const getNativeMarkerView = createLazyNativeViewManager('MarkerView');
6
7
  const AUTO_SIZE_FALLBACK = { width: 0, height: 0 };
8
+ function areSmoothMovePathsEqual(prevPath, nextPath) {
9
+ if (prevPath === nextPath) {
10
+ return true;
11
+ }
12
+ if (!prevPath || !nextPath) {
13
+ return prevPath === nextPath;
14
+ }
15
+ if (prevPath.length !== nextPath.length) {
16
+ return false;
17
+ }
18
+ for (let index = 0; index < prevPath.length; index += 1) {
19
+ const prevPoint = normalizeLatLng(prevPath[index]);
20
+ const nextPoint = normalizeLatLng(nextPath[index]);
21
+ if (prevPoint.latitude !== nextPoint.latitude ||
22
+ prevPoint.longitude !== nextPoint.longitude) {
23
+ return false;
24
+ }
25
+ }
26
+ return true;
27
+ }
7
28
  /**
8
29
  * Marker 组件 - 完全声明式 API
9
30
  *
@@ -18,7 +39,7 @@ function Marker(props) {
18
39
  const NativeMarkerView = React.useMemo(() => getNativeMarkerView(), []);
19
40
  const [measuredSize, setMeasuredSize] = React.useState(AUTO_SIZE_FALLBACK);
20
41
  // 从 props 中排除 position 属性,避免传递到原生层
21
- const { position, customViewWidth, customViewHeight, iconWidth, iconHeight, children, smoothMovePath, ...restProps } = props;
42
+ const { position, customViewWidth, customViewHeight, iconWidth, iconHeight, children, smoothMovePath, cacheKey, ...restProps } = props;
22
43
  // 归一化坐标处理
23
44
  const normalizedPosition = normalizeLatLng(position);
24
45
  const normalizedSmoothMovePath = smoothMovePath ? normalizeLatLngList(smoothMovePath) : undefined;
@@ -32,6 +53,67 @@ function Marker(props) {
32
53
  const resolvedCustomViewHeight = customViewHeight && customViewHeight > 0
33
54
  ? customViewHeight
34
55
  : (shouldUseAutoMeasuredSize ? measuredSize.height : 0);
56
+ React.useEffect(() => {
57
+ if (!normalizedSmoothMovePath ||
58
+ normalizedSmoothMovePath.length < 2 ||
59
+ !props.smoothMoveDuration ||
60
+ props.smoothMoveDuration <= 0) {
61
+ return undefined;
62
+ }
63
+ const totalDistance = ExpoGaodeMapModule.calculatePathLength(normalizedSmoothMovePath);
64
+ if (totalDistance <= 0) {
65
+ props.onSmoothMoveEnd?.({
66
+ nativeEvent: {
67
+ position: normalizedSmoothMovePath[normalizedSmoothMovePath.length - 1],
68
+ angle: 0,
69
+ totalDistance,
70
+ },
71
+ });
72
+ return undefined;
73
+ }
74
+ const durationMs = props.smoothMoveDuration * 1000;
75
+ const startedAt = Date.now();
76
+ const tick = () => {
77
+ const progress = Math.min(1, (Date.now() - startedAt) / durationMs);
78
+ const distance = totalDistance * progress;
79
+ const pointInfo = ExpoGaodeMapModule.getPointAtDistance(normalizedSmoothMovePath, distance);
80
+ const point = pointInfo
81
+ ? { latitude: pointInfo.latitude, longitude: pointInfo.longitude }
82
+ : normalizedSmoothMovePath[normalizedSmoothMovePath.length - 1];
83
+ const angle = pointInfo?.angle ?? 0;
84
+ props.onSmoothMoveProgress?.({
85
+ nativeEvent: {
86
+ position: point,
87
+ angle,
88
+ progress,
89
+ distance,
90
+ totalDistance,
91
+ },
92
+ });
93
+ if (progress >= 1) {
94
+ props.onSmoothMoveEnd?.({
95
+ nativeEvent: {
96
+ position: point,
97
+ angle,
98
+ totalDistance,
99
+ },
100
+ });
101
+ }
102
+ };
103
+ tick();
104
+ const intervalId = setInterval(() => {
105
+ tick();
106
+ if (Date.now() - startedAt >= durationMs) {
107
+ clearInterval(intervalId);
108
+ }
109
+ }, 100);
110
+ return () => clearInterval(intervalId);
111
+ }, [
112
+ normalizedSmoothMovePath,
113
+ props.onSmoothMoveEnd,
114
+ props.onSmoothMoveProgress,
115
+ props.smoothMoveDuration,
116
+ ]);
35
117
  const handleAutoMeasure = (event) => {
36
118
  const nextWidth = customViewWidth && customViewWidth > 0
37
119
  ? customViewWidth
@@ -54,7 +136,8 @@ function Marker(props) {
54
136
  const finalIconHeight = hasChildren
55
137
  ? resolvedCustomViewHeight
56
138
  : (iconHeight && iconHeight > 0 ? iconHeight : 40);
57
- return (<NativeMarkerView latitude={normalizedPosition.latitude} longitude={normalizedPosition.longitude} iconWidth={finalIconWidth} iconHeight={finalIconHeight} customViewWidth={finalIconWidth} customViewHeight={finalIconHeight} smoothMovePath={normalizedSmoothMovePath} {...restProps}>
139
+ const optionalNativeProps = cacheKey != null ? { cacheKey } : undefined;
140
+ return (<NativeMarkerView latitude={normalizedPosition.latitude} longitude={normalizedPosition.longitude} iconWidth={finalIconWidth} iconHeight={finalIconHeight} customViewWidth={finalIconWidth} customViewHeight={finalIconHeight} smoothMovePath={normalizedSmoothMovePath} {...optionalNativeProps} {...restProps}>
58
141
  {hasChildren && shouldWrapChildrenForMeasurement ? (<View collapsable={false} onLayout={Platform.OS === 'ios' ? handleAutoMeasure : undefined} style={styles.measureContainer}>
59
142
  {children}
60
143
  </View>) : children}
@@ -89,7 +172,7 @@ function arePropsEqual(prevProps, nextProps) {
89
172
  return false;
90
173
  }
91
174
  // 比较 smoothMovePath (平滑移动路径)
92
- if (JSON.stringify(prevProps.smoothMovePath) !== JSON.stringify(nextProps.smoothMovePath)) {
175
+ if (!areSmoothMovePathsEqual(prevProps.smoothMovePath, nextProps.smoothMovePath)) {
93
176
  return false;
94
177
  }
95
178
  // 比较 smoothMoveDuration (平滑移动时长)