import React, { useEffect, useRef, useState } from 'react'; import { Animated, Dimensions, Easing, StyleSheet, View } from 'react-native'; const { width, height } = Dimensions.get('window'); // 粒子类型定义 interface Particle { id: number; position: { x: number; y: number }; animation: Animated.CompositeAnimation; color: string; size: number; translateX: Animated.Value; translateY: Animated.Value; opacity: Animated.Value; scale: Animated.Value; rotation: Animated.Value; } // 烟花组件属性 interface FireworksProps { autoPlay?: boolean; loop?: boolean; interval?: number; particleCount?: number; colors?: string[]; } export const Fireworks: React.FC = ({ autoPlay = true, loop = true, interval = 2000, particleCount = 80, colors = [ '#FF5252', '#FF4081', '#E040FB', '#7C4DFF', '#536DFE', '#448AFF', '#40C4FF', '#18FFFF', '#64FFDA', '#69F0AE', '#B2FF59', '#EEFF41', '#FFD740', '#FFAB40', '#FF6E40' ] }) => { const [particles, setParticles] = useState([]); const [isPlaying, setIsPlaying] = useState(autoPlay); const particleId = useRef(0); const timerRef = useRef | null>(null); // 生成随机位置 const getRandomPosition = () => { const x = 50 + Math.random() * (width - 100); const y = 100 + Math.random() * (height / 2); return { x, y }; }; // 创建烟花粒子 const createParticles = (position?: { x: number; y: number }) => { const pos = position || getRandomPosition(); const newParticles: Particle[] = []; for (let i = 0; i < particleCount; i++) { const id = particleId.current++; const angle = Math.random() * Math.PI * 2; const speed = 1 + Math.random() * 3; const size = 3 + Math.random() * 7; // 动画值 const translateX = new Animated.Value(0); const translateY = new Animated.Value(0); const opacity = new Animated.Value(1); const scale = new Animated.Value(0.1); const rotation = new Animated.Value(Math.random() * 360); // 粒子动画 const moveAnimation = Animated.parallel([ // X轴移动 Animated.timing(translateX, { toValue: Math.cos(angle) * speed * 100, duration: 1500 + Math.random() * 1000, easing: Easing.out(Easing.quad), useNativeDriver: true, }), // Y轴移动(添加重力效果) Animated.timing(translateY, { toValue: Math.sin(angle) * speed * 100 + 50, // 向下弯曲 duration: 1500 + Math.random() * 1000, easing: Easing.quad, useNativeDriver: true, }), // 淡出 Animated.timing(opacity, { toValue: 0, duration: 1500 + Math.random() * 500, easing: Easing.ease, useNativeDriver: true, }), // 缩放效果 Animated.sequence([ Animated.timing(scale, { toValue: 1.8, duration: 200, useNativeDriver: true, }), Animated.timing(scale, { toValue: 1, duration: 300, useNativeDriver: true, }) ]), // 旋转效果 Animated.timing(rotation, { toValue: (rotation as Animated.Value & { _value: number })._value + 360, duration: 2000, easing: Easing.linear, useNativeDriver: true, }) ]); // 创建粒子对象 newParticles.push({ id, position: pos, animation: moveAnimation, color: colors[Math.floor(Math.random() * colors.length)], size, translateX, translateY, opacity, scale, rotation }); } // 添加新粒子 setParticles(prev => [...prev, ...newParticles]); // 启动动画并在结束后移除粒子 newParticles.forEach(particle => { particle.animation.start(() => { setParticles(prev => prev.filter(p => p.id !== particle.id)); }); }); }; // 开始烟花效果 const startFireworks = () => { if (!isPlaying) return; createParticles(); if (loop) { timerRef.current = setTimeout(() => { startFireworks(); }, interval); } }; // 停止烟花效果 const stopFireworks = () => { if (timerRef.current) { clearTimeout(timerRef.current); timerRef.current = null; } }; // 切换播放状态 const togglePlay = () => { setIsPlaying(prev => { const newState = !prev; if (newState) { startFireworks(); } else { stopFireworks(); } return newState; }); }; // 初始化 useEffect(() => { if (autoPlay) { startFireworks(); } return () => { stopFireworks(); }; }, [autoPlay, loop, interval]); return ( {/* 渲染所有粒子 */} {particles.map((particle) => ( ))} {/* 控制面板 */} {/* 烟花特效 {isPlaying ? '暂停动画' : '播放动画'} */} ); }; const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#FFB645', justifyContent: 'flex-end', alignItems: 'center', zIndex: 9999, }, particle: { position: 'absolute', }, controlPanel: { backgroundColor: 'rgba(0, 0, 0, 0.6)', padding: 20, borderRadius: 20, marginBottom: 40, alignItems: 'center', width: '90%', borderWidth: 1, borderColor: '#7C4DFF', }, title: { color: '#FFFFFF', fontSize: 28, fontWeight: 'bold', marginBottom: 20, textShadowColor: 'rgba(255, 255, 255, 0.75)', textShadowOffset: { width: 0, height: 0 }, textShadowRadius: 10, }, button: { paddingVertical: 12, paddingHorizontal: 30, borderRadius: 30, marginVertical: 8, width: '100%', alignItems: 'center', }, playButton: { backgroundColor: '#4CAF50', }, pauseButton: { backgroundColor: '#FF5252', }, buttonText: { color: 'white', fontSize: 18, fontWeight: 'bold', }, });