React Native Reanimated:点击后重置动画至初始值的实现方案
问题:React Native Reanimated入场动画组件的重置方案
我基于react-native-reanimated写了个可复用的入场动画组件EntryAnimation,运行起来没问题,但不知道怎么在点击按钮后快速把动画恢复到初始状态。一开始想过用useSharedValue存所有动画前后值,但要存的参数太多,肯定有更简单的方法。试过用三元表达式切换组件属性来重置,结果报错了,求可行的解决办法。
原EntryAnimation组件代码
import React, { useEffect } from 'react'; import Animated, { useAnimatedStyle, useSharedValue, useDerivedValue, interpolate, withDelay, withTiming, withSpring, Easing, } from 'react-native-reanimated'; export const EntryAnimation = ({ children, index, rotateV, scaleV, offsetXV, offsetYX, }) => { const play = useSharedValue(play); const progress = useDerivedValue(() => { return play.value ? withDelay(50 * (index ?? 0), withSpring(1, { duration: 350 })) : 0; }); useEffect(() => { play.value = play; }, []); const animatedStyle = useAnimatedStyle(() => { const translateY = interpolate(progress.value, [0, 1], [0, offsetYX]); const translateX = interpolate(progress.value, [0, 1], [0, offsetXV]); const rotate = interpolate(progress.value, [0, 1], [0, rotateV]); const scale = interpolate(progress.value, [0, 1], [1, scaleV]); return { transform: [{ translateY }, { translateX }, { rotate }, { scale }], }; }); return <Animated.View style={animatedStyle}>{children}</Animated.View>; };
组件使用示例
<EntryAnimation index={1} rotateV={0} scaleV={0.8} offsetXV={0} offsetYX={-270}> <Animated.Image source={{ uri: item.poster }} style={[styles.posterImage, { zIndex: 6 }]} /> </EntryAnimation>
尝试的报错代码
{animStarted ? ( <EntryAnimation index={1} rotateV={0} scaleV={0.8} offsetXV={0} offsetYX={-270} > ) : ( <EntryAnimation index={1} rotateV={0} scaleV={1} offsetXV={0} offsetYX={0} > )}
解决方案
1. 修复组件内部逻辑错误
你的组件里有个明显bug:const play = useSharedValue(play);用未定义的props初始化共享值,useEffect里play.value = play;也是错误逻辑。先调整组件,新增控制动画状态的props来实现播放/重置。
2. 改造EntryAnimation组件
给组件添加isActive props,通过它控制动画的正向播放和反向重置,不需要存多组状态值:
import React, { useEffect } from 'react'; import Animated, { useAnimatedStyle, useSharedValue, useDerivedValue, interpolate, withDelay, withSpring, } from 'react-native-reanimated'; export const EntryAnimation = ({ children, index, rotateV, scaleV, offsetXV, offsetYX, isActive = true, // 新增:控制动画激活/重置的开关 }) => { const play = useSharedValue(0); // 初始值设为0,对应动画初始状态 const progress = useDerivedValue(() => { if (isActive) { // 播放入场动画:按index延迟后弹到目标状态 return withDelay(50 * (index ?? 0), withSpring(1, { duration: 350 })); } else { // 重置动画:平滑弹回初始状态 return withSpring(0, { duration: 350 }); } }); // 监听isActive变化,触发动画切换 useEffect(() => { play.value = isActive ? 1 : 0; }, [isActive]); const animatedStyle = useAnimatedStyle(() => { // 插值逻辑保持不变:progress从0到1对应从初始状态到目标状态 const translateY = interpolate(progress.value, [0, 1], [0, offsetYX]); const translateX = interpolate(progress.value, [0, 1], [0, offsetXV]); const rotate = interpolate(progress.value, [0, 1], [0, rotateV]); const scale = interpolate(progress.value, [0, 1], [1, scaleV]); return { transform: [{ translateY }, { translateX }, { rotate }, { scale }], }; }); return <Animated.View style={animatedStyle}>{children}</Animated.View>; };
3. 父组件中控制动画重置
父组件只需维护一个状态,点击按钮时切换状态即可触发动画重置,无需重复渲染不同属性的组件:
import { useState } from 'react'; import { Button } from 'react-native'; // 父组件内 const [animStarted, setAnimStarted] = useState(true); // 点击按钮切换动画状态 const handleReset = () => { setAnimStarted(prev => !prev); }; // 使用改造后的组件 <EntryAnimation index={1} rotateV={0} scaleV={0.8} offsetXV={0} offsetYX={-270} isActive={animStarted} // 传入控制开关 > <Animated.Image source={{ uri: item.poster }} style={[styles.posterImage, { zIndex: 6 }]} /> </EntryAnimation> <Button title="重置动画" onPress={handleReset} />
之前报错的原因
你写的三元表达式里,两个<EntryAnimation>都没有闭合标签(缺少</EntryAnimation>),属于语法错误。即使补全标签,重复渲染组件会导致不必要的重新挂载,远不如通过共享值控制动画状态高效。
内容的提问来源于stack exchange,提问作者JulesUK
相关产品推荐
相关产品推荐

