更改无限动画的持续时间

Change duration in an infinite animation

我尝试制作一个无限循环的脉冲动画视图。以下视图实现了该动画。但我想在动画时更新动画持续时间。实际上它不会更新循环动画。向 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;

您不能更改 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;