You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 08:42:11