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

Framer Motion:监听resize更新状态后拖拽约束失效

问题原因
  1. resize事件监听未正确清理:你在useEffect中添加和移除的是两个独立的箭头函数实例(每次创建新的箭头函数),导致组件无法正确移除resize监听。随着窗口多次调整,会积累大量重复的监听回调,频繁触发setWindowsSize引发组件频繁重渲染,干扰了motion组件的拖拽状态管理,最终导致拖拽约束失效。
  2. 组件频繁重渲染破坏motion内部状态:窗口调整触发的状态更新会让整个组件(包括motion.div)重新渲染,motion内部维护的拖拽状态可能被意外重置,使得原本的约束规则不再生效。
解决步骤

步骤1:修复resize事件的监听与清理逻辑

将resize回调抽离为独立函数,确保添加和移除监听使用同一个函数引用:

const [windowSize, setWindowsSize] = useState(window.innerWidth);

// 用useCallback缓存函数引用,避免每次渲染生成新函数
const handleResize = useCallback(() => {
  setWindowsSize(window.innerWidth);
}, []);

useEffect(() => {
  window.addEventListener("resize", handleResize);
  // 移除监听时使用同一个函数引用
  return () => window.removeEventListener("resize", handleResize);
}, [handleResize]);

步骤2:确保拖拽约束的稳定性

如果你的拖拽约束是固定值,用useMemo缓存约束对象,避免每次重渲染生成新对象导致motion重新计算约束:

const dragConstraints = useMemo(() => ({ left: 0, right: 0 }), []);

<motion.div
    dragConstraints={dragConstraints}
    initial={{ x: 0 }}
    drag="x"
    ref={ref}
    dragElastic={0.2}
    className="flex gap-8"
>

如果约束需要随窗口尺寸动态调整,直接将约束与windowSize关联即可:

<motion.div
    dragConstraints={{ left: 0, right: windowSize - (ref.current?.offsetWidth || 0) }}
    initial={{ x: 0 }}
    drag="x"
    ref={ref}
    dragElastic={0.2}
    className="flex gap-8"
>

步骤3:减少不必要的组件重渲染(可选)

如果组件其他部分不需要依赖windowSize,可以将拖拽组件用React.memo包裹,避免因窗口尺寸更新触发无意义的重渲染:

const DraggableDiv = React.memo(({ windowSize }) => {
  const ref = useRef(null);
  const dragConstraints = useMemo(() => ({ left: 0, right: 0 }), []);
  
  return (
    <motion.div
        dragConstraints={dragConstraints}
        initial={{ x: 0 }}
        drag="x"
        ref={ref}
        dragElastic={0.2}
        className="flex gap-8"
    >
      {/* 组件内容 */}
    </motion.div>
  );
});

内容的提问来源于stack exchange,提问作者Marcin Maciąg

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 00:05:54