fix(share-trip): 修复车辆旋转延迟及地图视觉残留问题
- 提前同步车辆朝向参数,修复iOS端车辆marker旋转慢半拍的问题
- 关闭分享页已行驶轨迹绘制,避免出现车辆移动后残留的浅蓝色影子
- 调整 completed 订单的地图视口更新逻辑
- 优化车辆跟踪钩子的代码格式与定时器逻辑
| | |
| | | * 判断经纬度是否发生了可见位移。 |
| | | */ |
| | | const hasMeaningfulLocationDelta = ( |
| | | nextLocation: { latitude: number; longitude: number }, |
| | | nextLocation: { latitude: number; longitude: number }, |
| | | previousLocation: { latitude: number; longitude: number } | null |
| | | ) => { |
| | | if (!previousLocation) { |
| | |
| | | flushTranslateTask(); |
| | | } |
| | | }; |
| | | |
| | | |
| | | // 兜底定时器,防止回调丢失导致动画队列卡死 |
| | | fallbackTimer = setTimeout(() => { |
| | | finishTranslate(); |
| | |
| | | startSite: any; |
| | | endSite: any; |
| | | vehicleLocation: { latitude: number; longitude: number } | null; |
| | | vehicleDirection: number | null; |
| | | vehicleMarkerId: number; |
| | | arrivedMarkerReady: boolean; |
| | | onVehicleMarkerReady: () => void; |
| | |
| | | startSite: null, |
| | | endSite: null, |
| | | vehicleLocation: null, |
| | | vehicleDirection: null, |
| | | vehicleMarkerId: 3, |
| | | arrivedMarkerReady: false, |
| | | onVehicleMarkerReady: () => { } |
| | |
| | | anchor: { x: 0.5, y: 0.5 }, |
| | | zIndex: 2, |
| | | name: '车辆位置', |
| | | ...(deps.isAndroid ? { |
| | | rotate: typeof deps.vehicleDirection === 'number' ? deps.vehicleDirection : 0, |
| | | } : {}), |
| | | customCallout: { anchorY: 80, anchorX: 0, display: 'ALWAYS' } |
| | | }; |
| | | }; |
| | |
| | | deps.endSite?.latitude, |
| | | deps.endSite?.longitude, |
| | | deps.vehicleLocation, |
| | | deps.vehicleDirection, |
| | | deps.vehicleMarkerId, |
| | | deps.isAndroid, |
| | | deps.statusRule.showPickup, |
| | |
| | | |
| | | useEffect(() => { |
| | | viewportKeyRef.current = ""; |
| | | completedOrderDetailViewportKeyRef.current = ""; |
| | | if (deps.tripStatus < TripStatus.ArrivedDestination || deps.statusRule.viewport !== "order_detail") { |
| | | completedOrderDetailViewportKeyRef.current = ""; |
| | | } |
| | | }, [ |
| | | deps.tripStatus, |
| | | deps.statusRule.viewport |
| | | ]); |
| | | |
| | | useEffect(() => { |
| | |
| | | if (deps.statusRule.viewport !== "order_detail") { |
| | | return; |
| | | } |
| | | if (completedOrderDetailViewportKeyRef.current) { |
| | | return; |
| | | } |
| | | const { pickupSite, dropoffSite } = resolveViewportSites(); |
| | | const points: ViewportPoint[] = []; |
| | | const pickupPoint = buildSiteViewportPoint(pickupSite); |
| | |
| | | return; |
| | | } |
| | | const nextKey = buildOrderDetailRouteSignature(deps.routePoints, pickupSite, dropoffSite); |
| | | if (nextKey === completedOrderDetailViewportKeyRef.current) { |
| | | return; |
| | | } |
| | | completedOrderDetailViewportKeyRef.current = nextKey; |
| | | if (orderDetailSyncTimerRef.current) { |
| | | clearTimeout(orderDetailSyncTimerRef.current); |
| | |
| | | }; |
| | | |
| | | const getPassedRoutePoints = () => { |
| | | const shouldShowPassedRoute = deps.tripStatus === TripStatus.InService |
| | | // 对齐 begin-route 当前主链:分享页不单独绘制贴着车辆的浅色已行驶轨迹, |
| | | // 避免视觉上像车辆 marker 移动后残留一层浅蓝色影子。 |
| | | const shouldShowPassedRoute = false |
| | | && deps.tripStatus === TripStatus.InService |
| | | && Array.isArray(deps.vehicleMoveAlongPoints) |
| | | && deps.vehicleMoveAlongPoints.length > 1; |
| | | if (!shouldShowPassedRoute) { |
| | |
| | | servedPointsLength: servedPoints.length, |
| | | routePointsLength: extractedRoutePoints.length, |
| | | }); |
| | | if (typeof resolvedDirection === 'number') { |
| | | // 对齐 begin-route:路线数据到达时先直接同步一次朝向, |
| | | // 不只等后面的 RouteSnapshot 消费,避免 iOS 上旋转看起来慢半拍。 |
| | | deps.setVehicleDirection(resolvedDirection); |
| | | } |
| | | const routeSnapshot = buildShareRouteSnapshot({ |
| | | points: extractedRoutePoints, |
| | | servedPoints, |
| | |
| | | translateSessionIdRef.current += 1; |
| | | }; |
| | | |
| | | const flushTranslateTask = useCallback(() => { |
| | | const notifyVehicleMarkerReady = useCallback(() => { |
| | | if (vehicleMarkerReadyRef.current) { |
| | | return; |
| | | } |
| | | clearVehicleMarkerReadyTimer(); |
| | | vehicleMarkerReadyTimerRef.current = setTimeout(() => { |
| | | vehicleMarkerReadyTimerRef.current = null; |
| | | vehicleMarkerReadyRef.current = true; |
| | | flushTranslateTask(); |
| | | }, FIRST_VEHICLE_MARKER_READY_DELAY_MS); |
| | | }, []); |
| | | |
| | | // 执行一次平移动画,完成后自动消费最新待执行目标 |
| | | const flushTranslateTask = () => { |
| | | if (!vehicleMarkerReadyRef.current) return; |
| | | if (isTranslatingRef.current) return; |
| | | if (!pendingTranslateRef.current) return; |
| | |
| | | pendingTranslateRef.current = null; |
| | | isTranslatingRef.current = true; |
| | | let finished = false; |
| | | let fallbackTimer: ReturnType<typeof setTimeout> | null = null; |
| | | let fallbackTimer: any = null; |
| | | const finishTranslate = () => { |
| | | if (finished) return; |
| | | if (translateSessionId !== translateSessionIdRef.current) return; |
| | | finished = true; |
| | | if (fallbackTimer) { |
| | | clearTimeout(fallbackTimer); |
| | | } |
| | | if (fallbackTimer) clearTimeout(fallbackTimer); |
| | | isTranslatingRef.current = false; |
| | | if (pendingTranslateRef.current) { |
| | | flushTranslateTask(); |
| | | } |
| | | }; |
| | | |
| | | // 兜底定时器,防止回调丢失导致动画队列卡死 |
| | | fallbackTimer = setTimeout(() => { |
| | | finishTranslate(); |
| | | }, TRANSLATE_DURATION_MS + 50); |
| | | |
| | | Taro.nextTick(() => { |
| | | Taro.createMapContext("map").translateMarker({ |
| | | const mapCtx = Taro.createMapContext('map'); |
| | | mapCtx.translateMarker({ |
| | | markerId: task.markerId, |
| | | destination: { |
| | | latitude: task.latitude, |
| | | longitude: task.longitude |
| | | longitude: task.longitude, |
| | | }, |
| | | autoRotate: false, |
| | | moveWithRotate: true, |
| | |
| | | } |
| | | }); |
| | | }); |
| | | }, []); |
| | | |
| | | const notifyVehicleMarkerReady = useCallback(() => { |
| | | if (vehicleMarkerReadyRef.current) { |
| | | return; |
| | | } |
| | | clearVehicleMarkerReadyTimer(); |
| | | vehicleMarkerReadyTimerRef.current = setTimeout(() => { |
| | | vehicleMarkerReadyTimerRef.current = null; |
| | | vehicleMarkerReadyRef.current = true; |
| | | flushTranslateTask(); |
| | | }, FIRST_VEHICLE_MARKER_READY_DELAY_MS); |
| | | }, [flushTranslateTask]); |
| | | }; |
| | | |
| | | // 更新车辆位置并维护最近位置引用 |
| | | const updateVehicleLocationThrottled = ( |
| | |
| | | force = false |
| | | ) => { |
| | | const now = Date.now(); |
| | | if (!force && !Number.isFinite(lat) && !Number.isFinite(lng)) { |
| | | return; |
| | | } |
| | | if (Number.isFinite(lat) && Number.isFinite(lng)) { |
| | | lastVehiclePointRef.current = { latitude: lat, longitude: lng }; |
| | | lastVehicleUpdateAtRef.current = now; |
| | |
| | | duration: hasLocationDelta ? TRANSLATE_DURATION_MS : ROTATE_ONLY_DURATION_MS |
| | | }; |
| | | flushTranslateTask(); |
| | | }, [statusRule.translateCar, vehicleLocation, vehicleDirection, vehicleMarkerId, flushTranslateTask]); |
| | | }, [statusRule.translateCar, vehicleLocation, vehicleDirection, vehicleMarkerId]); |
| | | |
| | | useEffect(() => { |
| | | if (!statusRule.translateCar) { |
| | |
| | | startSite, |
| | | endSite, |
| | | vehicleLocation, |
| | | vehicleDirection, |
| | | vehicleMarkerId, |
| | | arrivedMarkerReady, |
| | | onVehicleMarkerReady: notifyVehicleMarkerReady, |