| | |
| | | |
| | | export default function useMapMarkers(rawDeps?: Partial<Deps>) { |
| | | const deps = { ...defaultDeps, ...(rawDeps || {}) } as Deps; |
| | | // 地图 marker 集合 |
| | | // 内部"逻辑 markers"——真相态,所有内部判断(签名、是否含车标等)以此为准 |
| | | const [markers, setMarkers] = useState<any[]>([]); |
| | | // 对外"展示 markers"——返回给页面,通过 rebuild dance 制造短暂"无车标"窗口以重建原生 marker |
| | | const [displayMarkers, setDisplayMarkers] = useState<any[]>([]); |
| | | const markersRef = useRef<any[]>([]); |
| | | const markersSignatureRef = useRef<string>(''); |
| | | const carMarkerInitialized = useRef(false); |
| | |
| | | const rebuildCarMarkerTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
| | | |
| | | /** |
| | | * 重建 CarMarker:先移除车辆 marker 再恢复,强制原生层重建以清除 translateMarker 遗留的旋转矩阵。 |
| | | * 支持函数式更新(与 setMarkers 行为一致),便于在依赖 prevMarkers 的场景下复用。 |
| | | * 当逻辑 markers 变化时,对展示 markers 做"先移除车标 → 50ms 后恢复完整列表"的两阶段提交。 |
| | | * 该 effect 与 markers 解耦,不会被外部 useEffect 频繁触发打断(即便 vehicleLocation/vehicleDirection 更新, |
| | | * 只要 logical markers 引用未变化,rebuild dance 就不会重启)。 |
| | | */ |
| | | const rebuildCarMarker = (updater: any[] | ((prev: any[]) => any[])) => { |
| | | useEffect(() => { |
| | | if (rebuildCarMarkerTimerRef.current) { |
| | | clearTimeout(rebuildCarMarkerTimerRef.current); |
| | | rebuildCarMarkerTimerRef.current = null; |
| | | } |
| | | let resolvedMarkers: any[] | null = null; |
| | | let didChange = false; |
| | | setMarkers((prevMarkers: any[]) => { |
| | | const next = typeof updater === 'function' ? (updater as (prev: any[]) => any[])(prevMarkers) : updater; |
| | | if (next === prevMarkers) { |
| | | return prevMarkers; |
| | | } |
| | | didChange = true; |
| | | resolvedMarkers = next; |
| | | const hasCarMarker = next.some((m: any) => m.id === deps.vehicleMarkerId); |
| | | if (!hasCarMarker) { |
| | | return next; |
| | | } |
| | | // 第一步:先提交不含车辆 marker 的列表,触发原生层销毁旧 car marker |
| | | return next.filter((m: any) => m.id !== deps.vehicleMarkerId); |
| | | }); |
| | | if (!didChange || !resolvedMarkers) { |
| | | return; |
| | | } |
| | | const hasCarMarker = (resolvedMarkers as any[]).some((m: any) => m.id === deps.vehicleMarkerId); |
| | | const hasCarMarker = markers.some((m: any) => m.id === deps.vehicleMarkerId); |
| | | if (!hasCarMarker) { |
| | | setDisplayMarkers(markers); |
| | | return; |
| | | } |
| | | // 第二步:下一帧恢复完整 marker 列表,原生层会重新创建车辆 marker |
| | | // 第一步:展示层先去掉车标,原生层销毁旧 car marker |
| | | setDisplayMarkers(markers.filter((m: any) => m.id !== deps.vehicleMarkerId)); |
| | | // 第二步:下一帧恢复完整列表,原生层重建 car marker(清除 translateMarker 遗留的旋转矩阵) |
| | | rebuildCarMarkerTimerRef.current = setTimeout(() => { |
| | | rebuildCarMarkerTimerRef.current = null; |
| | | setMarkers(resolvedMarkers as any[]); |
| | | setDisplayMarkers(markers); |
| | | }, 50); |
| | | }; |
| | | return () => { |
| | | if (rebuildCarMarkerTimerRef.current) { |
| | | clearTimeout(rebuildCarMarkerTimerRef.current); |
| | | rebuildCarMarkerTimerRef.current = null; |
| | | } |
| | | }; |
| | | }, [markers, deps.vehicleMarkerId]); |
| | | |
| | | // 行驶中/接驾中/到达上车点 车辆 marker 统一交由 translateMarker 托管位置与角度 |
| | | const shouldUsePureTranslateCarMarker = [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService].includes(deps.tripStatus) |
| | |
| | | const applyMarkerState = (nextMarkers: any[], nextMarkersSignature: string, shouldSkipUpdate: boolean, carMarker: any) => { |
| | | if (shouldSkipUpdate) { |
| | | // 平移态下车辆位置完全交给 translateMarker 托管,React 仅同步非车辆 marker 的结构变化 |
| | | rebuildCarMarker((prevMarkers: any[]) => { |
| | | setMarkers((prevMarkers: any[]) => { |
| | | const prevMarkersSignature = buildMarkersSignature(prevMarkers, deps.vehicleMarkerId, shouldUsePureTranslateCarMarker); |
| | | const targetCarIndex = prevMarkers.findIndex((item: any) => item.id === deps.vehicleMarkerId); |
| | | const nextCarIndex = nextMarkers.findIndex((item: any) => item.id === deps.vehicleMarkerId); |
| | |
| | | } |
| | | if (!shouldSkipUpdate && markersSignatureRef.current !== nextMarkersSignature) { |
| | | markersSignatureRef.current = nextMarkersSignature; |
| | | rebuildCarMarker(nextMarkers); |
| | | setMarkers(nextMarkers); |
| | | } |
| | | if (!deps.isAndroid && [TripStatus.VehicleApproaching, TripStatus.VehicleArrivedPickup, TripStatus.InService].includes(deps.tripStatus) && deps.statusRule.showCar && !!carMarker && !tripStatus6MarkerRefreshedRef.current) { |
| | | tripStatus6MarkerRefreshedRef.current = true; |
| | |
| | | useEffect(() => { |
| | | markersRef.current = markers; |
| | | }, [markers]); |
| | | |
| | | useEffect(() => () => { |
| | | if (rebuildCarMarkerTimerRef.current) { |
| | | clearTimeout(rebuildCarMarkerTimerRef.current); |
| | | } |
| | | }, []); |
| | | |
| | | useEffect(() => { |
| | | const hasVehicleMarker = deps.statusRule.showCar && markers.some((item: any) => item.id === deps.vehicleMarkerId); |
| | |
| | | }, [deps.tripStatus, deps.startSite?.name, deps.startSite?.latitude, deps.startSite?.longitude, deps.endSite?.name, deps.endSite?.latitude, deps.endSite?.longitude, deps.beforeUserEndSite?.latitude, deps.beforeUserEndSite?.longitude, deps.vehicleLocation, deps.vehicleDirection, deps.vehicleMarkerId, deps.isAndroid, deps.statusRule.showPickup, deps.statusRule.showCar, deps.statusRule.showDropoff, deps.isModifyDestinationReviewVisible, deps.showTrip1StyleEndpoint]); |
| | | |
| | | return { |
| | | markers, |
| | | // 对外返回经过 rebuild dance 的展示态 markers;内部逻辑(applyMarkerState 等)仍读取 markers(逻辑态) |
| | | markers: displayMarkers, |
| | | setMarkers, |
| | | markersRef, |
| | | markersSignatureRef, |