使用array.map渲染的元素如何在Framer Motion中单独触发进入视口动画
Framer Motion map渲染元素单独触发视口动画解决方案
问题根因
- 父容器绑定ref:所有子元素复用父容器的同一个
inView状态,父容器进入视口时状态批量更新,3个div动画同步触发 - 循环绑定同一ref:单个ref变量在map遍历过程中会被反复赋值覆盖,最终仅存储最后一个div的引用,因此只有最后一个元素进入视口时才会触发全局共享的
inView状态,全量div统一触发动画
最简实现方案
核心思路是将单个待渲染的div封装为独立子组件,每个子组件内部独立维护自己的视口检测ref、动画状态,天然隔离互不干扰。
步骤1:封装独立动画子组件
import { motion, useInView, useAnimation } from "framer-motion"; import { useRef, useEffect } from "react"; const AnimatedDiv = ({ content }) => { // 每个组件内部独立的动画控制器、ref、视口检测状态 const controls = useAnimation(); const itemRef = useRef(null); const isInView = useInView(itemRef, { once: true, // 仅触发一次,不需要可删除 margin: "-50px 0px" // 自定义触发阈值,元素进入视口50px时触发 }); useEffect(() => { if (isInView) { controls.start("show"); } }, [controls, isInView]); return ( <motion.div ref={itemRef} initial="hide" animate={controls} variants={{ hide: { opacity: 0, y: 20 }, show: { opacity: 1, y: 0, transition: { duration: 0.5 } } }} > {content} </motion.div> ); };
步骤2:父组件遍历渲染子组件
const ParentComponent = () => { // 你的原始数据列表 const dataList = [ { id: 1, content: "第一个div内容" }, { id: 2, content: "第二个div内容" }, { id: 3, content: "第三个div内容" } ]; return ( <div className="父容器样式"> {dataList.map(item => ( <AnimatedDiv key={item.id} content={item.content} /> ))} </div> ); };
可选替代方案(不抽离组件)
如果不想封装子组件,也可以通过ref数组存储每个元素的引用,单独维护每个元素的inView状态即可,但代码冗余度更高,不优先推荐。
内容的提问来源于stack exchange,提问作者Mun
相关产品推荐
相关产品推荐

