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

为何添加新行时MotionButton先上浮再下沉?

添加航班日期时按钮动画效果

我希望添加新行时按钮能平滑下移适配布局,因此给基于motion(...)包装的自定义组件MotionButton添加了layout属性。当前实现代码如下:

<div className="grid gap-5">
  <AnimatePresence>
    {flightDates.map((f, index) => (
      <motion.div
        key={index}
        exit={{
          opacity: 0,
          y: 10,
        }}
        initial={{
          opacity: 0,
          y: 10,
        }}
        animate={{
          opacity: 1,
          y: 0,
        }}
        transition={{
          duration: 0.2,
        }}
      >
        <FlightDateRow
          key={index}
          onChange={(updatedFlight) => {
            setFlightDates((prev) => {
              const newFlights = [...prev]
              newFlights[index] = {
                ...newFlights[index],
                ...updatedFlight,
              }
              return newFlights
            })
          }}
          onDelete={removeFlightDate(index)}
          canDelete={flightDates.length > 1}
          {...f}
        />
      </motion.div>
    ))}
  </AnimatePresence>
  <MotionButton
    className="w-min pl-2"
    variant="secondary"
    onClick={addNewFlightDate}
    layout
    transition={{
      duration: 0.2,
    }}
  >
    <PlusMini className="mr-2" />
    Flight
  </MotionButton>
</div>

优化建议

  1. 替换列表key为唯一标识:当前用index作为motion.div和FlightDateRow的key,在列表项增删时会导致React错误地复用组件,干扰动画和状态更新。建议改用每个航班日期对象的唯一ID(比如f.id)作为key。
  2. 确保MotionButton的layout属性生效:确认自定义的MotionButton组件正确传递layout属性到底层motion元素,示例包装方式:
    const MotionButton = ({ children, ...props }) => (
      <motion.button {...props}>
        {children}
      </motion.button>
    );
    
  3. 统一动画节奏:把过渡时长提取为常量,保持行和按钮的动画时长一致,提升交互流畅度。

修改后的代码示例:

const TRANSITION_DURATION = 0.2;

<div className="grid gap-5">
  <AnimatePresence>
    {flightDates.map((f) => (
      <motion.div
        key={f.id}
        exit={{ opacity: 0, y: 10 }}
        initial={{ opacity: 0, y: 10 }}
        animate={{ opacity: 1, y: 0 }}
        transition={{ duration: TRANSITION_DURATION }}
      >
        <FlightDateRow
          key={f.id}
          onChange={(updatedFlight) => {
            setFlightDates((prev) =>
              prev.map(item => 
                item.id === f.id ? {...item, ...updatedFlight} : item
              )
            );
          }}
          onDelete={removeFlightDate(f.id)}
          canDelete={flightDates.length > 1}
          {...f}
        />
      </motion.div>
    ))}
  </AnimatePresence>
  <MotionButton
    className="w-min pl-2"
    variant="secondary"
    onClick={addNewFlightDate}
    layout
    transition={{ duration: TRANSITION_DURATION }}
  >
    <PlusMini className="mr-2" />
    Flight
  </MotionButton>
</div>

内容的提问来源于stack exchange,提问作者Stephen Horton

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:52:54