Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | /**
* CreatePostFAB - 创建帖子浮动按钮
*
* 底部右侧的悬浮创建按钮
* 设计风格:渐变背景,阴影效果,弹性动画
*/
import React, { memo } from 'react';
import { Pressable, StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
withSequence,
} from 'react-native-reanimated';
import { LinearGradient } from 'expo-linear-gradient';
import { Plus } from '@tamagui/lucide-icons';
import { styled, Stack } from 'tamagui';
import { useThemeColors } from '@/src/hooks/useThemeColors';
export interface CreatePostFABProps {
onPress: () => void;
}
const FABContainer = styled(Stack, {
name: 'FABContainer',
width: 60,
height: 60,
borderRadius: 30,
overflow: 'hidden',
shadowColor: 'rgba(254, 190, 152, 0.4)',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 1,
shadowRadius: 16,
});
const AnimatedPressable = Animated.createAnimatedComponent(Pressable);
function CreatePostFABComponent({ onPress }: CreatePostFABProps) {
const colors = useThemeColors();
const scale = useSharedValue(1);
const rotation = useSharedValue(0);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }, { rotate: `${rotation.value}deg` }],
}));
const handlePressIn = () => {
scale.value = withSpring(0.88, { damping: 15, stiffness: 400 });
rotation.value = withSpring(-15, { damping: 15, stiffness: 400 });
};
const handlePressOut = () => {
scale.value = withSpring(1, { damping: 10, stiffness: 300 });
rotation.value = withSpring(0, { damping: 10, stiffness: 300 });
};
const handlePress = () => {
// 按下时的弹跳效果
scale.value = withSequence(
withSpring(1.1, { damping: 10, stiffness: 400 }),
withSpring(1, { damping: 12, stiffness: 300 })
);
rotation.value = withSequence(
withSpring(90, { damping: 15, stiffness: 400 }),
withSpring(0, { damping: 10, stiffness: 300 })
);
onPress();
};
return (
<AnimatedPressable
testID="create-post-fab"
onPressIn={handlePressIn}
onPressOut={handlePressOut}
onPress={handlePress}
style={[styles.pressable, animatedStyle]}
>
<FABContainer shadowColor={colors.primary as any}>
<LinearGradient
colors={[colors.primaryLight, colors.primary, colors.primaryDark]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.gradient}
>
<Plus size={28} color="#FFFFFF" strokeWidth={2.5} />
</LinearGradient>
</FABContainer>
</AnimatedPressable>
);
}
const styles = StyleSheet.create({
pressable: {
position: 'absolute',
bottom: 32,
right: 20,
},
gradient: {
width: 60,
height: 60,
borderRadius: 30,
alignItems: 'center',
justifyContent: 'center',
},
});
export const CreatePostFAB = memo(CreatePostFABComponent);
|