【问题标题】:Change duration in an infinite animation在无限动画中更改持续时间
【发布时间】:2021-04-23 18:30:55
【问题描述】:

我尝试制作一个无限循环的脉冲动画视图。以下视图实现了该动画。但我想在动画时更新动画持续时间。实际上它不会更新循环动画。向 useEffect 依赖项添加持续时间无法正常工作,因为它会刷新当前动画圆圈。

喜欢这个(Source):

这是我当前的代码:

const LoopPulseView = (props: {duration: number;}) => {
  const nbPulse = 6;
  const [pulses, setPulses] = useState([] as any);
  const [duration, setDuration] = useState(props.duration);
  const [delay] = useState(duration / nbPulse);

  useEffect(() => {
    setDuration(props.duration);
  }, [props.duration]);


  const scaleOut = {
    0: {
      opacity: 1,
      scale: 1,
    },
    1: {
      opacity: 0,
      scale: 1.5,
    },
  };

  useEffect(() => {
    const newPulses = [];
    for (let p = 0; p < nbPulse; p++) {
      const view = (
        <Animatable.View
          key={p}
          duration={duration}
          delay={delay * p}
          style={[styles.pulse, pulseStyle]}
          animation={scaleOut}
          iterationCount={'infinite'}
          useNativeDriver={true}
        />
      );

      newPulses.push(view);
    }

    setPulses(newPulses);
  }, []);

  return (
    <View style={containerStyle}>
      <View style={pulseWrapperStyle}>
        {pulses.map((pulse: any) => {
          return pulse;
        })}
      </View>
    </View>
  );
};


export default LoopPulseView;

【问题讨论】:

  • 如果你添加 'duration' 作为你的 useEffect 的依赖,它会在修改时长时重新启动
  • 您正在从道具访问持续时间,并且您正在将其与状态一起使用。这是不行的。你不需要这一行: const [duration, setDuration] = useState(props.duration);
  • @Kai 是的,但它会重置动画,即使它正在运行,它也不流畅
  • @Arbnor 是的,你说得对
  • @Ludovic 您需要使用transitionTo 命令式方法才能更改持续时间。但是,我认为您会从切换动画持续时间中发现一些不需要的副作用。在这里查看更多docs 你应该尝试使用react-native-reanimated 从头开始​​实现这个动画。你会发现这样你有更多的灵活性/

标签: react-native react-animated react-native-animatable


【解决方案1】:

您不能更改 Animatable.View 的道具,因为它会触发组件的渲染

您不能替换所有 Animatable.View,因为它也会渲染您的组件

解决方案创建一个动画数组,您将在其中逐渐增加动画的数量并替换旧的

我可以举个例子。

import React, { useState, useEffect } from 'react';
import {
  View,
  StyleSheet,
  Button
} from 'react-native';

import * as Animatable from 'react-native-animatable';

const scaleOut = {
  0: {
    opacity: 1,
    scale: 1,
  },
  1: {
    opacity: 0,
    scale: 1.5,
  },
};

function Pulse({ duration }) {

  return (
    <Animatable.View
      duration={duration}
      animation={scaleOut}
      useNativeDriver={true}
    >
      <View
        style={[styles.pulse]}
      >
      </View>
    </Animatable.View>
  )

}

function App() {

  const [pulses, setPulses] = useState([]);
  const [nbPulse, setNbPulse] = useState(3);
  const [duration, setDuration] = useState(3000);

  useEffect(() => {
    setDelay(duration / nbPulse);
  }, [duration, nbPulse])

  const [delay, setDelay] = useState(duration / nbPulse);

  useEffect(() => {
    const interval = setInterval(() => {

      setPulses((prevState) => {

        // create a new pulse
        let pulse = { 
          id: String(Date.now()),
          duration: duration
        }

        let newState = [...prevState];
        // remove the first object when the limite of the length's array is reached
        if (newState.length >= nbPulse) { 
          newState.splice(0, newState.length - nbPulse);

        }
        // increase the size of the array
        newState.push(pulse); 

        return (newState);
      });

    }, delay);

    return () => {
      clearInterval(interval)
    };
  }, [delay]) // create a new intervale when delay is change

  return (

    <View
      style={{
        width: "100%",
        flex: 1
      }}>
      <Button
        onPress={() => { setNbPulse(nbPulse + 1) }}
        title="more pulse"
        color="#841584"
      />

      <Button
        onPress={() => {
          if (nbPulse > 1) {
            setNbPulse(nbPulse - 1)
          }
        }}
        title="less pulse"
        color="#841584"
      />

      <Button
        onPress={() => {
          setDuration(duration + 1000)
        }}
        title="pulse live increase"
        color="#841584"
      />

      <Button
        onPress={() => {
          if (duration > 1000) {
            setDuration(duration - 1000)
          }
        }}
        title="pulse live decrease"
        color="#841584"
      />

      {pulses.map((item) => (
        <Pulse
          key={item.id} // key to avoid rerender
          id={item.id}
          duration={item.duration} />
      ))}

    </View>

  );
}
const styles = StyleSheet.create({
  pulse: {
    backgroundColor: "red",
    position: 'absolute',
    top: 100,
    left: 100,
    height: 200,
    width: 200,
    borderRadius: 200
  }
});

export default App;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多