滚动时如何实现元素逐个淡入的动画效果?

佳怡酱~ 阅读 25

我在做页面滚动动画,想让多个卡片在滚到视口时逐个淡入,但目前所有元素一进视口就同时触发了。

我用的是 IntersectionObserver,代码大概是这样:

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('fade-in');
    }
  });
});

document.querySelectorAll('.card').forEach(card => {
  observer.observe(card);
});

但这样所有 .card 一进入视口就一起加类名,怎么让它们一个一个按顺序淡入?是不是要加延迟?

我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
慕容婧妍
对,加延迟就行。最简单的做法是用 CSS 的 transition-delay,配合索引计算延迟时间:

.card {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s, transform 0.5s;
}

.card.fade-in {
opacity: 1;
transform: translateY(0);
}


const cards = document.querySelectorAll('.card');

// 给每个卡片设置递增的延迟
cards.forEach((card, index) => {
card.style.transitionDelay = ${index * 0.1}s;
});

const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in');
observer.unobserve(entry.target); // 触发后取消观察,省点性能
}
});
}, { threshold: 0.1 });

cards.forEach(card => observer.observe(card));


每个卡片延迟 0.1 秒,第二个 0.2 秒,以此类推。这样进入视口时会一个接一个淡入。

如果想控制速度,改 transitionDelay 的乘数就行。比如 index * 0.15 就慢一点。

另外提一下,threshold: 0.1 表示元素露出 10% 就触发,比默认的 0 体验好一点,不会太迟钝。
点赞
2026-03-13 04:00
轩辕君杰
对,你猜得没错,就是要加延迟。问题在于 IntersectionObserver 的回调里,所有进入视口的元素几乎同一时间被处理,CSS 动画也就同时触发了。

有两种常见做法,都很简单。

第一种是纯 CSS 方案,最省事。在写样式的时候,给每个卡片按顺序加上 transition-delay

.card {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.5s ease, transform 0.5s ease;
}

.card:nth-child(1) { transition-delay: 0s; }
.card:nth-child(2) { transition-delay: 0.1s; }
.card:nth-child(3) { transition-delay: 0.2s; }
.card:nth-child(4) { transition-delay: 0.3s; }

.card.fade-in {
opacity: 1;
transform: translateY(0);
}


这样你的 JS 代码完全不用改,CSS 会自动处理错开时间的问题。缺点是如果卡片数量不固定,得写很多 nth-child,或者用 CSS 变量配合 JS 设置。

第二种是用 JS 动态设置延迟,更灵活:

const cards = document.querySelectorAll('.card');

cards.forEach((card, index) => {
card.style.transitionDelay = ${index * 0.1}s;
});

const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in');
}
});
});

cards.forEach(card => observer.observe(card));


这样每个卡片的动画会依次延迟 0.1 秒触发,形成逐个淡入的效果。延迟时间可以根据你想要的节奏调整,一般 0.1s 到 0.15s 比较自然。

两种方案都能跑,看你项目情况选一个就行。
点赞 5
2026-02-28 14:15