Framer Motion Reorder元素错位及AnimatePresence动画问题求助
解决方案:Framer Motion Reorder组件两个问题的修复
问题1:首次拖拽X轴偏移等于侧边栏宽度
修复方案:
- 限制X轴拖拽弹性:因为是纵向(y轴)排序,完全禁用X轴的拖拽弹性,避免不必要的偏移:
<Reorder.Item // 其他props保持不变 dragElastic={{ x: 0, y: 0.1 }} // 仅保留Y轴弹性 dragSnapToOrigin // 拖拽结束后精确回到原始位置 > {/* 子组件 */} </Reorder.Item> - 校验dragConstraints的引用:确保
listRef通过useRef(null)正确初始化,且Reorder.Group的DOM结构没有额外的定位偏移:const listRef = useRef(null); // 在Reorder.Group中确保relative定位 <Reorder.Group ref={listRef} className="flex flex-col p-0 m-0 list-none relative" // 其他props不变 > - 修正父容器坐标系:如果侧边栏使用
position: fixed/absolute,给Reorder.Item添加style={{ transformOrigin: 'top left' }},确保拖拽计算基于自身左上角而非全局坐标系。
问题2:AnimatePresence高度动画未作用于子组件
修复方案:
- 添加overflow:hidden裁剪子组件:让Reorder.Item的高度动画能约束子组件的显示:
<Reorder.Item // 其他props不变 style={{ overflow: 'hidden' }} initial={{ height: 0 }} animate={{ height: 'fit-content' }} // 用fit-content替代auto,更精准 exit={{ height: 0 }} transition={{ duration: 0.3 }} // 统一动画时长 > <ProductItem // 子组件props不变 /> </Reorder.Item> - 让子组件参与动画:给ProductItem添加Framer Motion的motion属性,或者用motion.div包裹,确保子组件的内容随容器高度变化平滑过渡:
// 假设ProductItem是自定义组件,修改为motion组件 import { motion } from "framer-motion"; const ProductItem = motion.custom(ProductItemOriginal); // 或者在Reorder.Item内部用motion.div包裹 <Reorder.Item ...> <motion.div initial={{ opacity: 0, height: 0 }} animate={{ opacity: 1, height: 'fit-content' }} exit={{ opacity: 0, height: 0 }} transition={{ duration: 0.3 }} > <ProductItem ... /> </motion.div> </Reorder.Item> - 调整AnimatePresence的位置:确保AnimatePresence直接包裹动态渲染的Reorder.Item,且每个Item的key唯一(已满足,用id作为key)。
修改后的完整代码示例
import { motion, AnimatePresence } from "framer-motion"; import { Reorder } from "framer-motion/dom"; import { useRef } from "react"; // ... 其他组件逻辑 const listRef = useRef(null); // ... <Reorder.Group axis="y" className="flex flex-col p-0 m-0 list-none relative" values={orderedItems} onReorder={setOrderedItems} ref={listRef} > <AnimatePresence> {orderedItems?.map(item => { const { id, referencePayload: { abbreviation, description, title, url } } = item return ( <Reorder.Item key={id} value={item} dragConstraints={listRef} dragElastic={{ x: 0, y: 0.1 }} dragMomentum={false} dragSnapToOrigin onDragStart={() => onDragStart(id)} onDragEnd={() => onDragEnd(id)} initial={{ height: 0 }} animate={{ height: 'fit-content' }} exit={{ height: 0 }} transition={{ duration: 0.3 }} style={{ overflow: 'hidden' }} > <motion.div initial={{ opacity: 0, height: 0 }} animate={{ opacity: 1, height: 'fit-content' }} exit={{ opacity: 0, height: 0 }} transition={{ duration: 0.3 }} className="w-full" > <ProductItem id={id} url={url} abbreviation={abbreviation} title={title} description={description} onRemoveItem={handleRemoveFavourite} hasUpdateError={isRemovalError} isDragging={activeItemId === id} /> </motion.div> </Reorder.Item> ) })} </AnimatePresence> </Reorder.Group>
内容的提问来源于stack exchange,提问作者Ignacio Elias
相关产品推荐
相关产品推荐

