React Native中useState导致PanResponder滑动卡片动画卡顿问题求助
React Native滑动卡片动画卡顿问题解决
问题根源
你在onPanResponderRelease中调用setPointerEventDisabled(true)会触发组件同步重渲染,而此时Animated.timing动画正要启动,重渲染会抢占JS线程资源,打断动画的初始帧同步,导致出现短暂停滞卡顿。useState的状态更新必然触发组件重渲染,这是卡顿的核心原因。
解决方案
用useRef代替useState管理pointerEventDisabled状态——useRef的更新不会触发组件重渲染,既能在动画启动前禁用指针事件,又不会干扰动画执行。
修改后的完整代码
import React, { useRef, useState } from "react"; import { Animated, Dimensions, PanResponder, View } from "react-native"; import UserOverview from "./UserOverview"; const SCREEN_WIDTH = Dimensions.get("window").width; const CARD_OUT = SCREEN_WIDTH * 1.4; const FLING_OUT_X = SCREEN_WIDTH / 2.5; export default CardSwiper = ({ cards }) => { const swipe = useRef(new Animated.Value(0)).current; const [visibleCardsState, setVisibleCardsState] = useState(cards || []); // 用useRef代替useState,避免重渲染 const pointerEventDisabled = useRef(false); const panResponder = PanResponder.create({ onMoveShouldSetPanResponder: (evt, gestureState) => { return Math.abs(gestureState.dx) > Math.abs(gestureState.dy); }, onPanResponderMove: (e, { dx }) => { swipe.setValue(dx); }, onPanResponderRelease: (e, { dx, vx }) => { const direction = Math.sign(dx); const userAction = Math.abs(dx) > FLING_OUT_X; if (userAction) { // 直接修改ref的current值,不会触发重渲染 pointerEventDisabled.current = true; Animated.timing(swipe, { toValue: direction * CARD_OUT, useNativeDriver: true, }).start(() => { setVisibleCardsState((prevState) => [...prevState].slice(1)); swipe.setValue(0); pointerEventDisabled.current = false; }); } else { Animated.spring(swipe, { friction: 5, toValue: 0, useNativeDriver: true, }).start(); } }, }); const rotate = swipe.interpolate({ inputRange: [-SCREEN_WIDTH / 2, 0, SCREEN_WIDTH / 2], outputRange: ["10deg", "0deg", "-10deg"], }); const animatedCardStyle = { transform: [{ translateX: swipe }, { rotate: rotate }], }; return ( visibleCardsState.length > 0 && visibleCardsState .slice(0, 5) .map((card, idx) => { const isTopCard = idx === 0; const panHandlers = isTopCard ? panResponder.panHandlers : {}; return ( <Animated.View // 读取ref的current值绑定pointerEvents pointerEvents={pointerEventDisabled.current ? "none" : "auto"} key={idx} {...panHandlers} style={[ isTopCard && animatedCardStyle, { position: "absolute", justifyContent: "center", alignItems: "center", bottom: 0, top: 0, left: 0, right: 0, }, ]} > <UserOverview data={card} /> </Animated.View> ); }) .reverse() ); };
额外说明
useRef的更新是同步的,不会像useState那样触发组件重渲染,因此不会打断动画的启动流程- 动画结束后再把
pointerEventDisabled.current设回false,不影响后续卡片的交互
内容的提问来源于stack exchange,提问作者reactnative_newbie
相关产品推荐
相关产品推荐

