CSS scroll-timeline 太猛了,我把页面的滚动进度条做成了自定义动画

2026-08-29 0 342

以前做一个“阅读进度条”,哪个前端不是先写个 addEventListener('scroll'),然后计算 scrollTop / (scrollHeight – clientHeight),再改一个元素的 width。现在呢?Chrome 115 之后,你只要用 CSS 的 scroll-timelineanimation-timeline,就能把动画和滚动位置天然绑定。没错,不用 JS,甚至不用一行业务逻辑。

先搞清楚这俩新东西是干嘛的

简单说,scroll-timeline 允许你把某个滚动容器(比如页面的根元素,或者一个 overflow:scroll 的 div)定义成一条“时间线”。然后你写的 CSS 动画就不再以时间而是以滚动进度来驱动。

好,那另一个 view-timeline 又是啥?它是跟元素在视口里的可见位置挂钩的。比如一个图片从底部出现到完全进入视口,它会提供一个进度值,你拿这个进度可以做入场动画。这两个东西组合使用,可以告别绝大多数用于滚动监听的 JS。

先搞一个最简单的页面滚动进度条

假设你把整个 body 当作滚动容器,你想在页面顶部显示一条红色进度条。以前可能是这样的:

// 以前我们写一堆 JS
window.addEventListener('scroll', () => {
    const h = document.documentElement;
    const pct = h.scrollTop / (h.scrollHeight - h.clientHeight);
    progressBar.style.width = pct * 100 + '%';
});
    

现在你只需要一个 div 放在 body 开头,比如:

<div class="progress"></div>
    

然后 CSS 里定义好动画和时间线:

/* 先注册根元素滚动为一条时间线 */
@keyframes grow {
    from { transform: scaleX(0); }
    to   { transform: scaleX(1); }
}

.progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: #f00;
    transform-origin: left center;
    animation: grow linear both;
    animation-timeline: scroll(root block); /* 指定滚动时间线 */
}
    

注意到关键一行 animation-timeline: scroll(root block) 里的 root 表示用根滚动容器(也就是页面本身),block 表示按块方向滚动(纵向)。然后动画会随着滚动的进度走 fromto。你根本不需要知道滚动距离,浏览器自己在内部把滚动位置映射成 0 到 100% 的进度。

把进度条改成渐变、加个发光也随你

你会发现这个动画是可以叠加任何 CSS 属性变化的。比如我想让进度条在滚动时从左到右显示,而且颜色从蓝到绿,再带个模糊变化:

