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

Framer Motion Reorder元素错位及AnimatePresence动画问题求助

解决方案:Framer Motion Reorder组件两个问题的修复

问题1:首次拖拽X轴偏移等于侧边栏宽度

修复方案:

  1. 限制X轴拖拽弹性:因为是纵向(y轴)排序,完全禁用X轴的拖拽弹性,避免不必要的偏移:
    <Reorder.Item
      // 其他props保持不变
      dragElastic={{ x: 0, y: 0.1 }} // 仅保留Y轴弹性
      dragSnapToOrigin // 拖拽结束后精确回到原始位置
    >
      {/* 子组件 */}
    </Reorder.Item>
    
  2. 校验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不变
    >
    
  3. 修正父容器坐标系:如果侧边栏使用position: fixed/absolute,给Reorder.Item添加style={{ transformOrigin: 'top left' }},确保拖拽计算基于自身左上角而非全局坐标系。

问题2:AnimatePresence高度动画未作用于子组件

修复方案:

  1. 添加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>
    
  2. 让子组件参与动画:给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>
    
  3. 调整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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 13:35:25