有条件的动画组件是 janky

Conditionally animated component is janky

我正在尝试为我的应用程序创建一个 InputAccessoryView。它有一个 TextInput 和一个 Pressable 按钮。

我想做的是,当 TextInput 为空值时隐藏按钮,否则使用反应本机动画显示按钮。

我想出了按预期工作的工作组件。对于我使用的动画 useNativeDriver: true.

但是,有一个问题:

  1. 当文本为空并且我输入一个词然后暂停时,显示的动画就会流畅地播放。 (如下图)

  1. 但是当文本为空并且我正在 连续输入 时,显示的动画会以一种卡顿的方式播放。 (如下图)

这是代码,还有 link 到 expo snack:

export default function App() {
  const [text, setText] = React.useState('');
  const translateY = React.useRef(new Animated.Value(0)).current;

  React.useEffect(() => {
    if (text.length > 0) {
      // animate the add button to show by coming up
      _animateShowAddButton();
    } else {
      // animate the add button to hide by going down
      _animateHideAddButton();
    }
    // return null
  }, [text]);

  const _animateShowAddButton = () => {
    Animated.timing(translateY, {
      toValue: 0,
      duration: 300,
      useNativeDriver: true,
    }).start();
  };

  const _animateHideAddButton = () => {
    Animated.timing(translateY, {
      toValue: 100,
      duration: 300,
      useNativeDriver: true,
    }).start();
  };

  const onPressFunction = () => {
    console.log('onPressFunction');
  };

  return (
    <View style={styles.container}>
      <Text style={styles.paragraph}>Some text view</Text>
      <View style={styles.myInputAccessory}>
        <TextInput
          placeholder="Enter text"
          style={styles.textInputStyle}
          onChangeText={(text) => setText(text)}
        />
        <Animated.View
          style={[
            {
              transform: [
                {
                  translateY: translateY,
                },
              ],
            },
          ]}>
          <Pressable style={styles.buttonStyle} onPress={onPressFunction}>
            <Ionicons name="send" size={24} color="white" />
          </Pressable>
        </Animated.View>
      </View>
    </View>
  );
}

每当文本更改时,动画似乎暂停(并再次播放)。你能帮我当文本不为空时如何使按钮动画流畅显示吗?

问题在于,由于第二个参数 [text]useEffect 会在每次文本更改时触发。因此,动画会在每次文本更改时开始。

您可以通过使用一些状态“锁定”动画来防止这种情况。以下是您的组件的相关更改:

function App() {
  const [isAnimating, setIsAnimating] = useState(false);
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    // only start a new animation if we're not currently animating
    if (!isAnimating) {
      // only animate show if currently invisible
      if (!isVisible && text.length > 0) {
        _animateShowAddButton();
      // only animate hide if currently visible
      } else if (isVisible && text.length === 0) {
        _animateHideAddButton();
      }
    }
  // check if we need to animate whenever text changes
  // also check whenever animation finishes
  }, [text, isAnimating]);

  const _animateShowAddButton = () => {
    // secure the lock so another animation can't start
    setIsAnimating(true);
    setIsVisible(true);
    Animated.timing(translateY, {
      toValue: 0,
      duration: 300,
      useNativeDriver: true,
    // release the lock on completion
    }).start(() => setIsAnimating(false));
  };

  const _animateHideAddButton = () => {
    // secure the lock so another animation can't start
    setIsAnimating(true);
    setIsVisible(false);
    Animated.timing(translateY, {
      toValue: 100,
      duration: 300,
      useNativeDriver: true,
    // release the lock on completion
    }).start(() => setIsAnimating(false));
  };
}