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>
)
}
但是使用这种方式实现的弹出和关闭动画,但是动画只在弹出时有触发,而在弹窗关闭时 visible 为 false 会让 Modal 直接消失而来不及播放动画,我们预期的操作流程应该为:
visible变为trueAnimated.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>
)
}