CSS 滚动驱动动画(scroll-timeline):无 JS 实现滚动特效
<h1 data-id="heading-0">🧑💻 写在开头</h1><p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<h2 data-id="heading-0">一、传统方案的痛点</h2>
<p>以前实现滚动动画需要 JavaScript 监听 scroll 事件:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">window.addEventListener('scroll', () => {
const scrollTop = window.scrollY;
const progress = scrollTop / (document.body.scrollHeight - window.innerHeight);
// 更新进度条
progressBar.style.width = `${progress * 100}%`;
// 视差效果
parallaxElement.style.transform = `translateY(${scrollTop * 0.5}px)`;
});</pre>
</div>
<p>问题:</p>
<ul>
<li>性能开销大(频繁触发)</li>
<li>需要手动计算</li>
<li>代码复杂</li>
</ul>
<hr>
<h2 data-id="heading-1">二、scroll-timeline 的解决方案</h2>
<p>CSS 滚动驱动动画让元素的动画进度与滚动位置绑定,无需 JavaScript。</p>
<h3 data-id="heading-2">基础语法</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">@keyframes fade-in {
from { opacity: 0; transform: translateY(50px); }
to { opacity: 1; transform: translateY(0); }
}
.element {
animation: fade-in linear;
animation-timeline: scroll();/* 绑定到滚动 */
}</pre>
</div>
<h2 data-id="heading-3">三、scroll() 函数</h2>
<p><code>scroll()</code> 创建一个滚动时间线,将动画进度与滚动位置关联。</p>
<h3 data-id="heading-4">语法</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">animation-timeline: scroll(<scroller> <axis>);</pre>
</div>
<p>参数:</p>
<ul>
<li><code>scroller</code>: 滚动容器(nearest | root | self)</li>
<li><code>axis</code>: 滚动方向(block | inline | y | x)</li>
</ul>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* 最近的滚动祖先,垂直方向 */
animation-timeline: scroll(nearest block);
/* 根滚动容器,水平方向 */
animation-timeline: scroll(root inline);
/* 元素自身,垂直方向 */
animation-timeline: scroll(self y);</pre>
</div>
<h2 data-id="heading-5">四、实战案例</h2>
<h3 data-id="heading-6">案例 1:滚动进度条</h3>
<div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><div class="progress-bar"></div></pre>
</div>
</div>
<div class="code-block-extension-header">
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 4px;
background: linear-gradient(to right, #4caf50, #2196f3);
transform-origin: left;
animation: grow-progress linear;
animation-timeline: scroll(root block);
}
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
</pre>
</div>
<p> </p>
<p>效果:进度条宽度随页面滚动增长。</p>
<h3 data-id="heading-7">案例 2:滚动淡入</h3>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.fade-in-section {
opacity: 0;
transform: translateY(50px);
animation: fade-in linear;
animation-timeline: view();/* 元素进入视口时触发 */
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}</pre>
</div>
<h3 data-id="heading-8">案例 3:视差滚动</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.parallax-bg {
position: fixed;
top: 0;
width: 100%;
height: 100vh;
z-index: -1;
animation: parallax linear;
animation-timeline: scroll(root block);
}
@keyframes parallax {
to {
transform: translateY(50%);
}
}</pre>
</div>
<h3 data-id="heading-9">案例 4:图片缩放</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.hero-image {
width: 100%;
height: 100vh;
object-fit: cover;
animation: zoom-out linear;
animation-timeline: scroll(root block);
}
@keyframes zoom-out {
from {
transform: scale(1.2);
}
to {
transform: scale(1);
}
}</pre>
</div>
<h2 data-id="heading-10">五、view() 函数</h2>
<p><code>view()</code> 创建一个视图时间线,当元素进入/离开视口时触发动画。</p>
<h3 data-id="heading-11">语法</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">animation-timeline: view(<axis> <inset>);
</pre>
</div>
<p> </p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* 元素进入视口时触发 */
.element {
animation: fade-in linear;
animation-timeline: view();
}
/* 设置触发范围 */
.element {
animation: fade-in linear;
animation-timeline: view(block 20% 20%);
/* 在视口上下 20% 的范围内触发 */
}</pre>
</div>
<h2 data-id="heading-12">六、animation-range</h2>
<p>控制动画在滚动范围内的哪个阶段执行。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.element {
animation: fade-in linear;
animation-timeline: view();
animation-range: entry 0% entry 100%;
/* 只在进入阶段执行动画 */
}</pre>
</div>
<p>范围关键字:</p>
<ul>
<li><code>entry</code>: 元素进入视口</li>
<li><code>exit</code>: 元素离开视口</li>
<li><code>contain</code>: 元素完全在视口内</li>
<li><code>cover</code>: 整个过程</li>
</ul>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* 进入时淡入 */
.fade-in {
animation: fade linear;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
/* 离开时淡出 */
.fade-out {
animation: fade linear reverse;
animation-timeline: view();
animation-range: exit 0% exit 100%;
}</pre>
</div>
<h2 data-id="heading-13">七、组合使用</h2>
<h3 data-id="heading-14">多个动画阶段</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.card {
animation:
slide-in linear,
rotate linear;
animation-timeline: view();
animation-range:
entry 0% entry 50%,
entry 50% entry 100%;
}
@keyframes slide-in {
from { transform: translateX(-100%); }
to { transform: translateX(0); }
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}</pre>
</div>
<h3 data-id="heading-15">视差分层</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.layer-1 {
animation: parallax-slow linear;
animation-timeline: scroll();
}
.layer-2 {
animation: parallax-medium linear;
animation-timeline: scroll();
}
.layer-3 {
animation: parallax-fast linear;
animation-timeline: scroll();
}
@keyframes parallax-slow {
to { transform: translateY(20%); }
}
@keyframes parallax-medium {
to { transform: translateY(40%); }
}
@keyframes parallax-fast {
to { transform: translateY(60%); }
}</pre>
</div>
<h2 data-id="heading-16">八、浏览器支持</h2>
<p>滚动驱动动画在现代浏览器中支持:</p>
<ul>
<li>Chrome 115+</li>
<li>Edge 115+</li>
<li>Safari 尚未支持</li>
<li>Firefox 尚未支持</li>
</ul>
<h3 data-id="heading-17">特性检测</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">@supports (animation-timeline: scroll()) {
/* 支持滚动动画 */
.element {
animation: fade-in linear;
animation-timeline: scroll();
}
}
@supports not (animation-timeline: scroll()) {
/* 降级方案:使用 JavaScript */
.element {
opacity: 1;
}
}</pre>
</div>
<h3 data-id="heading-18">Polyfill</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;"><script src="https://flackr.github.io/scroll-timeline/dist/scroll-timeline.js"></script></pre>
</div>
<h2 data-id="heading-19">九、性能优化</h2>
<h3 data-id="heading-20">1. 使用 transform 和 opacity</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* ✅ 性能好:只触发合成 */
@keyframes good {
from {
opacity: 0;
transform: translateY(50px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ❌ 性能差:触发重排 */
@keyframes bad {
from {
top: 50px;
width: 100px;
}
to {
top: 0;
width: 200px;
}
}</pre>
</div>
<h3 data-id="heading-21">2. 使用 will-change</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.animated-element {
will-change: transform, opacity;
animation: fade-in linear;
animation-timeline: scroll();
}</pre>
</div>
<h3 data-id="heading-22">3. 限制动画元素数量</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">/* 只对可见区域的元素应用动画 */
.element {
animation: fade-in linear;
animation-timeline: view();
animation-range: entry 0% cover 100%;
}</pre>
</div>
<h2 data-id="heading-23">十、实用技巧</h2>
<h3 data-id="heading-24">1. 数字滚动计数</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">@property --num {
syntax: '<integer>';
initial-value: 0;
inherits: false;
}
.counter {
counter-reset: num var(--num);
animation: count linear;
animation-timeline: view();
}
.counter::after {
content: counter(num);
}
@keyframes count {
from { --num: 0; }
to { --num: 100; }
}</pre>
</div>
<h3 data-id="heading-25">2. 文字逐字显示</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.text {
animation: reveal linear;
animation-timeline: view();
}
@keyframes reveal {
from {
clip-path: inset(0 100% 0 0);
}
to {
clip-path: inset(0 0 0 0);
}
}</pre>
</div>
<h3 data-id="heading-26">3. 图片模糊到清晰</h3>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">.image {
filter: blur(10px);
animation: unblur linear;
animation-timeline: view();
}
@keyframes unblur {
to {
filter: blur(0);
}
}</pre>
</div>
<h2 data-id="heading-27">十一、与 JavaScript 对比</h2>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202604/2149129-20260428140100394-690419499.png" alt="ScreenShot_2026-04-28_135613_083" loading="lazy"></p>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19943806
頁:
[1]