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
相关产品推荐
相关产品推荐