@keyframes progress-anim {
    0%   { width: 0%; background: #0af; filter: blur(0px); }
    50%  { width: 50%; background: #0f0; filter: blur(1px); }
    100% { width: 100%; background: #f80; filter: blur(0px); }
}

.progress {
    height: 6px;
    animation: progress-anim linear both;
    animation-timeline: scroll(root block);
}
    

这里我用了 width 属性,你可能会问直接给 width 做动画会不会触发重排?会,但在这个场景里影响不大。不过我建议你改用 scaleX,流畅度更好。

案例二:图片渐显入场,不用IO了

以前我们用 IntersectionObserver 来做图片懒加载入场,现在可以考虑用 view-timeline 实现。假设你有一张图片:

<img src="photo.jpg" class="fade-in" alt="一张图">
    

CSS 里这样写:

@keyframes fadeSlide {
    from { opacity: 0; transform: translateY(40px); }
    to   { opacity: 1; transform: translateY(0); }
}

.fade-in {
    view-timeline-name: --item-in;
    view-timeline-axis: block;
    animation: fadeSlide linear both;
    animation-timeline: view(--item-in);
}
    

解释一下,view-timeline-name: --item-in 给这个元素自己定义了一条视图时间线。它的进度是 0% 的时候元素还没进入视口,100% 的时候元素完全离开视口?不对,这里有个细节:默认的 view-timeline 的范围是元素从进入视口到完全离开视口,所以如果你直接用它,动画会在元素离开视口时才结束。如果你想让它只在“进入视口”这一小段播放,可以通过 animation-range 调整。

.fade-in {
    view-timeline-name: --item-in;
    animation: fadeSlide linear both;
    animation-timeline: view(--item-in);
    animation-range: entry 0% entry 50%; /* 在进入视口的前半段完成动画 */
}
    

还可以写成覆盖 25% 到 75% 的区间。这个 animation-range 非常灵活,它就像截取时间线的一段来映射动画。你甚至可以这样:

animation-range: entry 10% exit 50%;
    

意思是元素进入视口 10% 到离开视口 50% 这个区间内,动画从 0 走到 100。这个能力以前根本不敢想。

案例三:滚动驱动某个元素旋转 360 度

我见过有人用这个做了一个小火箭,页面滚到底部的时候转一圈。其实更像是一个装饰性的图形。代码异常简单:

.spinner {
    width: 50px;
    height: 50px;
    background: conic-gradient(#f00, #ff0);
    border-radius: 50%;
    animation: spin linear both;
    animation-timeline: scroll(root block);
}

@keyframes spin {
    to { transform: rotate(360deg); }
}
    

当你滚动页面时,这个小圆球就会跟着滚动进度一直转。如果你想让它在滚动到某个区域才开始转,可以用 animation-range: cover 0% 100% 或者用 view() 配合容器。

几个实际项目里必须注意的坑

先说兼容性:目前 Chrome 和 Edge 都能用,Safari 和 Firefox 还没影呢。所以你要么把功能作为“增强”,要么用 JS 检测支持情况再 fallback。我一般用 @supports (animation-timeline: scroll()) 包一层,不支持无非就是没动画,不影响阅读。

还有一个大坑:当你给一个元素配上 animation-timeline 时,该元素不能再用普通的 animation-duration 来控制时间。因为时间线已经被滚动接管了。如果你不小心写了 animation: spin 2s linear both,这样时间线就变成 2 秒,而不是滚动。所以记得省略时间,或者单独写 animation-duration: auto

还有如果你把滚动容器设置成某个 div,而不是根元素,那必须给那个 div 设置 overflow: scroll 或者 overflow-y: scroll,并且给它一个高度,否则它根本不会滚动。

.scroll-container {
    overflow-y: scroll;
    height: 500px;
    scroll-timeline-name: --container-scroll;
    scroll-timeline-axis: block;
}

.child {
    animation: fancy both;
    animation-timeline: --container-scroll;
}
    

这里的 scroll-timeline-name: --container-scroll 就是把当前容器的滚动命名为一个自定义时间线。子元素就可以用 animation-timeline: --container-scroll 来引用。

用 view-timeline 做一个吸底菜单的自动隐藏

有些页面会在你向下滚动时隐藏导航栏,向上滚动时显示。以前这要用 JS 判断滚动方向。虽然现在 scroll-timeline 不直接给你方向判断,但你可以利用它结合两个关键帧让它“变相实现”。比如你在页脚放一个元素,当它“即将进入视口”时把导航栏推出屏幕,当它“即将离开视口”时拉回来。但这还是有点绕,不如 JS 直白。所以别什么都硬用 CSS,适合的才用。

给文章来个真·进度条,而且不碍事

最后我给你一个能直接用在博客上的完整代码。比如在 body 最上面放一个 div,然后在 CSS 里滚动 root 时间线,再配合一个非常实在的动画:

@keyframes reading-progress {
    0% { --n: 0; }
    100% { --n: 100; }
}

@property --n {
    syntax: '<integer>';
    initial-value: 0;
    inherits: false;
}

.progress::before {
    counter-reset: n var(--n);
    content: counter(n) '%';
    position: absolute;
    right: 8px;
    top: -16px;
    font-size: 12px;
    color: #666;
}
    

这个有点炫技了,它用计数器把滚动百分比显示在进度条尾巴上。但实际演示中,你直接在控制台看看 --n 变没变就行。

当你真的想放弃 JS 时,想想可访问性

滚动驱动动画大部分时候是装饰性的,所以要注意用户是否动了“减少动态效果”的系统设置。好在 CSS 有 prefers-reduced-motion,你可以这样处理:

@media (prefers-reduced-motion: reduce) {
    * {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        animation-timeline: none !important;
    }
}
    

这样那些不喜欢动画的用户就不会被晃到。

我自己的实际测试感受

我在一个长页面上做了四个不同元素的滚动动画,整个页面滚动时的流畅度非常棒,没有出现闪烁。相比以前用 JS 批量更新 requestAnimationFrame,这个方案没有主线程压力,因为合成器直接把滚动位置传给动画了。但是要特别注意,如果你在动画里改的是 widthtop 这类几何属性,浏览器可能不会走合成器,依然会产生重排,建议尽量对 transformopacity 做动画。

总结一下

  • scroll-timeline 用于监听滚动容器的滚动进度,view-timeline 用于监听元素自身在视口中的可见变化。
  • animation-timeline 指定为 scroll(root block) 或自定义的 --timeline 即可让动画由滚动驱动。
  • animation-range 控制动画对应时间线的哪一段,比如 entry 0% entry 50% 来让元素一进入画面就完成动效。
  • 兼容性并没有全面覆盖,但你可以放心用 @supports 做增强,不影响旧浏览器的阅读。

如果你还在用 JS 写滚动进度条,真的建议试试这玩意儿。刚开始有点不习惯,但用熟了以后你会回来叫爽的。顺手给项目加个平滑的滚动指示器,几行代码就搞定,还不用监听 scroll,多惬意。

完。

CSS scroll-timeline 太猛了,我把页面的滚动进度条做成了自定义动画
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

版权声明:
本站资源有的来自互联网收集整理,本站纯免费分享提供学习使用,如果侵犯了您的合法权益,请发送邮件1506151422@qq.com联系,将会及时下架删除。
本站资源仅供研究、学习交流之用,免费开源项目不代表完全可商用,若商业用途请先咨询开发企业能否商用,否则产生的一切后果将由下载用户自行承担。
原创板块未经允许不得转载,否则将追究法律责任。

淘吗网 css CSS scroll-timeline 太猛了,我把页面的滚动进度条做成了自定义动画 https://www.taomawang.com/web/css/2665.html

常见问题

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务