React Native 在组件销毁前播放 Animated.Value 动画

1,575 阅读1分钟

React Native 提供了强大的动画系统供开发者使用,如果想用 Animated.Value 驱动 Modal 弹窗在弹出和收起时播放滑入滑出动画,直觉上可以这样写:

const AddModal: React.FC<AddModalProps> = ({ visible, onRequestClose }) => {
  const { window } = useDimensions()
  const slideAnim = useRef(new Animated.Value(0)).current
  useEffect(() => {
    Animated.timing(slideAnim, {
      toValue: visible ? 0 : window.height,
      duration: 150,
      useNativeDriver: false
    }).start()
  }, [visible])
  return (
    <Modal visible={visible} onRequestClose={onRequestClose} animationType={'none'}>
      <Animated.View style={{ transform: [{ translateY: slideAnim }] }}>
        <Text>添加</Text>
        <TextInput />
        <Button title={'添加'} onPress={() => onRequestClose?.()} />
      </Animated.View>
    </Modal>
  )
}

但是使用这种方式实现的弹出和关闭动画,但是动画只在弹出时有触发,而在弹窗关闭时 visiblefalse 会让 Modal 直接消失而来不及播放动画,我们预期的操作流程应该为:

  • visible 变为 true
  • Animated.View 出现
  • 播放 slideAnim 动画移入
  • 用户操作,触发 onRequestClose
  • visible 变为 false
  • 播放 slideAnim 动画移出
  • Animated.View 消失

可以看到这里 Animated.View 的可见性并不是随 visible 瞬间变化的,我们可以另外添加一个变量 _visible 用于控制实际的可见性:visible 变为 true 时先将 _visible 变为 true 再执行动画,visible 变为 false 时先播放动画再将 _visible 变为 false

const AddModal: React.FC<AddModalProps> = ({ visible, onRequestClose }) => {
  const { window } = useDimensions()
  const slideAnim = useRef(new Animated.Value(0)).current
  const [_visible, _setVisible] = useState(false)
  useEffect(() => {
    visible && _setVisible(true)
    Animated.timing(slideAnim, {
      toValue: visible ? 0 : window.height,
      duration: 150,
      useNativeDriver: false
    }).start(() => visible || _setVisible(false))
  }, [visible])
  return (
    <Modal visible={_visible} onRequestClose={onRequestClose} animationType={'none'}>
      <Animated.View style={{ transform: [{ translateY: slideAnim }] }}>
        <Text>添加</Text>
        <TextInput />
        <Button title={'添加'} onPress={() => onRequestClose?.()} />
      </Animated.View>
    </Modal>
  )
}