feat: 登录
This commit is contained in:
parent
e2cd78f6a0
commit
027e72a364
@ -1,291 +1,183 @@
|
|||||||
|
import { Fonts } from '@/constants/Fonts';
|
||||||
import { checkAuthStatus } from '@/lib/auth';
|
import { checkAuthStatus } from '@/lib/auth';
|
||||||
import { useRouter } from 'expo-router';
|
import { useRouter } from 'expo-router';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Animated, Dimensions, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
import { Dimensions, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||||
|
import Animated, {
|
||||||
|
interpolate,
|
||||||
|
useAnimatedStyle,
|
||||||
|
useSharedValue,
|
||||||
|
withDelay,
|
||||||
|
withRepeat,
|
||||||
|
withSequence,
|
||||||
|
withTiming
|
||||||
|
} from 'react-native-reanimated';
|
||||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||||
|
|
||||||
|
// Worklet function for animations
|
||||||
|
const runShakeAnimation = (value: Animated.SharedValue<number>) => {
|
||||||
|
'worklet';
|
||||||
|
return withRepeat(
|
||||||
|
withSequence(
|
||||||
|
withTiming(1, { duration: 300 }),
|
||||||
|
withTiming(-1, { duration: 300 }),
|
||||||
|
withTiming(1, { duration: 300 }),
|
||||||
|
withTiming(-1, { duration: 300 }),
|
||||||
|
withTiming(0, { duration: 200 }),
|
||||||
|
withDelay(1000, withTiming(0, { duration: 0 }))
|
||||||
|
),
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const runWaveAnimation = (value: Animated.SharedValue<number>) => {
|
||||||
|
'worklet';
|
||||||
|
return withRepeat(
|
||||||
|
withSequence(
|
||||||
|
withTiming(1, { duration: 500 }),
|
||||||
|
withTiming(-1, { duration: 500 }),
|
||||||
|
withTiming(0, { duration: 500 }),
|
||||||
|
withDelay(1000, withTiming(0, { duration: 0 }))
|
||||||
|
),
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const insets = useSafeAreaInsets();
|
const insets = useSafeAreaInsets();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = React.useState(false);
|
||||||
|
|
||||||
// 获取屏幕宽度
|
|
||||||
const screenWidth = Dimensions.get('window').width;
|
const screenWidth = Dimensions.get('window').width;
|
||||||
|
|
||||||
// 动画值
|
// Animation values
|
||||||
const fadeAnim = useRef(new Animated.Value(0)).current; // IP图标的淡入动画
|
const fadeAnim = useSharedValue(0);
|
||||||
const shakeAnim = useRef(new Animated.Value(0)).current; // IP图标的摇晃动画
|
const shakeAnim = useSharedValue(0);
|
||||||
const animationRef = useRef<Animated.CompositeAnimation | null>(null); // 动画引用
|
const waveAnim = useSharedValue(0);
|
||||||
const descriptionAnim = useRef(new Animated.Value(0)).current; // 描述文本的淡入动画
|
const buttonShakeAnim = useSharedValue(0);
|
||||||
const buttonAnim = useRef(new Animated.Value(0)).current; // 按钮的淡入动画
|
const fadeInAnim = useSharedValue(0);
|
||||||
const buttonShakeAnim = useRef(new Animated.Value(0)).current; // 按钮的摇晃动画
|
const descriptionAnim = useSharedValue(0);
|
||||||
const buttonLoopAnim = useRef<Animated.CompositeAnimation | null>(null); // 按钮循环动画引用
|
const textAnimations = {
|
||||||
const fadeInAnim = useRef(new Animated.Value(0)).current;
|
line1: useSharedValue(0),
|
||||||
|
line2: useSharedValue(0),
|
||||||
|
line3: useSharedValue(0),
|
||||||
|
subtitle: useSharedValue(0),
|
||||||
|
};
|
||||||
|
|
||||||
// 文本行动画值
|
// Animation styles
|
||||||
const [textAnimations] = useState(() => ({
|
const ipAnimatedStyle = useAnimatedStyle(() => ({
|
||||||
line1: new Animated.Value(0), // 第一行文本动画
|
opacity: fadeAnim.value,
|
||||||
line2: new Animated.Value(0), // 第二行文本动画
|
transform: [
|
||||||
line3: new Animated.Value(0), // 第三行文本动画
|
{ translateX: interpolate(shakeAnim.value, [-1, 1], [-2, 2]) },
|
||||||
subtitle: new Animated.Value(0), // 副标题动画
|
{ rotate: `${interpolate(shakeAnim.value, [-1, 1], [-2, 2])}deg` },
|
||||||
|
],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 添加挥手动画值
|
const waveAnimatedStyle = useAnimatedStyle(() => ({
|
||||||
const waveAnim = useRef(new Animated.Value(0)).current;
|
transform: [
|
||||||
|
{ rotate: `${interpolate(waveAnim.value, [-1, 0, 1], [-15, 0, 15])}deg` },
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
// 启动IP图标摇晃动画
|
const buttonStyle = useAnimatedStyle(() => ({
|
||||||
const startShaking = () => {
|
opacity: fadeInAnim.value,
|
||||||
// 停止任何正在进行的动画
|
transform: [
|
||||||
if (animationRef.current) {
|
{ translateY: interpolate(fadeInAnim.value, [0, 1], [20, 0]) },
|
||||||
animationRef.current.stop();
|
{ translateX: interpolate(buttonShakeAnim.value, [-1, 0, 1], [-5, 0, 5]) }
|
||||||
}
|
]
|
||||||
|
}));
|
||||||
|
|
||||||
// 创建动画序列
|
const welcomeStyle = useAnimatedStyle(() => ({
|
||||||
const sequence = Animated.sequence([
|
opacity: fadeInAnim.value,
|
||||||
// 第一次左右摇晃
|
transform: [{ translateY: interpolate(fadeInAnim.value, [0, 1], [20, 0]) }]
|
||||||
Animated.timing(shakeAnim, {
|
}));
|
||||||
toValue: 1,
|
|
||||||
duration: 300,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(shakeAnim, {
|
|
||||||
toValue: -1,
|
|
||||||
duration: 300,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
// 第二次左右摇晃
|
|
||||||
Animated.timing(shakeAnim, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 300,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(shakeAnim, {
|
|
||||||
toValue: -1,
|
|
||||||
duration: 300,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
// 回到中心位置
|
|
||||||
Animated.timing(shakeAnim, {
|
|
||||||
toValue: 0,
|
|
||||||
duration: 200,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
// 1秒延迟
|
|
||||||
Animated.delay(1000),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 循环播放动画序列
|
const descriptionStyle = useAnimatedStyle(() => ({
|
||||||
animationRef.current = Animated.loop(sequence);
|
opacity: descriptionAnim.value,
|
||||||
animationRef.current.start();
|
transform: [{ translateY: interpolate(descriptionAnim.value, [0, 1], [20, 0]) }]
|
||||||
};
|
}));
|
||||||
|
|
||||||
// 启动文本动画
|
const textLine1Style = useAnimatedStyle(() => ({
|
||||||
const startTextAnimations = () => {
|
opacity: textAnimations.line1.value,
|
||||||
// 按顺序延迟启动每行文本动画
|
transform: [{ translateY: interpolate(textAnimations.line1.value, [0, 1], [10, 0]) }]
|
||||||
return new Promise<void>((resolve) => {
|
}));
|
||||||
Animated.stagger(300, [
|
|
||||||
Animated.timing(textAnimations.line1, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 500,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(textAnimations.line2, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 500,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(textAnimations.line3, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 500,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(textAnimations.subtitle, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 500,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
]).start(() => resolve());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 启动描述文本动画
|
const textLine2Style = useAnimatedStyle(() => ({
|
||||||
const startDescriptionAnimation = () => {
|
opacity: textAnimations.line2.value,
|
||||||
// IP图标显示后淡入描述文本
|
transform: [{ translateY: interpolate(textAnimations.line2.value, [0, 1], [10, 0]) }]
|
||||||
return new Promise<void>((resolve) => {
|
}));
|
||||||
Animated.sequence([
|
|
||||||
Animated.delay(200), // IP图标显示后延迟200ms
|
|
||||||
Animated.timing(descriptionAnim, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 800,
|
|
||||||
useNativeDriver: true,
|
|
||||||
})
|
|
||||||
]).start(() => resolve());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
// 启动欢迎语动画
|
|
||||||
const startWelcomeAnimation = () => {
|
|
||||||
// IP图标显示后淡入描述文本
|
|
||||||
return new Promise<void>((resolve) => {
|
|
||||||
Animated.sequence([
|
|
||||||
Animated.delay(200), // IP图标显示后延迟200ms
|
|
||||||
Animated.timing(fadeInAnim, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 800,
|
|
||||||
useNativeDriver: true,
|
|
||||||
})
|
|
||||||
]).start(() => resolve());
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 启动按钮动画
|
const textLine3Style = useAnimatedStyle(() => ({
|
||||||
const startButtonAnimation = () => {
|
opacity: textAnimations.line3.value,
|
||||||
// 首先淡入按钮
|
transform: [{ translateY: interpolate(textAnimations.line3.value, [0, 1], [10, 0]) }]
|
||||||
Animated.sequence([
|
}));
|
||||||
Animated.timing(buttonAnim, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 800,
|
|
||||||
useNativeDriver: true,
|
|
||||||
})
|
|
||||||
]).start(() => {
|
|
||||||
// 淡入完成后开始循环摇晃动画
|
|
||||||
startButtonShakeLoop();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 启动按钮循环摇晃动画
|
const subtitleStyle = useAnimatedStyle(() => ({
|
||||||
const startButtonShakeLoop = () => {
|
opacity: textAnimations.subtitle.value,
|
||||||
// 停止任何正在进行的动画
|
transform: [{ translateY: interpolate(textAnimations.subtitle.value, [0, 1], [10, 0]) }]
|
||||||
if (buttonLoopAnim.current) {
|
}));
|
||||||
buttonLoopAnim.current.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建摇晃动画序列
|
// Start animations
|
||||||
const shakeSequence = Animated.sequence([
|
|
||||||
// 向右摇晃
|
|
||||||
Animated.timing(buttonShakeAnim, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 100,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
// 向左摇晃
|
|
||||||
Animated.timing(buttonShakeAnim, {
|
|
||||||
toValue: -1,
|
|
||||||
duration: 100,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
// 再次向右摇晃
|
|
||||||
Animated.timing(buttonShakeAnim, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 100,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
// 回到中心位置
|
|
||||||
Animated.timing(buttonShakeAnim, {
|
|
||||||
toValue: 0,
|
|
||||||
duration: 100,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
// 暂停3秒
|
|
||||||
Animated.delay(3000)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 循环播放动画序列
|
|
||||||
buttonLoopAnim.current = Animated.loop(shakeSequence);
|
|
||||||
buttonLoopAnim.current.start();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 启动挥手动画
|
|
||||||
const startWaveAnimation = () => {
|
|
||||||
// 创建循环动画:左右摇摆
|
|
||||||
Animated.loop(
|
|
||||||
Animated.sequence([
|
|
||||||
Animated.timing(waveAnim, {
|
|
||||||
toValue: 1,
|
|
||||||
duration: 500,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(waveAnim, {
|
|
||||||
toValue: -1,
|
|
||||||
duration: 500,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.timing(waveAnim, {
|
|
||||||
toValue: 0,
|
|
||||||
duration: 500,
|
|
||||||
useNativeDriver: true,
|
|
||||||
}),
|
|
||||||
Animated.delay(1000), // 暂停1秒
|
|
||||||
])
|
|
||||||
).start();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 组件挂载时启动动画
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
checkAuthStatus(router, () => {
|
checkAuthStatus(router, () => {
|
||||||
router.replace('/ask')
|
router.replace('/ask');
|
||||||
}, false).then(() => {
|
}, false).then(() => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
});
|
});
|
||||||
// IP图标的淡入动画
|
|
||||||
Animated.timing(fadeAnim, {
|
// Start fade in animation
|
||||||
toValue: 1,
|
fadeAnim.value = withTiming(1, { duration: 1000 }, () => {
|
||||||
duration: 1000,
|
// Start shake animation
|
||||||
useNativeDriver: true,
|
shakeAnim.value = runShakeAnimation(shakeAnim);
|
||||||
}).start(() => {
|
|
||||||
// 淡入完成后开始摇晃动画
|
// Start text animations with delays
|
||||||
startShaking();
|
textAnimations.line1.value = withDelay(0, withTiming(1, { duration: 500 }));
|
||||||
// IP显示后开始文本动画
|
textAnimations.line2.value = withDelay(300, withTiming(1, { duration: 500 }));
|
||||||
startTextAnimations()
|
textAnimations.line3.value = withDelay(600, withTiming(1, { duration: 500 }));
|
||||||
.then(() => startWelcomeAnimation())
|
textAnimations.subtitle.value = withDelay(900, withTiming(1, { duration: 500 }));
|
||||||
.then(() => startDescriptionAnimation())
|
|
||||||
.then(() => startButtonAnimation())
|
// Start welcome animation
|
||||||
.catch(console.error);
|
fadeInAnim.value = withDelay(200, withTiming(1, { duration: 800 }));
|
||||||
// 启动挥手动画
|
|
||||||
startWaveAnimation();
|
// Start description animation
|
||||||
|
descriptionAnim.value = withDelay(200, withTiming(1, { duration: 800 }));
|
||||||
|
|
||||||
|
// Start button animation
|
||||||
|
fadeInAnim.value = withDelay(200, withTiming(1, { duration: 800 }, () => {
|
||||||
|
// Start button shake animation
|
||||||
|
buttonShakeAnim.value = withRepeat(
|
||||||
|
withSequence(
|
||||||
|
withTiming(1, { duration: 100 }),
|
||||||
|
withTiming(-1, { duration: 100 }),
|
||||||
|
withTiming(1, { duration: 100 }),
|
||||||
|
withTiming(0, { duration: 100 }),
|
||||||
|
withDelay(3000, withTiming(0, { duration: 0 }))
|
||||||
|
),
|
||||||
|
-1
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Start wave animation
|
||||||
|
waveAnim.value = runWaveAnimation(waveAnim);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 组件卸载时清理动画
|
// Cleanup
|
||||||
return () => {
|
return () => {
|
||||||
if (buttonLoopAnim.current) {
|
fadeAnim.value = 0;
|
||||||
buttonLoopAnim.current.stop();
|
shakeAnim.value = 0;
|
||||||
}
|
waveAnim.value = 0;
|
||||||
if (animationRef.current) {
|
buttonShakeAnim.value = 0;
|
||||||
animationRef.current.stop();
|
fadeInAnim.value = 0;
|
||||||
}
|
descriptionAnim.value = 0;
|
||||||
|
Object.values(textAnimations).forEach(anim => anim.value = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 动画样式
|
|
||||||
const animatedStyle = {
|
|
||||||
opacity: fadeAnim,
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateX: shakeAnim.interpolate({
|
|
||||||
inputRange: [-1, 1],
|
|
||||||
outputRange: [-2, 2],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rotate: shakeAnim.interpolate({
|
|
||||||
inputRange: [-1, 1],
|
|
||||||
outputRange: ['-2deg', '2deg'],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
// 旋转动画插值
|
|
||||||
const rotate = waveAnim.interpolate({
|
|
||||||
inputRange: [-1, 0, 1],
|
|
||||||
outputRange: ['-15deg', '0deg', '15deg'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<View style={styles.loadingContainer}>
|
<View style={styles.loadingContainer}>
|
||||||
@ -297,86 +189,27 @@ export default function HomeScreen() {
|
|||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<View style={[styles.contentContainer, { paddingTop: insets.top + 16 }]}>
|
<View style={[styles.contentContainer, { paddingTop: insets.top + 16 }]}>
|
||||||
{/* 标题区域 */}
|
|
||||||
<View style={styles.headerContainer}>
|
<View style={styles.headerContainer}>
|
||||||
<Animated.Text
|
<Animated.Text style={[styles.titleText, textLine1Style]}>
|
||||||
style={[
|
|
||||||
styles.titleText,
|
|
||||||
{
|
|
||||||
opacity: textAnimations.line1, transform: [{
|
|
||||||
translateY: textAnimations.line1.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [10, 0]
|
|
||||||
})
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{t('auth.welcomeAwaken.awaken', { ns: 'login' })}
|
{t('auth.welcomeAwaken.awaken', { ns: 'login' })}
|
||||||
</Animated.Text>
|
</Animated.Text>
|
||||||
<Animated.Text
|
<Animated.Text style={[styles.titleText, textLine2Style]}>
|
||||||
style={[
|
|
||||||
styles.titleText,
|
|
||||||
{
|
|
||||||
opacity: textAnimations.line2, transform: [{
|
|
||||||
translateY: textAnimations.line2.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [10, 0]
|
|
||||||
})
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{t('auth.welcomeAwaken.your', { ns: 'login' })}
|
{t('auth.welcomeAwaken.your', { ns: 'login' })}
|
||||||
</Animated.Text>
|
</Animated.Text>
|
||||||
<Animated.Text
|
<Animated.Text style={[styles.titleText, textLine3Style]}>
|
||||||
style={[
|
|
||||||
styles.titleText,
|
|
||||||
{
|
|
||||||
opacity: textAnimations.line3, transform: [{
|
|
||||||
translateY: textAnimations.line3.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [10, 0]
|
|
||||||
})
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{t('auth.welcomeAwaken.pm', { ns: 'login' })}
|
{t('auth.welcomeAwaken.pm', { ns: 'login' })}
|
||||||
</Animated.Text>
|
</Animated.Text>
|
||||||
<Animated.Text
|
<Animated.Text style={[styles.subtitleText, subtitleStyle]}>
|
||||||
style={[
|
|
||||||
styles.subtitleText,
|
|
||||||
{
|
|
||||||
opacity: textAnimations.subtitle,
|
|
||||||
transform: [{
|
|
||||||
translateY: textAnimations.subtitle.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [10, 0]
|
|
||||||
})
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{t('auth.welcomeAwaken.slogan', { ns: 'login' })}
|
{t('auth.welcomeAwaken.slogan', { ns: 'login' })}
|
||||||
</Animated.Text>
|
</Animated.Text>
|
||||||
</View>
|
</View>
|
||||||
{/* 欢迎语 */}
|
|
||||||
<View style={{ alignItems: 'flex-end' }}>
|
<View style={{ alignItems: 'flex-end' }}>
|
||||||
<Animated.View
|
<Animated.View style={[{
|
||||||
style={[{
|
height: screenWidth * 0.3,
|
||||||
height: screenWidth * 0.3,
|
width: screenWidth * 0.3,
|
||||||
width: screenWidth * 0.3,
|
marginTop: -screenWidth * 0.08,
|
||||||
marginTop: -screenWidth * 0.08,
|
}, welcomeStyle]}>
|
||||||
opacity: fadeInAnim,
|
|
||||||
transform: [{
|
|
||||||
translateY: fadeInAnim.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [20, 0]
|
|
||||||
})
|
|
||||||
}]
|
|
||||||
}]}
|
|
||||||
>
|
|
||||||
<Image
|
<Image
|
||||||
source={require('@/assets/images/png/icon/think.png')}
|
source={require('@/assets/images/png/icon/think.png')}
|
||||||
style={{
|
style={{
|
||||||
@ -388,62 +221,25 @@ export default function HomeScreen() {
|
|||||||
</Animated.View>
|
</Animated.View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Animated IP */}
|
|
||||||
<View style={styles.ipContainer}>
|
<View style={styles.ipContainer}>
|
||||||
<Animated.View style={[styles.ipWrapper, { transform: [{ rotate }] }]}>
|
<Animated.View style={[styles.ipWrapper, waveAnimatedStyle]}>
|
||||||
<Image
|
<Image
|
||||||
source={require('@/assets/images/png/icon/ip.png')}
|
source={require('@/assets/images/png/icon/ip.png')}
|
||||||
style={{ width: screenWidth * 0.9, marginBottom: - screenWidth * 0.18, marginTop: -screenWidth * 0.22 }}
|
style={{ width: screenWidth * 0.9, marginBottom: -screenWidth * 0.18, marginTop: -screenWidth * 0.22 }}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* 介绍文本 */}
|
<Animated.Text style={[styles.descriptionText, descriptionStyle]}>
|
||||||
<Animated.Text
|
|
||||||
style={[
|
|
||||||
styles.descriptionText,
|
|
||||||
{
|
|
||||||
opacity: descriptionAnim,
|
|
||||||
transform: [{
|
|
||||||
translateY: descriptionAnim.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [20, 0]
|
|
||||||
})
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
{t('auth.welcomeAwaken.gallery', { ns: 'login' })}
|
{t('auth.welcomeAwaken.gallery', { ns: 'login' })}
|
||||||
{"\n"}
|
{"\n"}
|
||||||
{t('auth.welcomeAwaken.back', { ns: 'login' })}
|
{t('auth.welcomeAwaken.back', { ns: 'login' })}
|
||||||
</Animated.Text>
|
</Animated.Text>
|
||||||
|
|
||||||
{/* 唤醒按钮 */}
|
<Animated.View style={[{ alignItems: "center" }, buttonStyle]}>
|
||||||
<Animated.View
|
|
||||||
style={{
|
|
||||||
alignItems: "center",
|
|
||||||
opacity: buttonAnim,
|
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateY: buttonAnim.interpolate({
|
|
||||||
inputRange: [0, 1],
|
|
||||||
outputRange: [20, 0]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translateX: buttonShakeAnim.interpolate({
|
|
||||||
inputRange: [-1, 0, 1],
|
|
||||||
outputRange: [-5, 0, 5]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.awakenButton}
|
style={styles.awakenButton}
|
||||||
onPress={async () => {
|
onPress={() => router.push('/login')}
|
||||||
router.push('/login');
|
|
||||||
}}
|
|
||||||
activeOpacity={0.8}
|
activeOpacity={0.8}
|
||||||
>
|
>
|
||||||
<Text style={styles.buttonText}>
|
<Text style={styles.buttonText}>
|
||||||
@ -485,17 +281,19 @@ const styles = StyleSheet.create({
|
|||||||
},
|
},
|
||||||
titleText: {
|
titleText: {
|
||||||
color: '#FFFFFF',
|
color: '#FFFFFF',
|
||||||
fontSize: 30,
|
fontSize: 32,
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
marginBottom: 12,
|
marginBottom: 12,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
lineHeight: 36,
|
lineHeight: 36,
|
||||||
|
fontFamily: Fonts['quicksand']
|
||||||
},
|
},
|
||||||
subtitleText: {
|
subtitleText: {
|
||||||
color: 'rgba(255, 255, 255, 0.85)',
|
color: 'rgba(255, 255, 255, 0.85)',
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
lineHeight: 24,
|
lineHeight: 24,
|
||||||
|
fontFamily: Fonts['inter']
|
||||||
},
|
},
|
||||||
ipContainer: {
|
ipContainer: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
@ -514,11 +312,12 @@ const styles = StyleSheet.create({
|
|||||||
opacity: 0.9,
|
opacity: 0.9,
|
||||||
paddingHorizontal: 40,
|
paddingHorizontal: 40,
|
||||||
marginTop: -16,
|
marginTop: -16,
|
||||||
|
fontFamily: Fonts['inter']
|
||||||
},
|
},
|
||||||
awakenButton: {
|
awakenButton: {
|
||||||
backgroundColor: '#FFFFFF',
|
backgroundColor: '#FFFFFF',
|
||||||
borderRadius: 28,
|
borderRadius: 28,
|
||||||
paddingVertical: 16,
|
paddingVertical: 20,
|
||||||
shadowColor: '#000',
|
shadowColor: '#000',
|
||||||
shadowOffset: { width: 0, height: 2 },
|
shadowOffset: { width: 0, height: 2 },
|
||||||
shadowOpacity: 0.1,
|
shadowOpacity: 0.1,
|
||||||
@ -532,5 +331,6 @@ const styles = StyleSheet.create({
|
|||||||
color: '#4C320C',
|
color: '#4C320C',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
|
fontFamily: Fonts['quicksand']
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -83,7 +83,7 @@ const LoginScreen = () => {
|
|||||||
className="absolute left-1/2 z-10"
|
className="absolute left-1/2 z-10"
|
||||||
style={{
|
style={{
|
||||||
top: containerHeight > 0 ? windowHeight - containerHeight - 210 + statusBarHeight - insets.top - 28 : 0,
|
top: containerHeight > 0 ? windowHeight - containerHeight - 210 + statusBarHeight - insets.top - 28 : 0,
|
||||||
transform: [{ translateX: -200 }, { translateY: keyboardOffset > 0 ? -keyboardOffset + statusBarHeight - insets.top - 28 : -keyboardOffset }]
|
transform: [{ translateX: -200 }, { translateY: keyboardOffset > 0 ? -keyboardOffset + statusBarHeight : -keyboardOffset }]
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
@ -98,7 +98,7 @@ const LoginScreen = () => {
|
|||||||
className="absolute left-1/2 z-[1000] -translate-x-[39.5px] -translate-y-[4px]"
|
className="absolute left-1/2 z-[1000] -translate-x-[39.5px] -translate-y-[4px]"
|
||||||
style={{
|
style={{
|
||||||
top: containerHeight > 0 ? windowHeight - containerHeight - 1 + statusBarHeight - insets.top - 30 : 0,
|
top: containerHeight > 0 ? windowHeight - containerHeight - 1 + statusBarHeight - insets.top - 30 : 0,
|
||||||
transform: [{ translateX: -39.5 }, { translateY: keyboardOffset > 0 ? -4 - keyboardOffset + statusBarHeight - insets.top - 30 : -4 - keyboardOffset }]
|
transform: [{ translateX: -39.5 }, { translateY: keyboardOffset > 0 ? -4 - keyboardOffset + statusBarHeight : -4 - keyboardOffset }]
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Handers />
|
<Handers />
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 22 KiB |
Loading…
x
Reference in New Issue
Block a user