refactor: 抽离底部弹窗动画hook并优化样式
| | |
| | | import { View, Image, Video } from '@tarojs/components'; |
| | | import { useEffect, useState } from 'react'; |
| | | import './css-folder/announcements-popup.less' |
| | | import useBottomPopupAnimation from './hooks/use-bottom-popup-animation'; |
| | | |
| | | type Props = { |
| | | show?: boolean; |
| | |
| | | } |
| | | |
| | | export default function AnnouncementsPopup({ show = false, onClose }: Props) { |
| | | const [visible, setVisible] = useState(false); // 弹窗显示状态 |
| | | const [animationData, setAnimationData] = useState<any>(); // 弹窗开合动画 |
| | | const { visible, animationData } = useBottomPopupAnimation(show); |
| | | const [doorImgError, setDoorImgError] = useState(false); // 开门动画图加载失败状态 |
| | | const [seatbeltImgError, setSeatbeltImgError] = useState(false); // 系安全带动画图加载失败状态 |
| | | |
| | | const doorImage = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/door-anim.webp' |
| | | const seatbeltImage = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/seatbelt-anim.webp' |
| | | |
| | | // 控制弹窗开合动画并重置图片兜底状态 |
| | | // 打开弹窗时重置图片兜底状态 |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | if (show) { |
| | | setDoorImgError(false); |
| | | setSeatbeltImgError(false); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | | }); |
| | | animation.translateY(0).step(); |
| | | setAnimationData(animation.export()); |
| | | } else if (!show && visible) { |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.translateY(500).step(); |
| | | setAnimationData(animation.export()); |
| | | |
| | | setTimeout(() => { |
| | | setVisible(false); |
| | | }, 300); |
| | | } |
| | | }, [show]); |
| | | |
| | |
| | | import Taro from '@tarojs/taro'; |
| | | import { View, Text, Image } from '@tarojs/components'; |
| | | import { useEffect, useState } from 'react'; |
| | | import './index.less'; |
| | | import useBottomPopupAnimation from '../hooks/use-bottom-popup-animation'; |
| | | |
| | | type Props = { |
| | | show: boolean; |
| | |
| | | }; |
| | | |
| | | export default function AuthBluetoothPopup({ show, onClose, onAuthorize }: Props) { |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | const { animationData, shouldRender } = useBottomPopupAnimation(show); |
| | | |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | | }); |
| | | animation.opacity(1).translateY(0).step(); |
| | | setAnimationData(animation.export()); |
| | | } else if (!show && visible) { |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.opacity(1).translateY('100%').step(); |
| | | setAnimationData(animation.export()); |
| | | setTimeout(() => { |
| | | setVisible(false); |
| | | }, 300); |
| | | } |
| | | }, [show, visible]); |
| | | |
| | | if (!visible && !show) return null; |
| | | if (!shouldRender) return null; |
| | | |
| | | return ( |
| | | <View className={`auth-bluetooth-popup ${show ? 'show' : ''}`} catchMove> |
| | |
| | | import Taro from '@tarojs/taro'; |
| | | import { View, Text, Image } from '@tarojs/components'; |
| | | import { useEffect, useState } from 'react'; |
| | | import './index.less'; |
| | | import useBottomPopupAnimation from '../../hooks/use-bottom-popup-animation'; |
| | | |
| | | type Props = { |
| | | show: boolean; |
| | |
| | | }; |
| | | |
| | | export default function SuccessAuthPopup({ show, onClose, onConfirm }: Props) { |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | const { animationData, shouldRender } = useBottomPopupAnimation(show); |
| | | |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | | }); |
| | | animation.opacity(1).translateY(0).step(); |
| | | setAnimationData(animation.export()); |
| | | } else if (!show && visible) { |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.opacity(1).translateY('100%').step(); |
| | | setAnimationData(animation.export()); |
| | | setTimeout(() => { |
| | | setVisible(false); |
| | | }, 300); |
| | | } |
| | | }, [show, visible]); |
| | | |
| | | if (!visible && !show) return null; |
| | | if (!shouldRender) return null; |
| | | |
| | | const handleConfirm = () => { |
| | | onConfirm?.(); |
| | |
| | | import Taro from "@tarojs/taro"; |
| | | import { View, Image } from "@tarojs/components"; |
| | | import { View, Image, Text } from "@tarojs/components"; |
| | | import { useState, useEffect, useRef } from "react"; |
| | | // TODO(仅用于测试): 上线前将此处改回从 '../../../../api/begin-route' 导入 './test-case/ac-mock' |
| | | import { |
| | |
| | | |
| | | const canToggleSwitch = tripStatus === TripStatus.VehicleApproaching && !isTogglingAc; |
| | | const canAdjustTemperature = canToggleSwitch && acStatusLoaded && acSwitchOn && typeof tempStatus === 'number'; |
| | | const shouldShowTemperatureUnit = typeof tempStatus === 'number' && tempStatus > TEMP_MIN && tempStatus < TEMP_MAX; |
| | | // 温度展示文案:到达最高温显示 HI,到达最低温显示 LO,其余显示具体温度值 |
| | | const tempDisplayText = typeof tempStatus === 'number' |
| | | ? (tempStatus >= TEMP_MAX ? 'HI' : (tempStatus <= TEMP_MIN ? 'LO' : `${tempStatus.toFixed(1)}℃`)) |
| | | ? (tempStatus >= TEMP_MAX ? 'HI' : (tempStatus <= TEMP_MIN ? 'LO' : tempStatus.toFixed(1))) |
| | | : '--'; |
| | | const isAtMinTemperature = typeof tempStatus === 'number' && tempStatus <= TEMP_MIN; |
| | | const isAtMaxTemperature = typeof tempStatus === 'number' && tempStatus >= TEMP_MAX; |
| | |
| | | } |
| | | </View> |
| | | <View className="car-card-temp-num-text"> |
| | | <View className={`car-card-temp-num-text-temp ${tempTextDisabledClassName}`.trim()}>{tempDisplayText}</View> |
| | | <View className={`car-card-temp-num-text-temp ${tempTextDisabledClassName}`.trim()}> |
| | | <View className="car-card-temp-num-text-temp-value">{tempDisplayText}</View> |
| | | {shouldShowTemperatureUnit && ( |
| | | <View className="car-card-temp-num-text-temp-unit"> |
| | | <View className="car-card-temp-num-text-temp-unit-degree">°</View> |
| | | <View className="car-card-temp-num-text-temp-unit-letter">C</View> |
| | | </View> |
| | | )} |
| | | </View> |
| | | <View className={`car-card-temp-num-text-desc ${tempTextDisabledClassName}`.trim()}>设定温度</View> |
| | | </View> |
| | | <View className="car-card-temp-num-btn"> |
| | |
| | | clearOrderTripStorage |
| | | } from '../../../../utils/Robotaxi-public-method' |
| | | import { TripStatus } from '../../../constants/trip-status'; |
| | | import useBottomPopupAnimation from './hooks/use-bottom-popup-animation'; |
| | | |
| | | |
| | | type Props = { |
| | |
| | | } |
| | | |
| | | export default function CancelPopup({ show = false, cancelType = 1, startSite, endSite, waitMinutes, tripStatus, routeDurationSeconds, discountAmount, onClose, onConfirm }: Props) { |
| | | const [visible, setVisible] = useState(false); // 弹窗显示状态 |
| | | const [animationData, setAnimationData] = useState<any>(); // 弹窗开合动画 |
| | | const { visible, animationData } = useBottomPopupAnimation(show); |
| | | const cachedStartSite = startSite || Taro.getStorageSync('startSite') || {}; |
| | | const cachedEndSite = endSite || Taro.getStorageSync('endSite') || {}; |
| | | const startSiteName = cachedStartSite?.name || cachedStartSite?.address || '-'; |
| | |
| | | tip: '当前车辆无法继续服务', |
| | | }, |
| | | } |
| | | |
| | | // 控制弹窗开合动画 |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | | }); |
| | | animation.translateY(0).step(); |
| | | setAnimationData(animation.export()); |
| | | } else if (!show && visible) { |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.translateY(500).step(); |
| | | setAnimationData(animation.export()); |
| | | |
| | | setTimeout(() => { |
| | | setVisible(false); |
| | | }, 300); |
| | | } |
| | | }, [show]); |
| | | |
| | | // 关闭弹窗时按类型执行对应流程 |
| | | const closePopup = () => { |
| | |
| | | gap: 16px; |
| | | |
| | | &-temp { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | font-size: 52px; |
| | | font-weight: 500; |
| | | line-height: 48px; |
| | | letter-spacing: normal; |
| | | color: rgba(0, 0, 0, 0.9); |
| | | |
| | | &-value { |
| | | line-height: 48px; |
| | | } |
| | | |
| | | &-unit { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | margin-top: 0; |
| | | margin-left: 2px; |
| | | line-height: 1; |
| | | |
| | | &-degree { |
| | | font-size: 40px; |
| | | line-height: 24px; |
| | | } |
| | | |
| | | &-letter { |
| | | margin-top: 0; |
| | | margin-left: 1px; |
| | | font-size: 32px; |
| | | line-height: 24px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &-desc { |
| | |
| | | height: 17.5px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import { _ROBOTAXI_HOST } from '../../../../constants/api'; |
| | | import useImageUpload from '../../../hooks/use-image-upload'; |
| | | import { getOrderId } from '../../../utils/order'; |
| | | import useBottomPopupAnimation from './hooks/use-bottom-popup-animation'; |
| | | |
| | | type Props = { |
| | | show?: boolean; |
| | |
| | | const activeBad = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/active-bad.png'; |
| | | const activeNormal = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/active-normal.png'; |
| | | const activeGood = 'https://s.xiaopeng.com/xp-fe/robot-taxi/imgs/active-good.png'; |
| | | const [visible, setVisible] = useState(false); // 弹窗显示状态 |
| | | const [animationData, setAnimationData] = useState<any>(); // 弹窗进出场动画 |
| | | const { visible, animationData } = useBottomPopupAnimation(show); |
| | | const [selectedTypeId, setSelectedTypeId] = useState<number | null>(null); // 当前选择的问题类型 |
| | | const [feedbackContent, setFeedbackContent] = useState(''); // 反馈文本内容 |
| | | const [typeOptions, setTypeOptions] = useState<any[]>([]); // 问题类型选项 |
| | |
| | | const evaluateDimension = evaluateDimensionMap[currentOrderRating] || 3; // 接口维度:1 满意,2 一般,3 不满意 |
| | | const shouldRequireFeedbackType = !hideFeedbackType; |
| | | const canSubmit = shouldRequireFeedbackType ? !!selectedTypeId && !!feedbackContent.trim() : !!feedbackContent.trim(); |
| | | |
| | | // 控制弹窗开合动画 |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | | }); |
| | | animation.translateY(0).step(); |
| | | setAnimationData(animation.export()); |
| | | } else if (!show && visible) { |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.translateY(500).step(); |
| | | setAnimationData(animation.export()); |
| | | |
| | | setTimeout(() => { |
| | | setVisible(false); |
| | | }, 300); |
| | | } |
| | | }, [show]); |
| | | |
| | | // 提交反馈(评价/投诉) |
| | | const submit = () => { |
| New file |
| | |
| | | import Taro from '@tarojs/taro'; |
| | | import { useEffect, useRef, useState } from 'react'; |
| | | |
| | | type UseBottomPopupAnimationOptions = { |
| | | duration?: number; |
| | | openTimingFunction?: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out'; |
| | | closeTimingFunction?: 'linear' | 'ease' | 'ease-in' | 'ease-out' | 'ease-in-out'; |
| | | offsetY?: number | string; |
| | | }; |
| | | |
| | | const ENTER_START_DELAY_MS = 30; |
| | | |
| | | /** |
| | | * 统一管理底部弹窗的开合动画,确保每次打开都能从底部稳定滑入。 |
| | | */ |
| | | export default function useBottomPopupAnimation( |
| | | show: boolean, |
| | | options: UseBottomPopupAnimationOptions = {} |
| | | ) { |
| | | const { |
| | | duration = 300, |
| | | openTimingFunction = 'ease-out', |
| | | closeTimingFunction = 'ease-in', |
| | | offsetY = '100%' |
| | | } = options; |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | const enterTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
| | | const exitTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
| | | |
| | | useEffect(() => { |
| | | if (enterTimerRef.current) { |
| | | clearTimeout(enterTimerRef.current); |
| | | enterTimerRef.current = null; |
| | | } |
| | | if (exitTimerRef.current) { |
| | | clearTimeout(exitTimerRef.current); |
| | | exitTimerRef.current = null; |
| | | } |
| | | |
| | | if (show) { |
| | | setVisible(true); |
| | | const enterInitialAnimation = Taro.createAnimation({ |
| | | duration: 0, |
| | | timingFunction: 'linear' |
| | | }); |
| | | enterInitialAnimation.translateY(offsetY).step(); |
| | | setAnimationData(enterInitialAnimation.export()); |
| | | |
| | | enterTimerRef.current = setTimeout(() => { |
| | | const enterAnimation = Taro.createAnimation({ |
| | | duration, |
| | | timingFunction: openTimingFunction |
| | | }); |
| | | enterAnimation.translateY(0).step(); |
| | | setAnimationData(enterAnimation.export()); |
| | | enterTimerRef.current = null; |
| | | }, ENTER_START_DELAY_MS); |
| | | return; |
| | | } |
| | | |
| | | if (!visible) { |
| | | return; |
| | | } |
| | | |
| | | const exitAnimation = Taro.createAnimation({ |
| | | duration, |
| | | timingFunction: closeTimingFunction |
| | | }); |
| | | exitAnimation.translateY(offsetY).step(); |
| | | setAnimationData(exitAnimation.export()); |
| | | |
| | | exitTimerRef.current = setTimeout(() => { |
| | | setVisible(false); |
| | | const resetAnimation = Taro.createAnimation({ |
| | | duration: 0, |
| | | timingFunction: 'linear' |
| | | }); |
| | | resetAnimation.translateY(offsetY).step(); |
| | | setAnimationData(resetAnimation.export()); |
| | | exitTimerRef.current = null; |
| | | }, duration); |
| | | }, [show]); |
| | | |
| | | useEffect(() => () => { |
| | | if (enterTimerRef.current) { |
| | | clearTimeout(enterTimerRef.current); |
| | | } |
| | | if (exitTimerRef.current) { |
| | | clearTimeout(exitTimerRef.current); |
| | | } |
| | | }, []); |
| | | |
| | | return { |
| | | visible, |
| | | animationData, |
| | | shouldRender: show || visible |
| | | }; |
| | | } |
| | |
| | | import Taro from '@tarojs/taro'; |
| | | import { View } from '@tarojs/components'; |
| | | import { useEffect, useState } from 'react'; |
| | | import './css-folder/no-route-popup.less'; |
| | | import useBottomPopupAnimation from './hooks/use-bottom-popup-animation'; |
| | | |
| | | interface NoRoutePopupProps { |
| | | show: boolean; |
| | |
| | | |
| | | export default function NoRoutePopup(props: NoRoutePopupProps) { |
| | | const { show, onClose, onConfirm } = props; |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | | }); |
| | | animation.translateY(0).step(); |
| | | setAnimationData(animation.export()); |
| | | } else if (!show && visible) { |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.translateY(500).step(); |
| | | setAnimationData(animation.export()); |
| | | |
| | | setTimeout(() => { |
| | | setVisible(false); |
| | | }, 300); |
| | | } |
| | | }, [show]); |
| | | const { visible, animationData } = useBottomPopupAnimation(show); |
| | | |
| | | if (!visible) return null; |
| | | |
| | |
| | | import { useEffect, useState } from 'react'; |
| | | import './css-folder/passenger-notice.less' |
| | | import { getPrivacyContent } from '../../../api/home'; |
| | | import useBottomPopupAnimation from './hooks/use-bottom-popup-animation'; |
| | | |
| | | type Props = { |
| | | show?: boolean; |
| | |
| | | } |
| | | |
| | | export default function PassengerNotice({ show = false, onClose, onCheckedChange }: Props) { |
| | | const [visible, setVisible] = useState(false); // 弹窗显示状态 |
| | | const [animationData, setAnimationData] = useState<any>(); // 弹窗动画数据 |
| | | const { visible, animationData } = useBottomPopupAnimation(show); |
| | | const [checked, setChecked] = useState(true); // 近三个月不再显示勾选状态 |
| | | const [noticeContent, setNoticeContent] = useState(''); // 须知富文本内容 |
| | | const [thumbHeight, setThumbHeight] = useState(0); // 自定义滚动条高度 |
| | |
| | | // 文案渲染完成后计算滚动条尺寸 |
| | | useEffect(() => { |
| | | if (noticeContent && visible) { |
| | | // Give it a little time to render |
| | | setTimeout(() => { |
| | | const query = Taro.createSelectorQuery(); |
| | | query.select('.passenger-content-desc').boundingClientRect(); |
| | |
| | | }, 300); |
| | | } |
| | | }, [noticeContent, visible]); |
| | | |
| | | // 控制弹窗开合动画 |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | | }); |
| | | animation.translateY(0).step(); |
| | | setAnimationData(animation.export()); |
| | | } else if (!show && visible) { |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.translateY(500).step(); |
| | | setAnimationData(animation.export()); |
| | | |
| | | setTimeout(() => { |
| | | setVisible(false); |
| | | }, 300); |
| | | } |
| | | }, [show]); |
| | | |
| | | // 每次打开时拉取最新须知内容 |
| | | useEffect(() => { |
| | |
| | | import type { PickupExceptionInfo } from '../pickup-exception'; |
| | | import { getDistance } from '../../../../utils/Robotaxi-public-method'; |
| | | import './css-folder/pickup-exception-popup.less'; |
| | | import useBottomPopupAnimation from './hooks/use-bottom-popup-animation'; |
| | | |
| | | type Props = { |
| | | show?: boolean; |
| | |
| | | onClose, |
| | | onConfirm |
| | | }: Props) { |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | const { animationData, shouldRender } = useBottomPopupAnimation(show, { |
| | | duration: POPUP_ANIMATION_DURATION_MS |
| | | }); |
| | | const [currentUserLocation, setCurrentUserLocation] = useState<LocationPoint | null>(null); |
| | | const [linePoints, setLinePoints] = useState<LocationPoint[]>([]); |
| | | const [distanceToVehicle, setDistanceToVehicle] = useState<number | null>(null); |
| | | const closeTimerRef = useRef<any>(null); |
| | | const locationTimerRef = useRef<any>(null); |
| | | const mapViewportFittedRef = useRef(false); |
| | | const shouldRender = show || visible; |
| | | |
| | | const mapCenter = useMemo(() => { |
| | | const candidatePoints: LocationPoint[] = []; |
| | |
| | | }, [pickupExceptionInfo?.vehicleLat, pickupExceptionInfo?.vehicleLon]); |
| | | |
| | | useEffect(() => { |
| | | if (closeTimerRef.current) { |
| | | clearTimeout(closeTimerRef.current); |
| | | closeTimerRef.current = null; |
| | | } |
| | | |
| | | if (show) { |
| | | setVisible(true); |
| | | setAnimationData(undefined); |
| | | return; |
| | | } |
| | | |
| | | if (!visible) { |
| | | return; |
| | | } |
| | | |
| | | const animation = Taro.createAnimation({ |
| | | duration: POPUP_ANIMATION_DURATION_MS, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.translateY(500).step(); |
| | | setAnimationData(animation.export()); |
| | | closeTimerRef.current = setTimeout(() => { |
| | | setVisible(false); |
| | | setAnimationData(undefined); |
| | | closeTimerRef.current = null; |
| | | }, POPUP_ANIMATION_DURATION_MS); |
| | | |
| | | return () => { |
| | | if (closeTimerRef.current) { |
| | | clearTimeout(closeTimerRef.current); |
| | | closeTimerRef.current = null; |
| | | } |
| | | }; |
| | | }, [show, visible]); |
| | | |
| | | useEffect(() => { |
| | | if (!show) { |
| | | if (locationTimerRef.current) { |
| | | clearInterval(locationTimerRef.current); |
| | |
| | | return ( |
| | | <View className='pickup-exception-popup' catchMove> |
| | | <View className='pickup-exception-mask'></View> |
| | | <View className='pickup-exception-content' animation={show ? undefined : animationData}> |
| | | <View className='pickup-exception-content' animation={animationData}> |
| | | <View className='pickup-exception-close' onClick={onClose}> |
| | | <Image |
| | | className='pickup-exception-close-icon' |
| | |
| | | import Taro from '@tarojs/taro'; |
| | | import { View, Text, Image, ScrollView } from '@tarojs/components'; |
| | | import { useEffect, useState } from 'react'; |
| | | import './index.less'; |
| | | import { queryRoutePreferences } from '../../../../api/begin-route'; |
| | | import useBottomPopupAnimation from '../hooks/use-bottom-popup-animation'; |
| | | |
| | | export type RoutePreferenceOption = { |
| | | label: string; |
| | |
| | | }; |
| | | |
| | | export default function RoutePreferencePopup({ show, value, openCityId = 0, onClose, onSelect }: Props) { |
| | | const [visible, setVisible] = useState(false); |
| | | const [animationData, setAnimationData] = useState<any>(); |
| | | const { animationData, shouldRender } = useBottomPopupAnimation(show); |
| | | const [options, setOptions] = useState<RoutePreferenceOption[]>(DEFAULT_OPTIONS); |
| | | |
| | | useEffect(() => { |
| | | if (show && !visible) { |
| | | setVisible(true); |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-out' |
| | | }); |
| | | animation.translateY(0).step(); |
| | | setAnimationData(animation.export()); |
| | | } else if (!show && visible) { |
| | | const animation = Taro.createAnimation({ |
| | | duration: 300, |
| | | timingFunction: 'ease-in' |
| | | }); |
| | | animation.translateY('100%').step(); |
| | | setAnimationData(animation.export()); |
| | | |
| | | setTimeout(() => { |
| | | setVisible(false); |
| | | }, 300); |
| | | } |
| | | }, [show]); |
| | | |
| | | useEffect(() => { |
| | | if (!show) return; |
| | |
| | | }; |
| | | }, [show, openCityId]); |
| | | |
| | | if (!visible && !show) return null; |
| | | if (!shouldRender) return null; |
| | | |
| | | return ( |
| | | <View className={`route-preference-popup ${show ? 'show' : ''}`} catchMove> |