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

Framer-motion drag功能不遵循已更新props 滑块拖拽分页动画异常

问题根因

该异常是Framer Motion的拖拽特性导致的:拖拽元素时,Framer Motion会内部维护独立于你的position state的临时位移值,用于实现拖拽跟随效果。拖拽结束更新position后,Motion的动画会从拖拽结束时的临时偏移位置开始过渡到新的position,而非从之前的state基准位置开始,因此会出现跳页的错觉。按钮点击时没有拖拽操作,内部临时偏移始终为0,所以运行正常。

解决方案

推荐使用useMotionValue接管x轴位移管理,实现state和Motion内部值的同步,修复代码如下:

import { useState, useEffect } from 'react';
import { useMotionValue } from 'framer-motion';

export default function App() {
  const [position, setPosition] = useState<number>(0);
  // 声明motion值,和分页基准位置初始值同步
  const x = useMotionValue(position);

  // 监听分页位置变化,同步更新motion值
  useEffect(() => {
    x.set(position);
  }, [position, x]);

  const paginate = (direction: Direction) => {
    setPosition((prev) => {
      return direction === Direction.Forward
        ? Math.max(-800, prev - 200)
        : Math.min(0, prev + 200);
    });
  };

  return (
    <div className="App">
      <Slider>
        <Wrapper
          // 绑定motion值到元素x轴位移
          style={{ x }}
          transition={{
            x: { duration: 1, type: "tween" }
          }}
          drag="x"
          dragConstraints={{
            top: 0,
            left: 0,
            right: 0,
            bottom: 0
          }}
          // 关闭拖拽弹性,避免超出边界的回弹和分页动画冲突
          dragElastic={0}
          onDragEnd={(e, { offset, velocity }) => {
            const swipe = swipePower(offset.x, velocity.x);

            if (swipe < -swipeConfidenceThreshold) {
              paginate(Direction.Forward);
            } else if (swipe > swipeConfidenceThreshold) {
              paginate(Direction.Back);
            } else {
              // 未达到分页阈值,手动回弹到当前分页基准位置
              x.set(position);
            }
          }}
        >
          <Slide>1</Slide>
          <Slide className="alt">2</Slide>
          <Slide>3</Slide>
          <Slide className="alt">4</Slide>
          <Slide>5</Slide>
        </Wrapper>
      </Slider>
      <button onClick={() => paginate(Direction.Back)}>prev</button>
      <button onClick={() => paginate(Direction.Forward)}>next</button>
    </div>
  );
}

如果是简单场景想要快速修复,也可以直接给Wrapper组件添加key={position}属性,每次分页位置变化时强制重绘组件,自动清空Motion内部维护的拖拽偏移值即可,该方案无需修改其他逻辑,但性能略低于上述方案。

内容的提问来源于stack exchange,提问作者Samuel Goldenbaum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 13:06:03