ai-coding
2026-06-29 2c8c9ca1ea9426e349691bc691864bfcaf1d8baf
fix(map): 优化车标不展示问题

Co-Authored-By: ai-coding <ai-coding@devgenie.local>
2个文件已修改
117 ■■■■■ 已修改文件
src/robotaxi/pages/begin-route/hooks/use-map-markers.ts 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/share-trip/hooks/use-map-markers.ts 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-map-markers.ts
@@ -81,8 +81,10 @@
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);
@@ -90,43 +92,34 @@
  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)
@@ -259,7 +252,7 @@
  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);
@@ -304,7 +297,7 @@
    }
    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;
@@ -315,12 +308,6 @@
  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);
@@ -367,7 +354,8 @@
  }, [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,
src/robotaxi/pages/share-trip/hooks/use-map-markers.ts
@@ -71,7 +71,10 @@
export default function useMapMarkers(rawDeps?: Partial<Deps>) {
    const deps = { ...defaultDeps, ...(rawDeps || {}) } as Deps;
    // 内部"逻辑 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);
@@ -79,41 +82,34 @@
    const rebuildCarMarkerTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
    /**
     * 重建 CarMarker:先移除车辆 marker 再恢复,强制原生层重建以清除 translateMarker 遗留的旋转矩阵。
     * 支持函数式更新(与 setMarkers 行为一致),便于在依赖 prevMarkers 的场景下复用。
     * 当逻辑 markers 变化时,对展示 markers 做"先移除车标 → 50ms 后恢复完整列表"的两阶段提交。
     * 该 effect 与外部 markers useEffect 解耦,避免被频繁的 vehicleLocation/vehicleDirection 更新打断。
     */
    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;
        }
        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;
      }
      // 第一步:展示层先去掉车标,原生层销毁旧 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]);
    const shouldUsePureTranslateCarMarker =
        [TripStatus.VehicleApproaching, TripStatus.InService].includes(deps.tripStatus)
        && deps.statusRule.showCar;
@@ -233,7 +229,7 @@
    ) => {
        if (shouldSkipUpdate) {
            // 行驶中车辆位置和角度完全交由 translateMarker 托管,React 只同步结构变化。
            rebuildCarMarker((prevMarkers: any[]) => {
            setMarkers((prevMarkers: any[]) => {
                const prevMarkersSignature = buildMarkersSignature(
                    prevMarkers,
                    deps.vehicleMarkerId,
@@ -281,7 +277,7 @@
        }
        if (!shouldSkipUpdate && markersSignatureRef.current !== nextMarkersSignature) {
            markersSignatureRef.current = nextMarkersSignature;
            rebuildCarMarker(nextMarkers);
            setMarkers(nextMarkers);
        }
        if (
            !deps.isAndroid &&
@@ -298,12 +294,6 @@
    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);
@@ -391,7 +381,8 @@
    ]);
    return {
        markers,
        // 对外返回经过 rebuild dance 的展示态 markers;内部逻辑(applyMarkerState 等)仍读取 markers(逻辑态)
        markers: displayMarkers,
        setMarkers,
        markersRef,
        markersSignatureRef,