作者:Saron Yitbarek
前言
如果你是滚动驱动动画的新手,开始之前,不妨先阅读这篇面向初学者的滚动驱动动画介绍,然后再深入本指南。我在那篇文章中简要提到了animation-range
,这里我会更深入地探讨,涵盖不同的值及其含义。
让我们开始吧!🚀
首先,快速回顾一下 animation-range
是什么。
animation-range
与view()
时间轴是最佳拍档!如果你希望动画在用户滚动且元素在视口中可见时才开始执行,这对组合简直是神器。
但说元素"在视口中可见"可没那么简单,这里面学问大了!以下是几种不同的解释方式:
假设你要动画的元素是一张高度为 300px 的照片。
"在视口中可见" 是指:
- 照片第一个像素刚露脸就开始动画?(急性子模式)
- 等到整个照片都可见时才开始?(完美主义模式)
- 照片滚到页面中间时开始?(中庸之道模式)
- 照片快离开但还没完全离开时停止动画?(恋恋不舍模式)
选择困难症都要犯了?别慌!animation-range
就是来拯救你的选择困难的!它能让你精确到极致,想让动画啥时候开始就啥时候开始,想啥时候结束就啥时候结束。😎
定义
animation-range
其实是两个属性的简写:animation-range-start
和animation-range-end
。
animation-range-start
负责指定动画何时开始,animation-range-end
负责声明动画何时结束。在本文中,我们主要关注简写形式。
简写形式可以接受两种类型的值:timeline-range-name
和length-percentage
。让我们先深入了解timeline-range-name
。
timeline-range-name
根据元素相对于视口的位置来定义动画的开始和结束。
举个栗子🌰:
假设你有一张300x200px的图片,放在一个1000px宽的容器中。你希望图片从最右侧开始,随着滚动滑到容器的左侧。

以下是该动画的CSS:
css
img {
animation: slideIn;
animation-timeline: view();
}
@keyframes slideIn {
0% {
transform: translateX(700px);
}
100% {
transform: translateX(0px);
}
}
现在问题来了------你想在哪个确切的点开始这场滑动表演?
cover:急性子的选择
如果你想在图片的第一个像素刚进入视口时就喊"Action!",那么cover
就是你的菜!单独使用时,它会将时间轴的开始(0%)设置为图片刚进入视图的那一刻。
css
img {
animation: slideIn;
animation-timeline: view();
animation-range: cover;
}
它会让你的照片一路滑到左侧,直到时间轴的结束(100%),也就是最后一个像素从视图中消失的那一刻才喊"Cut!"。
这意味着即使只有一小部分照片在视图中,它也会坚持表演。就算一半的照片已经消失,它仍然会继续,直到完全离开舞台。

contain:完美主义者的福音
如果你希望动画在图像完全可见后才开始,并且在它开始退出前就结束,那么contain
就是为你量身定制的!
css
img {
animation: slideIn;
animation-timeline: view();
animation-range: contain;
}
让我们看看这是什么效果:

在这里,动画不会在第一个像素出现时就急着开始------它会等到完全可见后才优雅登场。当你的图像到达顶部并且第一个像素开始消失时,动画就会完美谢幕。
Entry:只在入场时表演
如果你希望整个动画,包括开始和结束,都只在照片进入视口时发生呢?也就是说,当第一个像素出现在屏幕上时,动画开始;当最后一个像素完全进入视口时,动画结束。这就是entry
的拿手好戏!
css
img {
animation: slideIn;
animation-timeline: view();
animation-range: entry;
}
效果如下:

Entry-crossing(vs. Entry):高个子的特殊待遇
还有一个与entry相关的范围叫做entry-crossing
。它与entry
的工作方式类似,但有一个关键区别。entry
在图像的第一个像素进入视口时开始动画(标记为时间轴的0%),当最后一个像素完全进入视口时结束动画(标记为时间轴的100%)。
但是当图像的高度大于视口的高度时会发生什么?我们时间轴的结束点(100%),是设置为图像首次填满视口的那一刻(即使还有部分隐藏)?还是要等到最后一个像素完全进入视口?
这时候,entry
和entry-crossing
就派上用场了:
-
如果你选择
entry
,当图像填满视口时,就到达了时间轴的末尾(100%)并结束动画。 效果如下: -
如果你选择
entry-crossing
,100%会被设置为图像中的最后一个像素穿过入口并进入视口的时刻:cssimg { animation: slideIn; animation-timeline: view(); animation-range: entry-crossing; }
效果如下:
Exit:退场也要有仪式感
exit
遵循与entry相同的理念,但它不是在图像进入视口时设置0%和100%,而是在图像退出视口时设置。
效果如下:

Exit-crossing(vs. exit):高个子退场的讲究
exit-crossing
与entry-crossing
的理念相同。当图像的高度大于视口时,exit-crossing
和exit
之间的区别最容易理解,让我们看个例子:
-
当你对高度大于视口的图像使用
exit
时,0%设置为最后一个像素进入视口的时刻,100%设置为最后一个像素消失、离开视口的时刻:cssimg { animation: slideIn; animation-timeline: view(); animation-range: exit; }
效果如下:
-
而对于
exit-crossing
,0%设置为第一个像素开始退出视口、穿过视口边缘的时刻,100%设置为最后一个像素消失的时刻:cssimg { animation: slideIn; animation-timeline: view(); animation-range: exit-crossing; }
效果如下:
以上就是不同timeline-range-name
的全部介绍。它们让你能够像导演一样,精确控制动画开始和结束的每一个瞬间!
为了获得更多创意空间,你还可以混合搭配这些值。比如,如果你想在图像首次完全可见时开始动画,但希望动画继续直到最后一个像素离开,你可以这样做:
css
img {
animation: slideIn;
animation-timeline: view();
animation-range: contain exit;
}
你可能记得前面提到过animation-range
是一个简写属性,所以这里第一个值用于animation-range-start
,第二个值用于animation-range-end
。这样就能实现你想要的效果啦!
Length-percentage:自定义你的时间轴
如果你想更进一步定制动画,比如不想在图像完全可见之前开始时间轴(保持contain
),但希望动画在时间轴的中途(50%)开始,该怎么办?
这时候,你需要在animation-range
中明确设置<length-precentage>
值:
css
img {
animation: slideIn;
animation-timeline: view();
animation-range: contain 50% exit;
}
效果可能是这样的:

<length-precentage>
值类型可以接受百分比或任何单位的长度,为你的创意提供无限可能!
选项总结
如果你想完全掌控animation-range
的每个方面,你可以分别定义animation-range-start
和animation-range-end
,为每个属性声明一个timeline-range-name
和length-percentage
值。
让我们复习一下timeline-range-name
的所有可能值:
cover
:从第一个像素进入到最后一个像素离开contain
:从完全可见到开始退出entry
:从第一个像素进入到完全进入(或填满视口)entry-crossing
:从第一个像素进入到最后一个像素完全进入exit
:从完全进入到最后一个像素离开exit-crossing
:从第一个像素开始离开到最后一个像素离开
如果不声明任何<length-percentage>
值,它们默认为开始0%和结束100%。如果不声明timeline-range-name
,它将默认为开始entry
和结束exit
。
现在,轮到你发挥创意了!你会用滚动驱动动画做出什么炫酷效果呢?