import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { LinearGradient } from 'expo-linear-gradient'; import { useRouter } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import { Animated, Easing, FlatList, Image, ImageSourcePropType, NativeScrollEvent, NativeSyntheticEvent, Pressable, StyleSheet, Text, TouchableOpacity, View, useWindowDimensions, } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { palette } from '@/constants/Colors'; import { ROUTES } from '@/constants/Routes'; import { STORAGE_KEYS } from '@/services/api'; import AsyncStorage from '@/utils/kvStore'; const ONBOARDING_COMPLETED_KEY = STORAGE_KEYS.onboardingCompleted; type OnboardingSlide = { key: string; title: string; description: string; image: ImageSourcePropType; }; const SLIDES: OnboardingSlide[] = [ { key: 'statistics', title: '全方位健康数据追踪', description: '实时监测步数、心率、睡眠等多维度健康指标,助你全面了解身体状况。', image: require('@/assets/images/onboarding/statistic.png'), }, { key: 'insights', title: '科学轻断食计划', description: '个性化断食方案,智能提醒与进度追踪,助你改善代谢,科学控脂。', image: require('@/assets/images/onboarding/fasting.jpg'), }, { key: 'support', title: '健康挑战赛', description: '参与精选健康挑战,与好友一起打卡,保持每日运动动力。', image: require('@/assets/images/onboarding/challange.jpg'), }, ]; export default function OnboardingScreen() { const router = useRouter(); const { width } = useWindowDimensions(); const [currentIndex, setCurrentIndex] = useState(0); const listRef = useRef>(null); const indicatorAnim = useRef(SLIDES.map((_, index) => new Animated.Value(index === 0 ? 1 : 0))).current; const glassAvailable = isLiquidGlassAvailable(); useEffect(() => { indicatorAnim.forEach((anim, index) => { Animated.timing(anim, { toValue: index === currentIndex ? 1 : 0, duration: 250, easing: Easing.out(Easing.quad), useNativeDriver: false, }).start(); }); }, [currentIndex, indicatorAnim]); const updateIndexFromScroll = useCallback( (event: NativeSyntheticEvent) => { const offsetX = event.nativeEvent.contentOffset.x; const nextIndex = Math.round(offsetX / width); if (!Number.isNaN(nextIndex) && nextIndex !== currentIndex) { setCurrentIndex(nextIndex); } }, [currentIndex, width], ); const completeOnboarding = useCallback(async () => { await AsyncStorage.setItem(ONBOARDING_COMPLETED_KEY, 'true'); router.replace(ROUTES.TAB_STATISTICS); }, [router]); const handleSkip = useCallback(() => { completeOnboarding(); }, [completeOnboarding]); const handleNext = useCallback(() => { if (currentIndex < SLIDES.length - 1) { const nextIndex = currentIndex + 1; listRef.current?.scrollToOffset({ offset: nextIndex * width, animated: true }); setCurrentIndex(nextIndex); return; } completeOnboarding(); }, [completeOnboarding, currentIndex, width]); const renderSlide = useCallback( ({ item }: { item: OnboardingSlide }) => ( ), [width], ); const currentSlide = SLIDES[currentIndex]; return ( 跳过 item.key} pagingEnabled decelerationRate="fast" bounces={false} showsHorizontalScrollIndicator={false} renderItem={renderSlide} onMomentumScrollEnd={updateIndexFromScroll} /> {SLIDES.map((slide, index) => { const animatedStyle = { width: indicatorAnim[index].interpolate({ inputRange: [0, 1], outputRange: [8, 24], }), backgroundColor: indicatorAnim[index].interpolate({ inputRange: [0, 1], outputRange: ['#D8D8D8', '#0066FF'], }), }; return ; })} {currentSlide.title} {currentSlide.description} {glassAvailable ? ( {currentIndex === SLIDES.length - 1 ? '开始使用' : '下一步'} ) : ( {currentIndex === SLIDES.length - 1 ? '开始使用' : '下一步'} )} ); } const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: '#FFFFFF', }, header: { alignItems: 'flex-end', paddingHorizontal: 24, paddingTop: 12, }, skipText: { fontSize: 16, color: '#666C7A', fontWeight: '500', }, carouselContainer: { flex: 1, marginTop: 20, alignItems: 'center', justifyContent: 'center', }, slide: { height: 'auto', justifyContent: 'center', alignItems: 'center', }, slideImage: { width: '85%', height: '100%', resizeMode: 'cover', }, body: { paddingHorizontal: 24, paddingBottom: 24, }, indicatorContainer: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', marginVertical: 24, gap: 10, }, indicatorDot: { height: 8, borderRadius: 4, }, textContainer: { alignItems: 'center', marginBottom: 32, }, title: { fontSize: 24, color: '#222532', fontWeight: '700', textAlign: 'center', marginBottom: 12, }, description: { fontSize: 16, color: '#5C6373', textAlign: 'center', lineHeight: 22, paddingHorizontal: 8, }, primaryButtonWrapper: { marginTop: 16, }, primaryButtonGlass: { borderRadius: 24, paddingVertical: 16, alignItems: 'center', justifyContent: 'center', overflow: 'hidden', shadowColor: palette.purple[600], shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.35, shadowRadius: 16, elevation: 6, }, primaryButtonFallback: { borderRadius: 24, }, primaryButtonGradient: { ...StyleSheet.absoluteFillObject, }, primaryButtonText: { color: '#FFFFFF', fontSize: 18, fontWeight: '600', }, });