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

佳怡酱~ 阅读 9

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

我用的是 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 一进入视口就一起加类名,怎么让它们一个一个按顺序淡入?是不是要加延迟?

我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
轩辕君杰
对,你猜得没错,就是要加延迟。问题在于 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 比较自然。

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