React中使用styled-components实现滚动触发的Testimonial卡片逐个动画
实现滚动触发的卡片逐个入场动画(Testimonial板块)
要实现滚动到区域时卡片从底部逐个出现的效果,只需对现有代码做以下几处关键修改:
修改后的完整代码
import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import ticket from "./assets/ticket0.webp"; const TestimonialSection = styled.section``; const TestimonialDiv = styled.div` height: 80vh; margin: auto; width: 100%; display: flex; align-items: center; justify-content: center; min-height: 70%; `; const BottomCenter = styled.div` display: flex; align-items: center; justify-content: center; width: 100%; @keyframes splash { from { transform: translate(0%, 100%); opacity: 0; } to { transform: rotate(var(--rx)); opacity: 1; } } img{ width: 0%; position:absolute; transform:rotate(0deg); opacity: 0; transition: width 0.3s ease; } .f-image-25{ width:15%; --rx: -3deg; z-index:25; animation: splash 1s normal forwards ease-in-out; animation-delay: 0ms; } .f-image-35{ width:17%; --rx: -3deg; z-index:35; animation: splash 1s normal forwards ease-in-out; animation-delay: 200ms; } .f-image-40{ width:20%; --rx: 3deg; z-index:40; animation: splash 1s normal forwards ease-in-out; animation-delay: 400ms; } .f-image-30{ width:17%; --rx: -6deg; z-index:30; animation: splash 1s normal forwards ease-in-out; animation-delay: 600ms; } .f-image-20{ width:15%; --rx: 10deg; z-index:20; animation: splash 1s normal forwards ease-in-out; animation-delay: 800ms; } `; const Testimonial = () => { const [isAnimated, setIsAnimated] = useState(false); const options = { root: null, margin: '0px', threshold: 0.2 } const observerCallback = (entries) => { const [entry] = entries // 当元素进入视口且未触发过动画时,启动动画 if (entry.isIntersecting && !isAnimated) { setIsAnimated(true); } } useEffect(() => { const container = document.getElementById('testimonial-container') const observer = new IntersectionObserver(observerCallback, options) observer.observe(container) return () => { observer.unobserve(container); } }, []) return ( <TestimonialSection id="testimonial-container"> <TestimonialDiv> <BottomCenter> <img alt="testimonial-card" className={isAnimated ? 'f-image-25' : ''} src={ticket} /> <img alt="testimonial-card" className={isAnimated ? 'f-image-35' : ''} src={ticket} /> <img alt="testimonial-card" className={isAnimated ? 'f-image-40' : ''} src={ticket} /> <img alt="testimonial-card" className={isAnimated ? 'f-image-30' : ''} src={ticket} /> <img alt="testimonial-card" className={isAnimated ? 'f-image-20' : ''} src={ticket} /> </BottomCenter> </TestimonialDiv> </TestimonialSection> ) } export default Testimonial
关键改动说明
添加动画延迟(animation-delay)
给每个卡片的动画类设置不同的延迟时间,实现逐个入场的效果,延迟间隔可根据需求调整(这里用200ms间隔)。优化动画效果
在splash动画中加入opacity过渡,让卡片入场更自然,避免突然出现的生硬感。替换DOM操作为React状态控制类名
用isAnimated状态直接控制卡片的className,符合React的声明式编程范式,避免直接操作DOM带来的潜在问题。调整Intersection Observer阈值
将threshold设为0.2,意味着当元素有20%进入视口时就触发动画,让用户更早看到动画效果,提升交互体验。修复原代码的类名拼写错误
原代码中f- image-40多了空格,已修正为正确的f-image-40。
内容的提问来源于stack exchange,提问作者SE1822
相关产品推荐
相关产品推荐

