expo-gaode-map 2.2.32-next.0 → 2.2.32
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 +1 -1
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +2 -2
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +1 -1
- package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +5 -5
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerBitmapRenderer.kt +3 -1
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +3 -3
- package/build/ExpoGaodeMapModule.d.ts.map +1 -1
- package/build/ExpoGaodeMapModule.js +9 -26
- package/build/ExpoGaodeMapModule.js.map +1 -1
- package/build/components/overlays/Marker.d.ts.map +1 -1
- package/build/components/overlays/Marker.js +3 -2
- package/build/components/overlays/Marker.js.map +1 -1
- package/build/hooks/useRoutePlayback.d.ts.map +1 -1
- package/build/hooks/useRoutePlayback.js +83 -43
- package/build/hooks/useRoutePlayback.js.map +1 -1
- package/build/types/common.types.d.ts +5 -5
- package/build/types/common.types.js +5 -5
- package/build/types/common.types.js.map +1 -1
- package/ios/ExpoGaodeMapModule.swift +9 -4
- package/ios/ExpoGaodeMapView.swift +23 -9
- package/ios/ExpoGaodeMapViewModule.swift +2 -10
- package/ios/managers/UIManager.swift +5 -4
- package/ios/modules/LocationManager.swift +17 -0
- package/ios/overlays/MarkerView.swift +3 -1
- package/ios/overlays/MarkerViewModule.swift +1 -1
- package/ios/utils/PermissionManager.swift +11 -6
- package/package.json +13 -2
|
@@ -49,11 +49,14 @@ export function useRoutePlayback(points, options = {}) {
|
|
|
49
49
|
const map = React.useContext(MapContext);
|
|
50
50
|
const optionsRef = React.useRef(options);
|
|
51
51
|
const [state, setState] = React.useState(DEFAULT_STATE);
|
|
52
|
+
const stateRef = React.useRef(DEFAULT_STATE);
|
|
52
53
|
const [speedMultiplier, setSpeedMultiplierState] = React.useState(options.speedMultiplier ?? 1);
|
|
53
54
|
const timerRef = React.useRef(null);
|
|
54
55
|
const startAtRef = React.useRef(0);
|
|
55
56
|
const elapsedBeforePauseRef = React.useRef(0);
|
|
56
57
|
const lastAngleRef = React.useRef(0);
|
|
58
|
+
const previousPathRef = React.useRef(null);
|
|
59
|
+
const previousDurationRef = React.useRef(null);
|
|
57
60
|
React.useEffect(() => {
|
|
58
61
|
optionsRef.current = options;
|
|
59
62
|
}, [options]);
|
|
@@ -91,6 +94,7 @@ export function useRoutePlayback(points, options = {}) {
|
|
|
91
94
|
}, []);
|
|
92
95
|
const publishState = React.useCallback((nextState) => {
|
|
93
96
|
// 统一从这里下发状态,避免不同控制分支各自维护回调时机。
|
|
97
|
+
stateRef.current = nextState;
|
|
94
98
|
setState(nextState);
|
|
95
99
|
optionsRef.current.onProgress?.(nextState);
|
|
96
100
|
}, []);
|
|
@@ -124,7 +128,7 @@ export function useRoutePlayback(points, options = {}) {
|
|
|
124
128
|
smoothMoveDuration: keepPlaying ? durationSeconds : undefined,
|
|
125
129
|
};
|
|
126
130
|
publishState(nextState);
|
|
127
|
-
if (optionsRef.current.followCamera
|
|
131
|
+
if (optionsRef.current.followCamera === true && nextState.currentPosition && map) {
|
|
128
132
|
await map.moveCamera({
|
|
129
133
|
target: nextState.currentPosition,
|
|
130
134
|
zoom: optionsRef.current.followZoom ?? 17,
|
|
@@ -139,6 +143,26 @@ export function useRoutePlayback(points, options = {}) {
|
|
|
139
143
|
publishState,
|
|
140
144
|
totalDistance,
|
|
141
145
|
]);
|
|
146
|
+
const runPlaybackTick = React.useCallback(async () => {
|
|
147
|
+
const elapsedMs = Date.now() - startAtRef.current + elapsedBeforePauseRef.current;
|
|
148
|
+
const progress = durationSeconds <= 0 ? 1 : elapsedMs / (durationSeconds * 1000);
|
|
149
|
+
if (progress >= 1) {
|
|
150
|
+
stopTimer();
|
|
151
|
+
elapsedBeforePauseRef.current = 0;
|
|
152
|
+
startAtRef.current = 0;
|
|
153
|
+
const completedState = await syncProgress(1, false);
|
|
154
|
+
optionsRef.current.onComplete?.(completedState);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
await syncProgress(progress, true);
|
|
158
|
+
}, [durationSeconds, stopTimer, syncProgress]);
|
|
159
|
+
const startTimer = React.useCallback(() => {
|
|
160
|
+
stopTimer();
|
|
161
|
+
const interval = Math.max(optionsRef.current.updateIntervalMs ?? 100, 16);
|
|
162
|
+
timerRef.current = setInterval(() => {
|
|
163
|
+
void runPlaybackTick();
|
|
164
|
+
}, interval);
|
|
165
|
+
}, [runPlaybackTick, stopTimer]);
|
|
142
166
|
const stop = React.useCallback(() => {
|
|
143
167
|
stopTimer();
|
|
144
168
|
elapsedBeforePauseRef.current = 0;
|
|
@@ -156,30 +180,15 @@ export function useRoutePlayback(points, options = {}) {
|
|
|
156
180
|
elapsedBeforePauseRef.current = 0;
|
|
157
181
|
startAtRef.current = Date.now();
|
|
158
182
|
lastAngleRef.current = 0;
|
|
159
|
-
if (optionsRef.current.autoFit
|
|
183
|
+
if (optionsRef.current.autoFit === true && map && normalizedPath.length > 0) {
|
|
160
184
|
await fitCameraToCoordinates(map, normalizedPath, optionsRef.current.fitOptions);
|
|
161
185
|
}
|
|
162
186
|
await syncProgress(0, true);
|
|
163
|
-
|
|
164
|
-
const interval = Math.max(optionsRef.current.updateIntervalMs ?? 100, 16);
|
|
165
|
-
timerRef.current = setInterval(async () => {
|
|
166
|
-
const elapsedMs = Date.now() - startAtRef.current + elapsedBeforePauseRef.current;
|
|
167
|
-
const progress = durationSeconds <= 0 ? 1 : elapsedMs / (durationSeconds * 1000);
|
|
168
|
-
if (progress >= 1) {
|
|
169
|
-
stopTimer();
|
|
170
|
-
elapsedBeforePauseRef.current = 0;
|
|
171
|
-
startAtRef.current = 0;
|
|
172
|
-
const completedState = await syncProgress(1, false);
|
|
173
|
-
optionsRef.current.onComplete?.(completedState);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
await syncProgress(progress, true);
|
|
177
|
-
}, interval);
|
|
187
|
+
startTimer();
|
|
178
188
|
}, [
|
|
179
|
-
durationSeconds,
|
|
180
189
|
map,
|
|
181
190
|
normalizedPath,
|
|
182
|
-
|
|
191
|
+
startTimer,
|
|
183
192
|
syncProgress,
|
|
184
193
|
]);
|
|
185
194
|
const pause = React.useCallback(() => {
|
|
@@ -188,49 +197,37 @@ export function useRoutePlayback(points, options = {}) {
|
|
|
188
197
|
}
|
|
189
198
|
stopTimer();
|
|
190
199
|
elapsedBeforePauseRef.current += Date.now() - startAtRef.current;
|
|
200
|
+
startAtRef.current = 0;
|
|
201
|
+
const currentState = stateRef.current;
|
|
191
202
|
publishState({
|
|
192
|
-
...
|
|
203
|
+
...currentState,
|
|
193
204
|
isPlaying: false,
|
|
194
205
|
isPaused: true,
|
|
195
206
|
smoothMovePath: undefined,
|
|
196
207
|
smoothMoveDuration: undefined,
|
|
197
208
|
});
|
|
198
|
-
}, [durationSeconds, publishState,
|
|
209
|
+
}, [durationSeconds, publishState, stopTimer]);
|
|
199
210
|
const resume = React.useCallback(async () => {
|
|
200
|
-
|
|
211
|
+
const currentState = stateRef.current;
|
|
212
|
+
if (currentState.progress >= 1) {
|
|
201
213
|
await start();
|
|
202
214
|
return;
|
|
203
215
|
}
|
|
204
216
|
startAtRef.current = Date.now();
|
|
205
217
|
publishState({
|
|
206
|
-
...
|
|
218
|
+
...currentState,
|
|
207
219
|
isPlaying: true,
|
|
208
220
|
isPaused: false,
|
|
209
221
|
smoothMovePath: normalizedPath,
|
|
210
222
|
smoothMoveDuration: durationSeconds,
|
|
211
223
|
});
|
|
212
|
-
|
|
213
|
-
timerRef.current = setInterval(async () => {
|
|
214
|
-
const elapsedMs = Date.now() - startAtRef.current + elapsedBeforePauseRef.current;
|
|
215
|
-
const progress = durationSeconds <= 0 ? 1 : elapsedMs / (durationSeconds * 1000);
|
|
216
|
-
if (progress >= 1) {
|
|
217
|
-
stopTimer();
|
|
218
|
-
elapsedBeforePauseRef.current = 0;
|
|
219
|
-
startAtRef.current = 0;
|
|
220
|
-
const completedState = await syncProgress(1, false);
|
|
221
|
-
optionsRef.current.onComplete?.(completedState);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
await syncProgress(progress, true);
|
|
225
|
-
}, interval);
|
|
224
|
+
startTimer();
|
|
226
225
|
}, [
|
|
227
226
|
durationSeconds,
|
|
228
227
|
normalizedPath,
|
|
229
228
|
publishState,
|
|
230
229
|
start,
|
|
231
|
-
|
|
232
|
-
stopTimer,
|
|
233
|
-
syncProgress,
|
|
230
|
+
startTimer,
|
|
234
231
|
]);
|
|
235
232
|
const seek = React.useCallback((progress) => {
|
|
236
233
|
// seek 只重算“已经播放过的时间”,其余状态由 syncProgress 统一刷新。
|
|
@@ -244,13 +241,56 @@ export function useRoutePlayback(points, options = {}) {
|
|
|
244
241
|
}, []);
|
|
245
242
|
React.useEffect(() => () => stopTimer(), [stopTimer]);
|
|
246
243
|
React.useEffect(() => {
|
|
244
|
+
const pathChanged = previousPathRef.current !== normalizedPath;
|
|
245
|
+
const durationChanged = previousDurationRef.current !== null && previousDurationRef.current !== durationSeconds;
|
|
246
|
+
previousPathRef.current = normalizedPath;
|
|
247
|
+
previousDurationRef.current = durationSeconds;
|
|
248
|
+
if (pathChanged) {
|
|
249
|
+
stopTimer();
|
|
250
|
+
elapsedBeforePauseRef.current = 0;
|
|
251
|
+
startAtRef.current = 0;
|
|
252
|
+
lastAngleRef.current = 0;
|
|
253
|
+
publishState({
|
|
254
|
+
...DEFAULT_STATE,
|
|
255
|
+
currentPosition: normalizedPath[0] ?? null,
|
|
256
|
+
totalDistance,
|
|
257
|
+
durationSeconds,
|
|
258
|
+
});
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
if (!durationChanged) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const currentState = stateRef.current;
|
|
265
|
+
if (currentState.isPlaying) {
|
|
266
|
+
elapsedBeforePauseRef.current = currentState.progress * durationSeconds * 1000;
|
|
267
|
+
startAtRef.current = Date.now();
|
|
268
|
+
publishState({
|
|
269
|
+
...currentState,
|
|
270
|
+
totalDistance,
|
|
271
|
+
durationSeconds,
|
|
272
|
+
smoothMoveDuration: durationSeconds,
|
|
273
|
+
});
|
|
274
|
+
startTimer();
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (currentState.isPaused) {
|
|
278
|
+
elapsedBeforePauseRef.current = currentState.progress * durationSeconds * 1000;
|
|
279
|
+
startAtRef.current = 0;
|
|
280
|
+
publishState({
|
|
281
|
+
...currentState,
|
|
282
|
+
totalDistance,
|
|
283
|
+
durationSeconds,
|
|
284
|
+
});
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
247
287
|
publishState({
|
|
248
|
-
...
|
|
249
|
-
currentPosition: normalizedPath[0] ?? null,
|
|
288
|
+
...currentState,
|
|
289
|
+
currentPosition: currentState.progress > 0 ? currentState.currentPosition : normalizedPath[0] ?? null,
|
|
250
290
|
totalDistance,
|
|
251
291
|
durationSeconds,
|
|
252
292
|
});
|
|
253
|
-
}, [durationSeconds, normalizedPath, publishState, totalDistance]);
|
|
293
|
+
}, [durationSeconds, normalizedPath, publishState, startTimer, stopTimer, totalDistance]);
|
|
254
294
|
return {
|
|
255
295
|
...state,
|
|
256
296
|
totalDistance,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRoutePlayback.js","sourceRoot":"","sources":["../../src/hooks/useRoutePlayback.ts"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\n\nimport ExpoGaodeMapModule from '../ExpoGaodeMapModule';\nimport { MapContext } from '../components/MapContext';\nimport type { LatLng } from '../types/common.types';\nimport type {\n RoutePlaybackController,\n RoutePlaybackOptions,\n RoutePlaybackState,\n} from '../types/route-playback.types';\nimport { normalizeLatLngList } from '../utils/GeoUtils';\nimport { fitCameraToCoordinates } from '../utils/RouteUtils';\n\nconst DEFAULT_STATE: RoutePlaybackState = {\n isPlaying: false,\n isPaused: false,\n progress: 0,\n currentPosition: null,\n currentAngle: 0,\n totalDistance: 0,\n traveledDistance: 0,\n durationSeconds: 0,\n};\n\nfunction getMarkerAngle(\n path: LatLng[],\n totalDistance: number,\n targetDistance: number,\n lastAngle: number,\n options: RoutePlaybackOptions\n): { angle: number; point: LatLng | null } {\n // 先取当前距离上的点,再额外预读一个前瞻点,\n // 让车辆朝向在转弯时更自然,不会出现生硬折角。\n const pointInfo = ExpoGaodeMapModule.getPointAtDistance(path, targetDistance);\n if (!pointInfo) {\n return { angle: lastAngle, point: null };\n }\n\n const lookAheadDistance = options.lookAheadDistanceMeters ?? 5;\n const futurePoint = ExpoGaodeMapModule.getPointAtDistance(\n path,\n Math.min(totalDistance, targetDistance + lookAheadDistance)\n );\n\n let targetAngle = pointInfo.angle;\n if (futurePoint && targetDistance + lookAheadDistance < totalDistance) {\n let diffNext = futurePoint.angle - pointInfo.angle;\n if (diffNext > 180) diffNext -= 360;\n if (diffNext < -180) diffNext += 360;\n targetAngle = pointInfo.angle + diffNext * 0.4;\n }\n\n let diff = targetAngle - lastAngle;\n if (diff > 180) diff -= 360;\n if (diff < -180) diff += 360;\n\n const bearingSmoothing = options.bearingSmoothing ?? 0.2;\n return {\n angle: lastAngle + diff * bearingSmoothing,\n point: {\n latitude: pointInfo.latitude,\n longitude: pointInfo.longitude,\n },\n };\n}\n\nexport function useRoutePlayback(\n points: Array<LatLng | [number, number] | number[]>,\n options: RoutePlaybackOptions = {}\n): RoutePlaybackController {\n const map = React.useContext(MapContext);\n const optionsRef = React.useRef(options);\n const [state, setState] = React.useState<RoutePlaybackState>(DEFAULT_STATE);\n const [speedMultiplier, setSpeedMultiplierState] = React.useState(options.speedMultiplier ?? 1);\n const timerRef = React.useRef<ReturnType<typeof setInterval> | null>(null);\n const startAtRef = React.useRef(0);\n const elapsedBeforePauseRef = React.useRef(0);\n const lastAngleRef = React.useRef(0);\n\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const normalizedPath = React.useMemo(() => {\n // 长路径可选走一次抽稀,减少轨迹回放期间的计算量。\n const normalized = normalizeLatLngList(points) as LatLng[];\n if (normalized.length <= 2 || !options.simplificationTolerance) {\n return normalized;\n }\n return ExpoGaodeMapModule.simplifyPolyline(normalized, options.simplificationTolerance);\n }, [options.simplificationTolerance, points]);\n\n const totalDistance = React.useMemo(\n () => ExpoGaodeMapModule.calculatePathLength(normalizedPath),\n [normalizedPath]\n );\n\n const durationSeconds = React.useMemo(() => {\n if (options.durationSeconds) {\n return options.durationSeconds;\n }\n\n const baseSpeed = options.baseSpeedMps ?? 15;\n const minDuration = options.minDurationSeconds ?? 5;\n if (totalDistance <= 0) {\n return 0;\n }\n\n return Math.max(minDuration, totalDistance / (baseSpeed * Math.max(speedMultiplier, 0.1)));\n }, [\n options.baseSpeedMps,\n options.durationSeconds,\n options.minDurationSeconds,\n speedMultiplier,\n totalDistance,\n ]);\n\n const stopTimer = React.useCallback(() => {\n if (timerRef.current) {\n clearInterval(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const publishState = React.useCallback(\n (nextState: RoutePlaybackState) => {\n // 统一从这里下发状态,避免不同控制分支各自维护回调时机。\n setState(nextState);\n optionsRef.current.onProgress?.(nextState);\n },\n []\n );\n\n const syncProgress = React.useCallback(\n async (progress: number, keepPlaying: boolean) => {\n // 根据“当前进度 -> 已行驶距离 -> 路径上的点”推导出车辆位置,\n // 再同步决定 smoothMovePath / 相机跟随 / 对外状态。\n if (normalizedPath.length === 0 || totalDistance <= 0 || durationSeconds <= 0) {\n const nextState = {\n ...DEFAULT_STATE,\n currentPosition: normalizedPath[0] ?? null,\n totalDistance,\n durationSeconds,\n };\n publishState(nextState);\n return nextState;\n }\n\n const clampedProgress = Math.max(0, Math.min(1, progress));\n const traveledDistance = totalDistance * clampedProgress;\n const { angle, point } = getMarkerAngle(\n normalizedPath,\n totalDistance,\n traveledDistance,\n lastAngleRef.current,\n optionsRef.current\n );\n lastAngleRef.current = angle;\n\n const nextState: RoutePlaybackState = {\n isPlaying: keepPlaying,\n isPaused: !keepPlaying && clampedProgress > 0 && clampedProgress < 1,\n progress: clampedProgress,\n currentPosition: point ?? normalizedPath[0] ?? null,\n currentAngle: angle,\n totalDistance,\n traveledDistance,\n durationSeconds,\n smoothMovePath: keepPlaying ? normalizedPath : undefined,\n smoothMoveDuration: keepPlaying ? durationSeconds : undefined,\n };\n\n publishState(nextState);\n\n if (optionsRef.current.followCamera !== false && nextState.currentPosition && map) {\n await map.moveCamera(\n {\n target: nextState.currentPosition,\n zoom: optionsRef.current.followZoom ?? 17,\n bearing: angle,\n },\n optionsRef.current.updateIntervalMs ?? 100\n );\n }\n\n return nextState;\n },\n [\n durationSeconds,\n map,\n normalizedPath,\n publishState,\n totalDistance,\n ]\n );\n\n const stop = React.useCallback(() => {\n stopTimer();\n elapsedBeforePauseRef.current = 0;\n startAtRef.current = 0;\n lastAngleRef.current = 0;\n publishState({\n ...DEFAULT_STATE,\n currentPosition: normalizedPath[0] ?? null,\n totalDistance,\n durationSeconds,\n });\n }, [durationSeconds, normalizedPath, publishState, stopTimer, totalDistance]);\n\n const start = React.useCallback(async () => {\n stopTimer();\n elapsedBeforePauseRef.current = 0;\n startAtRef.current = Date.now();\n lastAngleRef.current = 0;\n\n if (optionsRef.current.autoFit !== false && map && normalizedPath.length > 0) {\n await fitCameraToCoordinates(map, normalizedPath, optionsRef.current.fitOptions);\n }\n\n await syncProgress(0, true);\n\n // 使用固定间隔推进进度,保证回放速度与 UI 反馈稳定。\n const interval = Math.max(optionsRef.current.updateIntervalMs ?? 100, 16);\n timerRef.current = setInterval(async () => {\n const elapsedMs = Date.now() - startAtRef.current + elapsedBeforePauseRef.current;\n const progress = durationSeconds <= 0 ? 1 : elapsedMs / (durationSeconds * 1000);\n\n if (progress >= 1) {\n stopTimer();\n elapsedBeforePauseRef.current = 0;\n startAtRef.current = 0;\n const completedState = await syncProgress(1, false);\n optionsRef.current.onComplete?.(completedState);\n return;\n }\n\n await syncProgress(progress, true);\n }, interval);\n }, [\n durationSeconds,\n map,\n normalizedPath,\n stopTimer,\n syncProgress,\n ]);\n\n const pause = React.useCallback(() => {\n if (!timerRef.current || durationSeconds <= 0) {\n return;\n }\n\n stopTimer();\n elapsedBeforePauseRef.current += Date.now() - startAtRef.current;\n publishState({\n ...state,\n isPlaying: false,\n isPaused: true,\n smoothMovePath: undefined,\n smoothMoveDuration: undefined,\n });\n }, [durationSeconds, publishState, state, stopTimer]);\n\n const resume = React.useCallback(async () => {\n if (state.progress >= 1) {\n await start();\n return;\n }\n\n startAtRef.current = Date.now();\n publishState({\n ...state,\n isPlaying: true,\n isPaused: false,\n smoothMovePath: normalizedPath,\n smoothMoveDuration: durationSeconds,\n });\n\n const interval = Math.max(optionsRef.current.updateIntervalMs ?? 100, 16);\n timerRef.current = setInterval(async () => {\n const elapsedMs = Date.now() - startAtRef.current + elapsedBeforePauseRef.current;\n const progress = durationSeconds <= 0 ? 1 : elapsedMs / (durationSeconds * 1000);\n\n if (progress >= 1) {\n stopTimer();\n elapsedBeforePauseRef.current = 0;\n startAtRef.current = 0;\n const completedState = await syncProgress(1, false);\n optionsRef.current.onComplete?.(completedState);\n return;\n }\n\n await syncProgress(progress, true);\n }, interval);\n }, [\n durationSeconds,\n normalizedPath,\n publishState,\n start,\n state,\n stopTimer,\n syncProgress,\n ]);\n\n const seek = React.useCallback(\n (progress: number) => {\n // seek 只重算“已经播放过的时间”,其余状态由 syncProgress 统一刷新。\n const clamped = Math.max(0, Math.min(1, progress));\n elapsedBeforePauseRef.current = clamped * durationSeconds * 1000;\n startAtRef.current = Date.now();\n void syncProgress(clamped, !!timerRef.current);\n },\n [durationSeconds, syncProgress]\n );\n\n const setSpeedMultiplier = React.useCallback((multiplier: number) => {\n setSpeedMultiplierState(Math.max(multiplier, 0.1));\n }, []);\n\n React.useEffect(() => () => stopTimer(), [stopTimer]);\n\n React.useEffect(() => {\n publishState({\n ...DEFAULT_STATE,\n currentPosition: normalizedPath[0] ?? null,\n totalDistance,\n durationSeconds,\n });\n }, [durationSeconds, normalizedPath, publishState, totalDistance]);\n\n return {\n ...state,\n totalDistance,\n durationSeconds,\n start: () => {\n void start();\n },\n pause,\n resume: () => {\n void resume();\n },\n stop,\n seek,\n setSpeedMultiplier,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useRoutePlayback.js","sourceRoot":"","sources":["../../src/hooks/useRoutePlayback.ts"],"names":[],"mappings":"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","sourcesContent":["import * as React from 'react';\n\nimport ExpoGaodeMapModule from '../ExpoGaodeMapModule';\nimport { MapContext } from '../components/MapContext';\nimport type { LatLng } from '../types/common.types';\nimport type {\n RoutePlaybackController,\n RoutePlaybackOptions,\n RoutePlaybackState,\n} from '../types/route-playback.types';\nimport { normalizeLatLngList } from '../utils/GeoUtils';\nimport { fitCameraToCoordinates } from '../utils/RouteUtils';\n\nconst DEFAULT_STATE: RoutePlaybackState = {\n isPlaying: false,\n isPaused: false,\n progress: 0,\n currentPosition: null,\n currentAngle: 0,\n totalDistance: 0,\n traveledDistance: 0,\n durationSeconds: 0,\n};\n\nfunction getMarkerAngle(\n path: LatLng[],\n totalDistance: number,\n targetDistance: number,\n lastAngle: number,\n options: RoutePlaybackOptions\n): { angle: number; point: LatLng | null } {\n // 先取当前距离上的点,再额外预读一个前瞻点,\n // 让车辆朝向在转弯时更自然,不会出现生硬折角。\n const pointInfo = ExpoGaodeMapModule.getPointAtDistance(path, targetDistance);\n if (!pointInfo) {\n return { angle: lastAngle, point: null };\n }\n\n const lookAheadDistance = options.lookAheadDistanceMeters ?? 5;\n const futurePoint = ExpoGaodeMapModule.getPointAtDistance(\n path,\n Math.min(totalDistance, targetDistance + lookAheadDistance)\n );\n\n let targetAngle = pointInfo.angle;\n if (futurePoint && targetDistance + lookAheadDistance < totalDistance) {\n let diffNext = futurePoint.angle - pointInfo.angle;\n if (diffNext > 180) diffNext -= 360;\n if (diffNext < -180) diffNext += 360;\n targetAngle = pointInfo.angle + diffNext * 0.4;\n }\n\n let diff = targetAngle - lastAngle;\n if (diff > 180) diff -= 360;\n if (diff < -180) diff += 360;\n\n const bearingSmoothing = options.bearingSmoothing ?? 0.2;\n return {\n angle: lastAngle + diff * bearingSmoothing,\n point: {\n latitude: pointInfo.latitude,\n longitude: pointInfo.longitude,\n },\n };\n}\n\nexport function useRoutePlayback(\n points: Array<LatLng | [number, number] | number[]>,\n options: RoutePlaybackOptions = {}\n): RoutePlaybackController {\n const map = React.useContext(MapContext);\n const optionsRef = React.useRef(options);\n const [state, setState] = React.useState<RoutePlaybackState>(DEFAULT_STATE);\n const stateRef = React.useRef<RoutePlaybackState>(DEFAULT_STATE);\n const [speedMultiplier, setSpeedMultiplierState] = React.useState(options.speedMultiplier ?? 1);\n const timerRef = React.useRef<ReturnType<typeof setInterval> | null>(null);\n const startAtRef = React.useRef(0);\n const elapsedBeforePauseRef = React.useRef(0);\n const lastAngleRef = React.useRef(0);\n const previousPathRef = React.useRef<LatLng[] | null>(null);\n const previousDurationRef = React.useRef<number | null>(null);\n\n React.useEffect(() => {\n optionsRef.current = options;\n }, [options]);\n\n const normalizedPath = React.useMemo(() => {\n // 长路径可选走一次抽稀,减少轨迹回放期间的计算量。\n const normalized = normalizeLatLngList(points) as LatLng[];\n if (normalized.length <= 2 || !options.simplificationTolerance) {\n return normalized;\n }\n return ExpoGaodeMapModule.simplifyPolyline(normalized, options.simplificationTolerance);\n }, [options.simplificationTolerance, points]);\n\n const totalDistance = React.useMemo(\n () => ExpoGaodeMapModule.calculatePathLength(normalizedPath),\n [normalizedPath]\n );\n\n const durationSeconds = React.useMemo(() => {\n if (options.durationSeconds) {\n return options.durationSeconds;\n }\n\n const baseSpeed = options.baseSpeedMps ?? 15;\n const minDuration = options.minDurationSeconds ?? 5;\n if (totalDistance <= 0) {\n return 0;\n }\n\n return Math.max(minDuration, totalDistance / (baseSpeed * Math.max(speedMultiplier, 0.1)));\n }, [\n options.baseSpeedMps,\n options.durationSeconds,\n options.minDurationSeconds,\n speedMultiplier,\n totalDistance,\n ]);\n\n const stopTimer = React.useCallback(() => {\n if (timerRef.current) {\n clearInterval(timerRef.current);\n timerRef.current = null;\n }\n }, []);\n\n const publishState = React.useCallback(\n (nextState: RoutePlaybackState) => {\n // 统一从这里下发状态,避免不同控制分支各自维护回调时机。\n stateRef.current = nextState;\n setState(nextState);\n optionsRef.current.onProgress?.(nextState);\n },\n []\n );\n\n const syncProgress = React.useCallback(\n async (progress: number, keepPlaying: boolean) => {\n // 根据“当前进度 -> 已行驶距离 -> 路径上的点”推导出车辆位置,\n // 再同步决定 smoothMovePath / 相机跟随 / 对外状态。\n if (normalizedPath.length === 0 || totalDistance <= 0 || durationSeconds <= 0) {\n const nextState = {\n ...DEFAULT_STATE,\n currentPosition: normalizedPath[0] ?? null,\n totalDistance,\n durationSeconds,\n };\n publishState(nextState);\n return nextState;\n }\n\n const clampedProgress = Math.max(0, Math.min(1, progress));\n const traveledDistance = totalDistance * clampedProgress;\n const { angle, point } = getMarkerAngle(\n normalizedPath,\n totalDistance,\n traveledDistance,\n lastAngleRef.current,\n optionsRef.current\n );\n lastAngleRef.current = angle;\n\n const nextState: RoutePlaybackState = {\n isPlaying: keepPlaying,\n isPaused: !keepPlaying && clampedProgress > 0 && clampedProgress < 1,\n progress: clampedProgress,\n currentPosition: point ?? normalizedPath[0] ?? null,\n currentAngle: angle,\n totalDistance,\n traveledDistance,\n durationSeconds,\n smoothMovePath: keepPlaying ? normalizedPath : undefined,\n smoothMoveDuration: keepPlaying ? durationSeconds : undefined,\n };\n\n publishState(nextState);\n\n if (optionsRef.current.followCamera === true && nextState.currentPosition && map) {\n await map.moveCamera(\n {\n target: nextState.currentPosition,\n zoom: optionsRef.current.followZoom ?? 17,\n bearing: angle,\n },\n optionsRef.current.updateIntervalMs ?? 100\n );\n }\n\n return nextState;\n },\n [\n durationSeconds,\n map,\n normalizedPath,\n publishState,\n totalDistance,\n ]\n );\n\n const runPlaybackTick = React.useCallback(async () => {\n const elapsedMs = Date.now() - startAtRef.current + elapsedBeforePauseRef.current;\n const progress = durationSeconds <= 0 ? 1 : elapsedMs / (durationSeconds * 1000);\n\n if (progress >= 1) {\n stopTimer();\n elapsedBeforePauseRef.current = 0;\n startAtRef.current = 0;\n const completedState = await syncProgress(1, false);\n optionsRef.current.onComplete?.(completedState);\n return;\n }\n\n await syncProgress(progress, true);\n }, [durationSeconds, stopTimer, syncProgress]);\n\n const startTimer = React.useCallback(() => {\n stopTimer();\n const interval = Math.max(optionsRef.current.updateIntervalMs ?? 100, 16);\n timerRef.current = setInterval(() => {\n void runPlaybackTick();\n }, interval);\n }, [runPlaybackTick, stopTimer]);\n\n const stop = React.useCallback(() => {\n stopTimer();\n elapsedBeforePauseRef.current = 0;\n startAtRef.current = 0;\n lastAngleRef.current = 0;\n publishState({\n ...DEFAULT_STATE,\n currentPosition: normalizedPath[0] ?? null,\n totalDistance,\n durationSeconds,\n });\n }, [durationSeconds, normalizedPath, publishState, stopTimer, totalDistance]);\n\n const start = React.useCallback(async () => {\n stopTimer();\n elapsedBeforePauseRef.current = 0;\n startAtRef.current = Date.now();\n lastAngleRef.current = 0;\n\n if (optionsRef.current.autoFit === true && map && normalizedPath.length > 0) {\n await fitCameraToCoordinates(map, normalizedPath, optionsRef.current.fitOptions);\n }\n\n await syncProgress(0, true);\n startTimer();\n }, [\n map,\n normalizedPath,\n startTimer,\n syncProgress,\n ]);\n\n const pause = React.useCallback(() => {\n if (!timerRef.current || durationSeconds <= 0) {\n return;\n }\n\n stopTimer();\n elapsedBeforePauseRef.current += Date.now() - startAtRef.current;\n startAtRef.current = 0;\n const currentState = stateRef.current;\n publishState({\n ...currentState,\n isPlaying: false,\n isPaused: true,\n smoothMovePath: undefined,\n smoothMoveDuration: undefined,\n });\n }, [durationSeconds, publishState, stopTimer]);\n\n const resume = React.useCallback(async () => {\n const currentState = stateRef.current;\n if (currentState.progress >= 1) {\n await start();\n return;\n }\n\n startAtRef.current = Date.now();\n publishState({\n ...currentState,\n isPlaying: true,\n isPaused: false,\n smoothMovePath: normalizedPath,\n smoothMoveDuration: durationSeconds,\n });\n startTimer();\n }, [\n durationSeconds,\n normalizedPath,\n publishState,\n start,\n startTimer,\n ]);\n\n const seek = React.useCallback(\n (progress: number) => {\n // seek 只重算“已经播放过的时间”,其余状态由 syncProgress 统一刷新。\n const clamped = Math.max(0, Math.min(1, progress));\n elapsedBeforePauseRef.current = clamped * durationSeconds * 1000;\n startAtRef.current = Date.now();\n void syncProgress(clamped, !!timerRef.current);\n },\n [durationSeconds, syncProgress]\n );\n\n const setSpeedMultiplier = React.useCallback((multiplier: number) => {\n setSpeedMultiplierState(Math.max(multiplier, 0.1));\n }, []);\n\n React.useEffect(() => () => stopTimer(), [stopTimer]);\n\n React.useEffect(() => {\n const pathChanged = previousPathRef.current !== normalizedPath;\n const durationChanged =\n previousDurationRef.current !== null && previousDurationRef.current !== durationSeconds;\n\n previousPathRef.current = normalizedPath;\n previousDurationRef.current = durationSeconds;\n\n if (pathChanged) {\n stopTimer();\n elapsedBeforePauseRef.current = 0;\n startAtRef.current = 0;\n lastAngleRef.current = 0;\n publishState({\n ...DEFAULT_STATE,\n currentPosition: normalizedPath[0] ?? null,\n totalDistance,\n durationSeconds,\n });\n return;\n }\n\n if (!durationChanged) {\n return;\n }\n\n const currentState = stateRef.current;\n\n if (currentState.isPlaying) {\n elapsedBeforePauseRef.current = currentState.progress * durationSeconds * 1000;\n startAtRef.current = Date.now();\n publishState({\n ...currentState,\n totalDistance,\n durationSeconds,\n smoothMoveDuration: durationSeconds,\n });\n startTimer();\n return;\n }\n\n if (currentState.isPaused) {\n elapsedBeforePauseRef.current = currentState.progress * durationSeconds * 1000;\n startAtRef.current = 0;\n publishState({\n ...currentState,\n totalDistance,\n durationSeconds,\n });\n return;\n }\n\n publishState({\n ...currentState,\n currentPosition:\n currentState.progress > 0 ? currentState.currentPosition : normalizedPath[0] ?? null,\n totalDistance,\n durationSeconds,\n });\n }, [durationSeconds, normalizedPath, publishState, startTimer, stopTimer, totalDistance]);\n\n return {\n ...state,\n totalDistance,\n durationSeconds,\n start: () => {\n void start();\n },\n pause,\n resume: () => {\n void resume();\n },\n stop,\n seek,\n setSpeedMultiplier,\n };\n}\n"]}
|
|
@@ -170,24 +170,24 @@ export declare enum MapType {
|
|
|
170
170
|
/**
|
|
171
171
|
* 标准地图
|
|
172
172
|
*/
|
|
173
|
-
Standard =
|
|
173
|
+
Standard = 1,
|
|
174
174
|
/**
|
|
175
175
|
* 卫星地图
|
|
176
176
|
*/
|
|
177
|
-
Satellite =
|
|
177
|
+
Satellite = 2,
|
|
178
178
|
/**
|
|
179
179
|
* 夜间地图
|
|
180
180
|
*/
|
|
181
|
-
Night =
|
|
181
|
+
Night = 3,
|
|
182
182
|
/**
|
|
183
183
|
* 导航地图
|
|
184
184
|
*/
|
|
185
|
-
Navi =
|
|
185
|
+
Navi = 4,
|
|
186
186
|
/**
|
|
187
187
|
* 公交地图
|
|
188
188
|
* @platform android
|
|
189
189
|
*/
|
|
190
|
-
Bus =
|
|
190
|
+
Bus = 5
|
|
191
191
|
}
|
|
192
192
|
/**
|
|
193
193
|
* 颜色值类型
|
|
@@ -6,23 +6,23 @@ export var MapType;
|
|
|
6
6
|
/**
|
|
7
7
|
* 标准地图
|
|
8
8
|
*/
|
|
9
|
-
MapType[MapType["Standard"] =
|
|
9
|
+
MapType[MapType["Standard"] = 1] = "Standard";
|
|
10
10
|
/**
|
|
11
11
|
* 卫星地图
|
|
12
12
|
*/
|
|
13
|
-
MapType[MapType["Satellite"] =
|
|
13
|
+
MapType[MapType["Satellite"] = 2] = "Satellite";
|
|
14
14
|
/**
|
|
15
15
|
* 夜间地图
|
|
16
16
|
*/
|
|
17
|
-
MapType[MapType["Night"] =
|
|
17
|
+
MapType[MapType["Night"] = 3] = "Night";
|
|
18
18
|
/**
|
|
19
19
|
* 导航地图
|
|
20
20
|
*/
|
|
21
|
-
MapType[MapType["Navi"] =
|
|
21
|
+
MapType[MapType["Navi"] = 4] = "Navi";
|
|
22
22
|
/**
|
|
23
23
|
* 公交地图
|
|
24
24
|
* @platform android
|
|
25
25
|
*/
|
|
26
|
-
MapType[MapType["Bus"] =
|
|
26
|
+
MapType[MapType["Bus"] = 5] = "Bus";
|
|
27
27
|
})(MapType || (MapType = {}));
|
|
28
28
|
//# sourceMappingURL=common.types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.types.js","sourceRoot":"","sources":["../../src/types/common.types.ts"],"names":[],"mappings":"AA8LA;;GAEG;AACH,MAAM,CAAN,IAAY,OA0BX;AA1BD,WAAY,OAAO;IACjB;;OAEG;IACH,6CAAY,CAAA;IAEZ;;OAEG;IACH,+CAAa,CAAA;IAEb;;OAEG;IACH,uCAAS,CAAA;IAET;;OAEG;IACH,qCAAQ,CAAA;IAER;;;OAGG;IACH,mCAAO,CAAA;AACT,CAAC,EA1BW,OAAO,KAAP,OAAO,QA0BlB","sourcesContent":["/**\n * 高德地图通用类型定义\n * 基于 Expo Modules API\n */\nimport { PermissionResponse } from 'expo-modules-core';\n\n/**\n * SDK 配置参数\n */\nexport interface SDKConfig {\n /** Android 平台的高德地图 API Key */\n androidKey?: string;\n /** iOS 平台的高德地图 API Key */\n iosKey?: string;\n /** web api key 如果要使用expo-gaode-map-web-api相关的功能,需要配置web api key*/\n webKey?: string;\n}\n\n/**\n * 隐私同意配置\n * 推荐优先使用该配置对象一次性完成隐私状态同步\n */\nexport interface PrivacyConfig {\n /** 是否已经向用户展示隐私弹窗或隐私说明 */\n hasShow: boolean;\n /** 展示内容中是否包含隐私政策条款 */\n hasContainsPrivacy: boolean;\n /** 用户是否已经同意隐私政策 */\n hasAgree: boolean;\n /** 可选的隐私协议版本号;变更后会要求重新同意 */\n privacyVersion?: string;\n}\n/**\n * 隐私政策状态\n */\nexport interface PrivacyStatus {\n hasShow: boolean;\n hasContainsPrivacy: boolean;\n hasAgree: boolean;\n isReady: boolean;\n privacyVersion?: string | null;\n agreedPrivacyVersion?: string | null;\n restoredFromStorage?: boolean;\n}\n\n/**\n * 权限状态(增强版,支持 Android 14+ 和 iOS 17+)\n */\nexport interface PermissionStatus extends PermissionResponse{\n /** 是否已授权(前台位置权限) */\n granted: boolean;\n \n // Android 专用字段\n /** Android 精确位置权限 */\n fineLocation?: boolean;\n /** Android 粗略位置权限 */\n coarseLocation?: boolean;\n /** Android 后台位置权限(Android 10+) */\n backgroundLocation?: boolean;\n /** 是否应显示权限说明(Android) */\n shouldShowRationale?: boolean;\n /** 权限是否被永久拒绝(Android) */\n isPermanentlyDenied?: boolean;\n /** 是否为 Android 14+(Android) */\n isAndroid14Plus?: boolean;\n \n // 其他字段\n /** 额外的消息说明 */\n message?: string;\n}\n\n/**\n * 点坐标(屏幕坐标)\n */\nexport interface Point {\n x: number;\n y: number;\n}\n\n/**\n * 地理坐标\n */\nexport interface LatLng {\n /**\n * 纬度\n */\n latitude: number;\n\n /**\n * 经度\n */\n longitude: number;\n}\n\n/**\n * 坐标点类型\n * 支持对象格式 { latitude, longitude }\n * 或数组格式 [longitude, latitude] (GeoJSON 标准)\n * 注意:GeoJSON 标准允许数组包含更多元素(如海拔),但本组件只使用前两个\n */\nexport type LatLngPoint = LatLng | [number, number] | number[];\n\n/**\n * 地图标注点(POI)\n */\nexport interface MapPoi {\n /**\n * 标注点 ID\n */\n id: string;\n\n /**\n * 标注点名称\n */\n name: string;\n\n /**\n * 标注点坐标\n */\n position: LatLng;\n}\n\n/**\n * 矩形坐标边界\n */\nexport interface LatLngBounds {\n /**\n * 西南坐标\n */\n southwest: LatLng;\n\n /**\n * 东北坐标\n */\n northeast: LatLng;\n}\n\n/**\n * 地图相机位置\n */\nexport interface CameraPosition {\n /**\n * 中心坐标\n */\n target?: LatLng;\n\n /**\n * 缩放级别(3-20)\n */\n zoom?: number;\n\n /**\n * 朝向、旋转角度(0-360度)\n */\n bearing?: number;\n\n /**\n * 倾斜角度(0-60度)\n */\n tilt?: number;\n}\n\n/**\n * 地图相机更新参数\n *\n * 与 `CameraPosition` 不同,这个类型用于描述“局部更新”:\n * 你可以只传 `zoom`、只传 `target`,或组合传入需要变更的字段。\n */\nexport interface CameraUpdate {\n /**\n * 中心坐标\n */\n target?: LatLng;\n\n /**\n * 缩放级别(3-20)\n */\n zoom?: number;\n\n /**\n * 朝向、旋转角度(0-360度)\n */\n bearing?: number;\n\n /**\n * 倾斜角度(0-60度)\n */\n tilt?: number;\n}\n\n/**\n * 地图类型\n */\nexport enum MapType {\n /**\n * 标准地图\n */\n Standard =
|
|
1
|
+
{"version":3,"file":"common.types.js","sourceRoot":"","sources":["../../src/types/common.types.ts"],"names":[],"mappings":"AA8LA;;GAEG;AACH,MAAM,CAAN,IAAY,OA0BX;AA1BD,WAAY,OAAO;IACjB;;OAEG;IACH,6CAAY,CAAA;IAEZ;;OAEG;IACH,+CAAa,CAAA;IAEb;;OAEG;IACH,uCAAS,CAAA;IAET;;OAEG;IACH,qCAAQ,CAAA;IAER;;;OAGG;IACH,mCAAO,CAAA;AACT,CAAC,EA1BW,OAAO,KAAP,OAAO,QA0BlB","sourcesContent":["/**\n * 高德地图通用类型定义\n * 基于 Expo Modules API\n */\nimport { PermissionResponse } from 'expo-modules-core';\n\n/**\n * SDK 配置参数\n */\nexport interface SDKConfig {\n /** Android 平台的高德地图 API Key */\n androidKey?: string;\n /** iOS 平台的高德地图 API Key */\n iosKey?: string;\n /** web api key 如果要使用expo-gaode-map-web-api相关的功能,需要配置web api key*/\n webKey?: string;\n}\n\n/**\n * 隐私同意配置\n * 推荐优先使用该配置对象一次性完成隐私状态同步\n */\nexport interface PrivacyConfig {\n /** 是否已经向用户展示隐私弹窗或隐私说明 */\n hasShow: boolean;\n /** 展示内容中是否包含隐私政策条款 */\n hasContainsPrivacy: boolean;\n /** 用户是否已经同意隐私政策 */\n hasAgree: boolean;\n /** 可选的隐私协议版本号;变更后会要求重新同意 */\n privacyVersion?: string;\n}\n/**\n * 隐私政策状态\n */\nexport interface PrivacyStatus {\n hasShow: boolean;\n hasContainsPrivacy: boolean;\n hasAgree: boolean;\n isReady: boolean;\n privacyVersion?: string | null;\n agreedPrivacyVersion?: string | null;\n restoredFromStorage?: boolean;\n}\n\n/**\n * 权限状态(增强版,支持 Android 14+ 和 iOS 17+)\n */\nexport interface PermissionStatus extends PermissionResponse{\n /** 是否已授权(前台位置权限) */\n granted: boolean;\n \n // Android 专用字段\n /** Android 精确位置权限 */\n fineLocation?: boolean;\n /** Android 粗略位置权限 */\n coarseLocation?: boolean;\n /** Android 后台位置权限(Android 10+) */\n backgroundLocation?: boolean;\n /** 是否应显示权限说明(Android) */\n shouldShowRationale?: boolean;\n /** 权限是否被永久拒绝(Android) */\n isPermanentlyDenied?: boolean;\n /** 是否为 Android 14+(Android) */\n isAndroid14Plus?: boolean;\n \n // 其他字段\n /** 额外的消息说明 */\n message?: string;\n}\n\n/**\n * 点坐标(屏幕坐标)\n */\nexport interface Point {\n x: number;\n y: number;\n}\n\n/**\n * 地理坐标\n */\nexport interface LatLng {\n /**\n * 纬度\n */\n latitude: number;\n\n /**\n * 经度\n */\n longitude: number;\n}\n\n/**\n * 坐标点类型\n * 支持对象格式 { latitude, longitude }\n * 或数组格式 [longitude, latitude] (GeoJSON 标准)\n * 注意:GeoJSON 标准允许数组包含更多元素(如海拔),但本组件只使用前两个\n */\nexport type LatLngPoint = LatLng | [number, number] | number[];\n\n/**\n * 地图标注点(POI)\n */\nexport interface MapPoi {\n /**\n * 标注点 ID\n */\n id: string;\n\n /**\n * 标注点名称\n */\n name: string;\n\n /**\n * 标注点坐标\n */\n position: LatLng;\n}\n\n/**\n * 矩形坐标边界\n */\nexport interface LatLngBounds {\n /**\n * 西南坐标\n */\n southwest: LatLng;\n\n /**\n * 东北坐标\n */\n northeast: LatLng;\n}\n\n/**\n * 地图相机位置\n */\nexport interface CameraPosition {\n /**\n * 中心坐标\n */\n target?: LatLng;\n\n /**\n * 缩放级别(3-20)\n */\n zoom?: number;\n\n /**\n * 朝向、旋转角度(0-360度)\n */\n bearing?: number;\n\n /**\n * 倾斜角度(0-60度)\n */\n tilt?: number;\n}\n\n/**\n * 地图相机更新参数\n *\n * 与 `CameraPosition` 不同,这个类型用于描述“局部更新”:\n * 你可以只传 `zoom`、只传 `target`,或组合传入需要变更的字段。\n */\nexport interface CameraUpdate {\n /**\n * 中心坐标\n */\n target?: LatLng;\n\n /**\n * 缩放级别(3-20)\n */\n zoom?: number;\n\n /**\n * 朝向、旋转角度(0-360度)\n */\n bearing?: number;\n\n /**\n * 倾斜角度(0-60度)\n */\n tilt?: number;\n}\n\n/**\n * 地图类型\n */\nexport enum MapType {\n /**\n * 标准地图\n */\n Standard = 1,\n\n /**\n * 卫星地图\n */\n Satellite = 2,\n\n /**\n * 夜间地图\n */\n Night = 3,\n\n /**\n * 导航地图\n */\n Navi = 4,\n\n /**\n * 公交地图\n * @platform android\n */\n Bus = 5,\n}\n\n/**\n * 颜色值类型\n * 支持:\n * - 十六进制字符串: '#AARRGGBB' 或 '#RRGGBB'\n * - 数字格式: 0xAARRGGBB (用于 Android)\n */\nexport type ColorValue = string | number;\n"]}
|
|
@@ -179,7 +179,7 @@ public class ExpoGaodeMapModule: Module {
|
|
|
179
179
|
|
|
180
180
|
if status == .authorizedAlways || status == .authorizedWhenInUse {
|
|
181
181
|
let manager = self.getLocationManager()
|
|
182
|
-
manager.
|
|
182
|
+
manager.requestSingleLocation { location, regeocode, error in
|
|
183
183
|
if let error = error {
|
|
184
184
|
promise.reject("LOCATION_ERROR", error.localizedDescription)
|
|
185
185
|
return
|
|
@@ -214,7 +214,7 @@ public class ExpoGaodeMapModule: Module {
|
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
promise.resolve(locationData)
|
|
217
|
-
}
|
|
217
|
+
}
|
|
218
218
|
} else {
|
|
219
219
|
promise.reject("LOCATION_ERROR", "location unauthorized")
|
|
220
220
|
}
|
|
@@ -699,8 +699,13 @@ public class ExpoGaodeMapModule: Module {
|
|
|
699
699
|
if self.permissionManager == nil {
|
|
700
700
|
self.permissionManager = PermissionManager()
|
|
701
701
|
}
|
|
702
|
-
|
|
703
|
-
self.permissionManager
|
|
702
|
+
|
|
703
|
+
guard let permissionManager = self.permissionManager else {
|
|
704
|
+
promise.reject("PERMISSION_MANAGER_INIT_FAILED", "权限管理器初始化失败")
|
|
705
|
+
return
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
permissionManager.requestPermission { _, _ in
|
|
704
709
|
// 无论结果如何,都延迟后再次检查最终状态
|
|
705
710
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
|
706
711
|
let finalStatus = self.currentAuthorizationStatus()
|
|
@@ -16,8 +16,8 @@ import QuartzCore
|
|
|
16
16
|
class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate {
|
|
17
17
|
// MARK: - 属性
|
|
18
18
|
|
|
19
|
-
/// 地图类型 (
|
|
20
|
-
var mapType: Int =
|
|
19
|
+
/// 地图类型 (1:标准 2:卫星 3:夜间 4:导航 5:公交)
|
|
20
|
+
var mapType: Int = 1
|
|
21
21
|
/// 初始相机位置
|
|
22
22
|
var initialCameraPosition: [String: Any]?
|
|
23
23
|
/// 是否显示缩放控件
|
|
@@ -96,6 +96,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
|
|
|
96
96
|
private var uiManager: UIManager!
|
|
97
97
|
/// 地图是否已加载完成
|
|
98
98
|
private var isMapLoaded = false
|
|
99
|
+
/// 初始相机是否已应用(仅应用一次,避免与运行时相机控制冲突)
|
|
100
|
+
private var hasAppliedInitialCameraPosition = false
|
|
99
101
|
/// 是否正在处理 annotation 选择事件
|
|
100
102
|
private var isHandlingAnnotationSelect = false
|
|
101
103
|
/// MarkerView 的隐藏容器(用于渲染 children)
|
|
@@ -356,7 +358,7 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
|
|
|
356
358
|
return
|
|
357
359
|
}
|
|
358
360
|
|
|
359
|
-
uiManager.setMapType(
|
|
361
|
+
uiManager.setMapType(1)
|
|
360
362
|
uiManager.setShowsScale(showsScale)
|
|
361
363
|
uiManager.setShowsCompass(showsCompass)
|
|
362
364
|
uiManager.setZoomEnabled(isZoomEnabled)
|
|
@@ -380,9 +382,10 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
|
|
|
380
382
|
|
|
381
383
|
uiManager.setMapType(mapType)
|
|
382
384
|
|
|
383
|
-
//
|
|
384
|
-
if let position = initialCameraPosition {
|
|
385
|
+
// initialCameraPosition 只应用一次,避免每次 props 更新重置相机导致操作延迟感
|
|
386
|
+
if !hasAppliedInitialCameraPosition, let position = initialCameraPosition {
|
|
385
387
|
cameraManager.setInitialCameraPosition(position)
|
|
388
|
+
hasAppliedInitialCameraPosition = true
|
|
386
389
|
}
|
|
387
390
|
|
|
388
391
|
uiManager.setShowsScale(showsScale)
|
|
@@ -413,17 +416,20 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
|
|
|
413
416
|
*/
|
|
414
417
|
private func updateAppleMapStyle() {
|
|
415
418
|
switch mapType {
|
|
416
|
-
case
|
|
419
|
+
case 2: // 卫星
|
|
417
420
|
appleMapView.mapType = .satellite
|
|
418
421
|
appleMapView.overrideUserInterfaceStyle = .unspecified
|
|
419
|
-
case
|
|
422
|
+
case 3: // 夜间
|
|
420
423
|
// 苹果地图没有专门的夜间模式枚举,通过强制 Dark Mode 实现
|
|
421
424
|
appleMapView.mapType = .standard
|
|
422
425
|
appleMapView.overrideUserInterfaceStyle = .dark
|
|
423
|
-
case
|
|
426
|
+
case 4: // 导航
|
|
424
427
|
appleMapView.mapType = .standard
|
|
425
428
|
appleMapView.overrideUserInterfaceStyle = .unspecified
|
|
426
|
-
|
|
429
|
+
case 5: // 公交
|
|
430
|
+
appleMapView.mapType = .standard
|
|
431
|
+
appleMapView.overrideUserInterfaceStyle = .unspecified
|
|
432
|
+
default: // 标准 (1,兼容旧值 0)
|
|
427
433
|
appleMapView.mapType = .standard
|
|
428
434
|
// 标准模式下跟随系统,如果系统是深色则显示深色,否则浅色
|
|
429
435
|
appleMapView.overrideUserInterfaceStyle = .unspecified
|
|
@@ -853,6 +859,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate, UIGestureRecognizerDelegate
|
|
|
853
859
|
|
|
854
860
|
mapView = resolvedMapView
|
|
855
861
|
super.addSubview(resolvedMapView)
|
|
862
|
+
isMapLoaded = false
|
|
863
|
+
hasAppliedInitialCameraPosition = false
|
|
856
864
|
|
|
857
865
|
cameraManager = CameraManager(mapView: resolvedMapView)
|
|
858
866
|
uiManager = UIManager(mapView: resolvedMapView)
|
|
@@ -918,6 +926,12 @@ extension ExpoGaodeMapView {
|
|
|
918
926
|
public func mapViewDidFinishLoadingMap(_ mapView: MAMapView) {
|
|
919
927
|
guard !isMapLoaded else { return }
|
|
920
928
|
isMapLoaded = true
|
|
929
|
+
|
|
930
|
+
// 兜底:若初始化阶段尚未生效,在加载完成后应用一次初始相机
|
|
931
|
+
if !hasAppliedInitialCameraPosition, let position = initialCameraPosition {
|
|
932
|
+
cameraManager?.setInitialCameraPosition(position)
|
|
933
|
+
hasAppliedInitialCameraPosition = true
|
|
934
|
+
}
|
|
921
935
|
|
|
922
936
|
// 地图加载完成后,应用自定义样式
|
|
923
937
|
if let styleData = customMapStyleData {
|
|
@@ -2,14 +2,6 @@ import ExpoModulesCore
|
|
|
2
2
|
import MAMapKit
|
|
3
3
|
import CoreLocation
|
|
4
4
|
|
|
5
|
-
enum MapType: Int, Enumerable {
|
|
6
|
-
case standard = 0
|
|
7
|
-
case satellite = 1
|
|
8
|
-
case night = 2
|
|
9
|
-
case navi = 3
|
|
10
|
-
case bus = 4
|
|
11
|
-
}
|
|
12
|
-
|
|
13
5
|
/**
|
|
14
6
|
* 高德地图视图 Module
|
|
15
7
|
*/
|
|
@@ -20,8 +12,8 @@ public class ExpoGaodeMapViewModule: Module {
|
|
|
20
12
|
View(ExpoGaodeMapView.self) {
|
|
21
13
|
Events("onMapPress", "onPressPoi", "onMapLongPress", "onLoad", "onLocation", "onCameraMove", "onCameraIdle")
|
|
22
14
|
|
|
23
|
-
Prop("mapType") { (view: ExpoGaodeMapView, type:
|
|
24
|
-
view.mapType = type
|
|
15
|
+
Prop("mapType") { (view: ExpoGaodeMapView, type: Int) in
|
|
16
|
+
view.mapType = type
|
|
25
17
|
}
|
|
26
18
|
|
|
27
19
|
Prop("initialCameraPosition") { (view: ExpoGaodeMapView, position: [String: Any]?) in
|
|
@@ -29,14 +29,15 @@ class UIManager: NSObject, MAMapViewDelegate {
|
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* 设置地图类型
|
|
32
|
-
* @param type
|
|
32
|
+
* @param type 1:标准 2:卫星 3:夜间 4:导航 5:公交
|
|
33
33
|
*/
|
|
34
34
|
func setMapType(_ type: Int) {
|
|
35
35
|
guard let mapView = mapView else { return }
|
|
36
36
|
switch type {
|
|
37
|
-
case
|
|
38
|
-
case
|
|
39
|
-
case
|
|
37
|
+
case 2: mapView.mapType = .satellite
|
|
38
|
+
case 3: mapView.mapType = .standardNight
|
|
39
|
+
case 4: mapView.mapType = .navi
|
|
40
|
+
case 5: mapView.mapType = .bus
|
|
40
41
|
default: mapView.mapType = .standard
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -122,6 +122,23 @@ class LocationManager: NSObject, AMapLocationManagerDelegate {
|
|
|
122
122
|
ensureLocationManager()?.stopUpdatingHeading()
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
func requestSingleLocation(completion: @escaping (_ location: CLLocation?, _ reGeocode: AMapLocationReGeocode?, _ error: Error?) -> Void) {
|
|
126
|
+
guard let manager = ensureLocationManager() else {
|
|
127
|
+
let error = NSError(
|
|
128
|
+
domain: "ExpoGaodeMap",
|
|
129
|
+
code: -1,
|
|
130
|
+
userInfo: [NSLocalizedDescriptionKey: "定位管理器未初始化,请先完成隐私协议确认"]
|
|
131
|
+
)
|
|
132
|
+
completion(nil, nil, error)
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
manager.requestLocation(
|
|
137
|
+
withReGeocode: manager.locatingWithReGeocode,
|
|
138
|
+
completionBlock: completion
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
|
|
125
142
|
// MARK: - 初始化
|
|
126
143
|
|
|
127
144
|
@discardableResult
|
|
@@ -622,7 +622,9 @@ class MarkerView: ExpoView {
|
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
private func childrenCacheKey(for size: CGSize) -> String {
|
|
625
|
-
let
|
|
625
|
+
let signature = childrenRenderSignature()
|
|
626
|
+
let baseKey = cacheKey.map { "\($0)|\(signature)" }
|
|
627
|
+
?? "children_\(ObjectIdentifier(self).hashValue)|\(signature)"
|
|
626
628
|
let roundedWidth = Int(ceil(size.width))
|
|
627
629
|
let roundedHeight = Int(ceil(size.height))
|
|
628
630
|
return "\(baseKey)|\(roundedWidth)x\(roundedHeight)"
|
|
@@ -68,7 +68,7 @@ public class MarkerViewModule: Module {
|
|
|
68
68
|
Prop("growAnimation") { (view: MarkerView, enabled: Bool) in
|
|
69
69
|
view.setGrowAnimation(enabled)
|
|
70
70
|
}
|
|
71
|
-
Prop("cacheKey") { (view: MarkerView, key: String) in
|
|
71
|
+
Prop("cacheKey") { (view: MarkerView, key: String?) in
|
|
72
72
|
view.setCacheKey(key)
|
|
73
73
|
}
|
|
74
74
|
|