React + Framer Motion中如何为初始视口可见元素与滚动可见元素设置不同动画延迟
React + Framer Motion中如何为初始视口可见元素与滚动可见元素设置不同动画延迟
我完全懂你的困扰!页面加载时已经在视口内的元素需要有合理的延迟来保证整体动画连贯性,但滚动才会出现的元素如果还用同样的长延迟,用户就得等半天才能看到内容,体验太差了。咱们用Framer Motion自带的工具就能解决这个问题~
核心思路
我们需要在组件初始化时判断元素是否已经在视口中,然后根据这个结果动态切换动画延迟。Framer Motion提供的useInView钩子正好能帮我们做到这一点,它可以在组件挂载时就检测元素的可见性。
具体实现步骤(修改Card组件)
- 导入Framer Motion的
useInView和useRef(用来关联动画元素) - 创建一个ref绑定到motion.div上
- 用
useInView开启初始可见性检测,确保页面加载时就执行判断 - 根据检测结果动态设置延迟值
修改后的Card.tsx代码如下:
// Card.tsx import { motion, useInView, useRef } from "framer-motion"; type CardProps = { title: string; description: string; index: number; }; export default function Card({ title, description, index, }: CardProps) { const cardRef = useRef(null); // 初始加载时就检测元素是否在视口中 const isInitiallyInView = useInView(cardRef, { once: true, initial: true, }); // 根据初始可见性设置延迟:可见用原有的长延迟,不可见用短延迟 const delay = isInitiallyInView ? 1.05 + index * 0.2 : 0.25; return ( <motion.div ref={cardRef} initial={{ opacity: 0, y: 10 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ ease: 'linear', duration: 0.5, delay: delay, }} > <p>{title}</p> <p>{description}</p> <a> Learn more <svg>...</svg> </a> </motion.div> ); }
额外优化:统一页面其他元素的逻辑
注意到你页面中的Projects标题用的是animate属性,这会导致不管它是否在初始视口中都会立刻执行动画。为了保持整个页面逻辑一致,建议也改成whileInView模式,同样用useInView来设置延迟:
// page.tsx中修改Projects标题部分 import { motion, useInView, useRef } from "framer-motion"; // 在组件内部添加 const projectsRef = useRef(null); const isProjectsInitiallyInView = useInView(projectsRef, { once: true, initial: true, }); // 修改motion.h2 <motion.h2 ref={projectsRef} initial={{ opacity: 0, y: 10 }} whileInView={{ opacity: 1, y: 0 }} viewport={{ once: true }} transition={{ ease: 'linear', duration: 0.5, delay: isProjectsInitiallyInView ? 0.8 : 0.25 }} > Projects </motion.h2>
原理说明
useInView钩子的initial: true选项会让它在组件挂载完成后立刻检查元素是否在视口中,返回的isInitiallyInView值就是我们判断的依据。结合once: true,确保动画只会触发一次,完美匹配你的需求。
备注:内容来源于stack exchange,提问作者matt
相关产品推荐
相关产品推荐

