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

React结合Framer Motion开发动态列表:如何兼顾初始加载staggerChildren序列动画与新增项独立入场动画?

兼顾Framer Motion初始列表 stagger 动画与新增项独立入场的最佳实践

哈哈,这坑我刚踩过不久!刚好能给你一套完美解决的方案,兼顾初始 stagger 动画和新增项的独立入场效果,核心就是用状态区分初始渲染和后续新增的列表项,让两种场景的动画逻辑各司其职。

核心思路

我们需要给初始渲染的项保留父组件的变体继承(让staggerChildren生效),同时给新增的项强制开启独立动画。具体通过一个状态标记初始渲染是否完成,以此动态控制子组件的initial和animate属性。

修改后的完整代码

父组件(PlayList.tsx)

import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { SongCard } from './SongCard';

const containerVariant = {
  hidden: { opacity: 0 },
  visible: { opacity: 1, transition: { staggerChildren: 0.2 } }
};

export const PlayList = ({ queue }) => {
  // 标记初始渲染是否完成
  const [initialRenderDone, setInitialRenderDone] = useState(false);

  useEffect(() => {
    // 组件挂载后,标记初始渲染完成
    setInitialRenderDone(true);
  }, []);

  return (
    <motion.div 
      variants={containerVariant} 
      initial="hidden" 
      animate="visible"
    >
      <AnimatePresence>
        {queue.map((song) => (
          <SongCard 
            key={song.id} 
            song={song} 
            // 把状态传递给子组件,判断是否需要强制独立动画
            forceInitialAnimation={initialRenderDone}
          />
        ))}
      </AnimatePresence>
    </motion.div>
  );
};

子组件(SongCard.tsx)

import { motion } from 'framer-motion';

const itemVariant = {
  hidden: { opacity: 0, x: -20 },
  visible: { opacity: 1, x: 0 },
  exit: { opacity: 0, scale: 0.8 }
};

export const SongCard = ({ song, forceInitialAnimation }) => {
  return (
    <motion.div 
      variants={itemVariant} 
      exit="exit" 
      layout
      // 条件式设置动画属性:初始渲染时不设,继承父组件;新增项时强制设,触发独立动画
      initial={forceInitialAnimation ? "hidden" : undefined}
      animate={forceInitialAnimation ? "visible" : undefined}
    >
      {song.name}
    </motion.div>
  );
};

为什么这个方案能解决问题?

  1. 初始渲染阶段:initialRenderDone为false,子组件不设置initial和animate,完全继承父组件的变体状态。父组件的staggerChildren会控制所有列表项依次执行入场动画,完美实现你要的序列效果。
  2. 新增项阶段:initialRenderDone已经变为true,新增的SongCard会被强制设置initial="hidden"和animate="visible",成为独立的动画根节点,直接触发自身的入场动画,不会因为父组件已经处于visible状态而跳过。

额外注意点

  • 确保列表项的key是唯一且稳定的(你用song.id就很合适),AnimatePresence依赖key来识别元素的挂载/卸载。
  • 子组件的layout属性可以保留,它和这套动画逻辑不冲突,反而能让列表项的位置变化更平滑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 09:37:41