如何在 React Native 中停止循环动画



我的组件中有一个简单的循环动画,如下所示:

runAnimation() {
    console.log('run animation');
    this.state.angle.setValue(0);
    Animated.timing(this.state.angle, {
        toValue: 360,
        duration: 8000,
        easing: Easing.linear
    }).start(() => this.runAnimation());
}
...
<Animated.Image
    style={[
        styles.rotate,
        { transform: [
            { rotate: this.state.angle.interpolate({
                inputRange: [0, 360],
                outputRange: ['0deg', '360deg']
            })},
        ]}
    ]}
    source={require('./spinning_ball.png')}
/>

我将如何停止此动画?例如,当导航到另一个屏幕或用户单击按钮后。

我尝试使用this.state.angle.stopAnimation((,但注意到运行动画仍在控制台中打印。我应该调用不同的停止方法来防止执行启动回调吗?

基于我在Nguyên Hoàng的回答中的评论。如果您调用this.state.angle.stopAnimation(),这是停止循环动画的另一种方法:

runAnimation() {
  this.state.angle.setValue(0);
  Animated.timing(this.state.angle, {
    toValue: 360,
    duration: 8000,
    easing: Easing.linear
  }).start((o) => {
    if(o.finished) {
      this.runAnimation();
    }
  });
}
我喜欢将

我的动画封装在钩子中,它比类组件更干净、更容易、更可重用。以下是我在打字稿中使用简单的启动/停止控制进行循环动画的方法:

export const useBounceRotateAnimation = (running: boolean = true, rate: number = 300) => {
    //Example of making an infinite looping animation and controlling it with a boolean
    //Note that this assumes the "rate" is constant -- if you wanted to change the rate value after creation the implementation would have to change a bit
    //Only create the animated value once
    const val = useRef(new Animated.Value(0))
    //Store a reference to the animation since we're starting-stopping an infinite loop instead of starting new animations and we aren't changing any animation values after creation. We only want to create the animation object once.
    const anim = useRef(
        Animated.loop(
            Animated.timing(val.current, {
                toValue: 1,
                duration: rate,
                easing: Easing.linear,
                useNativeDriver: true,
                isInteraction: false,
            })
        )
    ).current
    //Interpolate the value(s) to whatever is appropriate for your case
    const interpolatedY = val.current.interpolate({
        inputRange: [0, 0.5, 1],
        outputRange: [0, 6, 0],
    })
    const interpolatedRotate = val.current.interpolate({
        inputRange: [0, 0.25, 0.5, 1],
        outputRange: ["0deg", "-3deg", "3deg", "0deg"],
    })
    //Start and stop the animation based on the value of the boolean prop
    useEffect(() => {
        if (running) {
            anim.start()
        } else {
            //When stopping reset the value to 0 so animated item doesn't stop in a random position
            anim.stop()
            val.current.setValue(0)
        }
        //Return a function from useEffect to stop the animation on unmount
        return () => anim.stop()
     //This useEffect should rerun if "running" or "anim" changes (but anim won't change since its a ref we never modify)
    }, [running, anim])
     //Return the animated values. Use "as const" const assertion to narrow the output type to exactly the two values being returned. 
    return [interpolatedY, interpolatedRotate] as const
}

我使用这个实际的钩子实现使角色图像在我的游戏中"行走"。现在在我的组件中,它非常易于使用:

const MyComponent = () => {
    const [isRunning, setIsRunning] = useState(true)
    const [translation, rotation] = useBounceRotateAnimation(isRunning)
    return (
        <Animated.View
            style={{
                transform: [{ translateY: translation}, { rotate: rotation}],
            }}
        >
             ....rest of your component here, just setIsRunning(false) to stop animation whenever you need
        </Animated.View>
    )
}

如您所见,此模式非常干净且可重用。动画组件不需要知道有关动画的任何信息,它只需使用动画值并告知动画何时运行。

您可以创建一个变量stopAnimation,以便仅在stopAnimation === false然后回调runAnimation函数时随时停止动画。例如:

this.state = { stopAnimation: false }
runAnimation() {
  this.state.spinValue.setValue(0);
  Animated.timing(
    this.state.spinValue,
    {
      toValue: 1,
      duration: 3000,
      easing: Easing.linear
    }
  ).start( () => {
    if(this.state.stopAnimation === false) {
      this.runAnimation();
    }
  });
}

因此,您只需要创建一个按钮,该按钮调用函数this.state = { stopAnimation: true }停止动画。

此处的示例:https://rnplay.org/apps/Lpmh8A。

您可以使用以下命令停止循环:

componentWillUnmount() {
   Animated.timing(this.state.angle).stop();
}

通过调用setValue()方法并分配值来停止循环动画的最简单方法。 如:

const [opacity, setOpacity] = useState(new Animated.value(0));
// declare your looping animated function
// Any time to stop the looping animated function just call setValue() 
// on your stop animation event or function using opcacity reference. like
opacity.setValue(0)

欲了解更多详情 https://reactnative.dev/docs/animatedvaluexy#setvalue

设置全局变量

let dataloaded = false;

停止动画 随时覆盖变量(数据加载(

componentWillUnmount() {
    dataloaded = true;
}
onPress={() => {
    dataloaded = true;
}}

最终代码将如下所示

runAnimation() {
    console.log('run animation');
    this.state.angle.setValue(0);
    Animated.timing(this.state.angle, {
        toValue: 360,
        duration: 8000,
        easing: Easing.linear
    })
    .start(() => {
        if ( ! dataloaded) {
            this.runAnimation();
        }
    })
}

相关内容

  • 没有找到相关文章

最新更新