lucl
2026-08-11 94c7b8acb973cebbe69c477c3c7654bdb8f32251
feat: 行程中直达评价页增加动效
3个文件已修改
70 ■■■■■ 已修改文件
src/robotaxi/pages/begin-route/components/calling-car.tsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/robotaxi/pages/begin-route/components/calling-car.tsx
@@ -26,6 +26,8 @@
    tripStatus?: TripStatus;
    /** 是否在本次挂载时播放"派单成功"转场(由父层捕捉首次进入派单成功(4)边沿传入,因本组件被 key 重挂载无法自判) */
    playDispatchTransition?: boolean;
    /** 是否在本次挂载时播放"直达待评价"入场(由父层捕捉 送驾中(6)→待评价(9) 边沿传入,内部免费订单跳过待支付时使用) */
    playEvaluateEnter?: boolean;
    /** 派单成功转场动效完成(或本次不播放转场)时回调,用于放行页面级蓝牙初始化 */
    onDispatchTransitionSettled?: () => void;
    /** 打开取消弹窗,入参为预计到达展示文案 */
@@ -119,6 +121,7 @@
export default function CallingCar({
    tripStatus = TripStatus.CallingCar,
    playDispatchTransition = false,
    playEvaluateEnter = false,
    onDispatchTransitionSettled,
    handleOpenCancelPopup,
    handleCancelTimeout,
@@ -197,6 +200,12 @@
    const isTripFinished = tripStatus === TripStatus.TripFinished;
    const isCallingCar = tripStatus === TripStatus.CallingCar;
    /**
     * 「直达待评价」入场快照:与 useDispatchTransition 里的 shouldPlayEnter 同理,
     * 父层的 playEvaluateEnter 在下一次渲染即翻 false,直接绑定会把动画类中途卸掉、动效被扼杀,
     * 故在挂载时快照一次,组件生命周期内不变(本组件由 key={tripStatus} 保证每次状态切换都重挂载)。
     */
    const [shouldPlayEvaluateEnter] = useState(playEvaluateEnter);
    /**
     * 车控(空调 / 音乐 / 香氛):卡片与弹窗分开挂载。
     * 卡片放进横向滑动的 ScrollView;弹窗是 fixed 全屏浮层,必须挂在 ScrollView 之外,
     * 否则 iOS(WKWebView)下滚动容器会成为 fixed 的包含块,弹窗被限制在卡片滑动区内。
@@ -255,8 +264,8 @@
      }
      return  <Image className='calling-car-bg' src={CARD_BG_IMG} />
    }
    // 根节点状态类:呼叫中覆盖 + 待支付态 + 明细展开态 + 派单转场阶段 + 整卡升起入场
    const rootClassName = `calling-car${isCallingCar ? ' calling-status-car' : ''}${isTripFinished ? ' calling-car--finished' : ''}${isTripFinished && detailExpanded ? ' is-detail-expanded' : ''}${dispatchPhase !== 'idle' ? ` dispatch-${dispatchPhase}` : ''}${shouldPlayEnter ? ' calling-car--enter' : ''}${!contentReady ? ' dispatch-defer-content' : ''}`;
    // 根节点状态类:呼叫中覆盖 + 待支付态 + 明细展开态 + 派单转场阶段 + 整卡升起入场 + 直达待评价入场
    const rootClassName = `calling-car${isCallingCar ? ' calling-status-car' : ''}${isTripFinished ? ' calling-car--finished' : ''}${isTripFinished && detailExpanded ? ' is-detail-expanded' : ''}${dispatchPhase !== 'idle' ? ` dispatch-${dispatchPhase}` : ''}${shouldPlayEnter ? ' calling-car--enter' : ''}${shouldPlayEvaluateEnter ? ' calling-car--evaluate-enter' : ''}${!contentReady ? ' dispatch-defer-content' : ''}`;
    // 按状态组合渲染行程卡片模块
    return (
src/robotaxi/pages/begin-route/components/css-folder/calling-car.less
@@ -136,6 +136,53 @@
    transform: translateY(0);
  }
}
// 「送驾中(6) 直达待评价(9)」整卡入场:内部免费订单跳过待支付(8),原本是无动效硬切。
// 卡片容器为底部锚定(.container position:fixed bottom:0),待评价态内容更高会瞬间向上顶,
// 故从「相当于送驾态卡顶」的位置升起,用运动吃掉高度差;同时做轻微透明度渐变,柔化内容替换。
//
// 起升距离 = 待评价态与送驾态的卡片高度差。
// 实测依据(同机型两态截图,designWidth 750 下 px 即设计稿单位):以两态共有的首个内容块
// VehicleInfoCard(“小鹏GX”行)到屏幕底部的距离比对,送驾态约 288px、待评价态约 659px,
// 即待评价态多出约 370px(新增费用明细两行 + 评价卡,减去状态条 + 操作栏)。
// 用 LESS 变量而非 CSS 自定义属性:keyframes 内的 var() 在旧版 WKWebView 上存在不生效的风险,
// LESS 变量在编译期就展开为字面量,按真机观感调这一处即可。
@evaluate-enter-rise: 380px;
@keyframes calling-car-evaluate-rise {
  from {
    transform: translateY(@evaluate-enter-rise);
    opacity: 0.4;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}
// 待评价态新增内容(费用明细卡 / 评价卡)分层浮现:轻微上移淡入,手法对齐 finished-distance-fade。
@keyframes calling-car-evaluate-content-fade {
  from {
    transform: translateY(16px);
    opacity: 0;
  }
  to {
    transform: translateY(0);
    opacity: 1;
  }
}
.calling-car--evaluate-enter {
  // 位移行程约 380px(大于派单入场的 170px),时长相应放到 0.7s,保持速度感与派单入场一致。
  animation: calling-car-evaluate-rise 0.7s cubic-bezier(0.22, 0.61, 0.36, 1) both;
  will-change: transform;
  // 分层:整卡升起过半后,新增的费用明细卡与评价卡再依次浮现,进一步掩盖高度差。
  .fare-box-card {
    animation: calling-car-evaluate-content-fade 600ms ease-out 420ms both;
  }
  .car-card-evaluate {
    animation: calling-car-evaluate-content-fade 600ms ease-out 560ms both;
  }
}
// 派单成功转场期间:蓝牙卡 / 车控卡 / 底部操作栏隐藏但保留占位(opacity),逻辑另在各组件延迟执行;
// 动效完成后移除 .dispatch-defer-content,随 opacity 过渡淡入。用 opacity 而非 visibility,保证淡入过渡生效。
.calling-car {
src/robotaxi/pages/begin-route/hooks/use-view-delegates.tsx
@@ -321,6 +321,15 @@
  const playDispatchTransition =
    deps.tripStatus === TripStatus.VehicleApproaching &&
    prevTripStatusRef.current !== TripStatus.VehicleApproaching;
  // 捕捉「送驾中(6) 直达待评价(9)」边沿:内部免费订单无需支付,行程结束后跳过待支付(8)直接进入待评价。
  // 该路径原本没有任何入场动效,且待评价态内容比送驾态更高(多出费用明细卡与评价卡),
  // 底部锚定容器(.container position:fixed bottom:0)会瞬间向上顶,观感生硬。
  // 与 playDispatchTransition 同理:CallingCar 被 key 重挂载无法自判边沿,由父层捕捉后传入,
  // 组件内做挂载快照,再用挂载即播的 keyframes 实现整卡升起 + 内容分层浮现。
  // 限定上一态为 InService:不影响支付后 8→9 的既有表现;冷启动恢复时 prev 为 undefined 也不会误播。
  const playEvaluateEnter =
    deps.tripStatus === TripStatus.PaymentSuccess &&
    prevTripStatusRef.current === TripStatus.InService;
  useEffect(() => {
    prevTripStatusRef.current = deps.tripStatus;
  }, [deps.tripStatus]);
@@ -1035,6 +1044,7 @@
        <CallingCar
          key={deps.tripStatus}
          playDispatchTransition={playDispatchTransition}
          playEvaluateEnter={playEvaluateEnter}
          onDispatchTransitionSettled={deps.onDispatchTransitionSettled}
          orderId={deps.orderId}
          hasBeforeUserEndSite={deps.hasBeforeUserEndSite}