其实写本文的初衷只是想分享一个css新特性【
scroll-timeline
】,但是我想要体现出它的强大效果,此时正好想到了之前看到过钉钉的首页动画,就是滚动页面然后进行一系列动画的,所以我决定实现一下这个效果。(见上方动图,只是实现了大体效果,细节没有优化,各位看官多多担待)它是干什么用的呢?
一句话概括为:“用滚动区域的滚动动作驱动动画的进度”。
可能有些同学还不太懂,我再稍加解释。我们以往的animation都是以时间作为动画的时间线的,比如我们需要设置动画的总时长animation-duration
为 n
秒,那么动画在进行 n 秒后,就会处于 100% 的状态。
而当我们使用了scroll-timeline
之后,以滚动动作驱动动画的进度,那么就是我们在滚动区域滚到底时,动画就会处于100%状态,即滚动了百分之多少,动画就会进行百分之多少。还不明白可以体验下方的小demo
jcode
以前如果要实现这种效果,往往得监听 scroll
事件,用js动画实现,总体来说非常复杂,但是有了这个新特性之后,为css的创造性又提升了一个台阶。
1️⃣ 首先我们需要在滚动区域上定义一个滚动时间线,滚动时间线需要设置两个值:
scroll-timeline-name
: 命名的滚动进度时间线的名称;scroll-timeline-axis
: 以哪个方向的滚动条驱动动画,即滚动方向;可选的滚动方向有:
2️⃣ 然后在滚动区域内需要执行动画的元素上,将动画时间线设置为我们定义的滚动时间线的名称。设置完成后,该元素的动画进度就会变成通过滚动动作驱动。
经过我十几分钟的分析,将这个复杂的特效切割成了三部分:
分析结束后,开始动工✊。
首先创建一个宽高满屏的div,为了方便后面描述,我们叫这个div为【滚动盒子】
,并设置黑色背景。
然后添加一个子元素,设置高度大于100%,这样父元素就能滚动了,不然无法驱动动画执行,这里我随便设置了高度200%。
那么有动效的元素(
我暂且叫它【动效盒子】
)在滚动盒子中如何定位呢,总不能就在文档流中吧,那岂不是滚动区域滚动的时候,整个动效盒子也会被来回滚动,甚至滚出可视区域。这样显然是不行的,并且设置 absolute 绝对定位也是不行的,同样会跟随滚动。得出结论: 为了让动效盒子不在滚动盒子内滚动,只能考虑 fixed 固定定位和 sticky 粘性定位,两者都可以实现,但是 fixed 定位不太灵活,一旦修改滚动盒子的位置,就得跟着调整动效盒子的位置。
所以最终我给动效盒子设置宽高100%,并粘性定位于滚动盒子顶部
。
布局结构大致如下:
:root {
--mainHeight: 100vh;
}
#container {
position: relative;
height: var(--mainHeight);
overflow-x: hidden;
overflow-y: scroll;
background: #040506;
}
#stretcher {
height: calc(2 * var(--mainHeight));
}
main {
pointer-events: none;
position: sticky;
left: 0;
top: 0;
width: 100%;
height: var(--mainHeight);
overflow: hidden;
}
//滚动盒子
<div id="container">
//动效盒子
<main>
//这里面是实现动效的元素
</main>
//撑开父元素高度,让父元素可以滚动
<div id="stretcher"></div>
</div>
因为动效是 3D动效,所以我们先在动效盒子设置下3D效果和视距:
main{
transform-style: preserve-3d;
perspective: 500px;
}
然后就是设置lodo的动画了,运动轨迹很简单,translate3d向上向屏幕外移动就行了,因为logo在消失之后还会继续面板动效,所以我假定logo动效大概占总动效时长 2/3。将动画绑定了滚动时间线后,只需要观察在logo从顶部消失时,滚动条大概滚动了 2/3 即可,慢慢调整参数。
❗需要注意的是:因为logo是在快移动出屏幕时才逐渐变透明,所以需要单独设置个透明度变化的animation。
.logo {
position: absolute;
left: 50%;
bottom: 0;
z-index: 2;
transform: translateX(-50%);
animation-name: logo,logohide;
animation-fill-mode: forwards;
animation-timeline: --squareTimeline;
}
@keyframes logo {
100% {
transform: translate3d(-50%, -400px, 500px) scale(.6);
}
}
@keyframes logohide {
55%{
opacity: 1;
}
63%,100% {
opacity: .2;
}
}
色块动效和logo动效差不多,也是 translate3d 的变化。因为他们作为一个整体在运动,新建个色块盒子,所有色块都扔里面设置绝对定位,通过 translateZ 呈现3d效果。
.blocks {
position: absolute;
top: 0;
left: 50%;
z-index: 0;
transform: translateX(-50%);
width: 100%;
height: 100%;
animation-name: block;
animation-fill-mode: forwards;
animation-timeline: --homeTimeline;
transform-style: preserve-3d;
perspective: 500px;
}
.blocks span {
--color: red;
--left: 10%;
--top: 10%;
--z: 0;
position: absolute;
left: var(--left);
top: var(--top);
width: 50px;
height: 50px;
border-radius: 10px;
background: var(--color);
transform: translateZ(var(--z));
}
@keyframes block {
100% {
transform: translate3d(-50%, -500px, 300px);
width: 270%;
}
}
<!-- 色块部分 -->
<div class="blocks">
<!-- 左色块 -->
<span style="--color:#261758;--left: 26%;--top: 40%;--z: 120px"></span>
<span style="--color:#c8940b;--left: 10%;--top: 60%;--z: -50px"></span>
<span style="--color:#2059a3;--left: 15%;--top: 100%;--z: -200px"></span>
<span style="--color:#562c0e;--left: 5%;--top: 90%;--z: -60px"></span>
<!-- 上色块 -->
<span style="--color:#051235;--left: 50%;--top: 40%;--z: 20px"></span>
<span style="--color:#17113a;--left: 40%;--top: 30%;--z: -40px"></span>
<span style="--color:#06143a;--left: 45%;--top: 70%;--z: -250px"></span>
<!-- 右色块 -->
<span style="--color:#072e90;--left: 70%;--top: 40%;--z: 100px"></span>
<span style="--color:#0ca15b;--left: 80%;--top: 60%;--z: -50px"></span>
<span style="--color:#479ec8;--left: 76%;--top: 70%;--z: 50px"></span>
<!-- 下色块 -->
<span style="--color:#072e90;--left: 75%;--top: 150%;--z: -300px"></span>
<span style="--color:#061640;--left: 30%;--top: 125%;--z: -200px"></span>
</div>
</main>
<div id="stretcher"></div>
面板动效是最复杂的,整体呈现为从中心点扩散成列表模式。逆向思维一下,其实这个动画的实质是先设置成列表形式,然后在动画 0% 时将所有元素移动缩小到中心点。
麻烦点来了,怎么确定每个元素移动的距离就能移动到面板中心点呢?我想了个办法,我先在面板中心显示了一个小点,如下:
然后在控制台不断设置元素的 translte 移动距离,直至和小点重合(先把元素scale成一个小点)。这样就可以很轻松获取到每个元素位移到中心点的距离。(ps:因为每行有7个元素,其实只需要调试获取到第一行的前四个元素的tanslate距离就行,第一行后面的三个就是把前面三个点x改为负值,第二行则负复制第一行,把y全部改为负值)
可以看到,已经成功实现了图标从中心点扩散,但似乎叮叮的图标像是一个一个出来的,所以还需要改进。
观察叮叮的面板动效得知,图标是从最外侧的两列开始出现,然后再是里面的两列,以此类推。
简单,只需要设置动画延迟就行。但是被绑定滚动时间线的动画无法设置 animation-delay
动画延迟,那只能在动画内容里下文章,1,7列是45%开始移动,2,6列是50%开始移动,3,5列55%开始移动,最中心第4列60%开始移动。
面板背景色显示逻辑差不多,在动画70%时再开始设置背景色。
emmmm,差不多了,就这样吧~ 完结撒花
点击码上掘金右上角“查看详情” 滚动鼠标滚轮查看效果。只是实现了大体动效,没有调整细节,多调调参数应该可以做到和钉钉首页99%相似度。
钉钉应该是用js监听滚动实现的这个复杂效果,可以看到,当我们使用css新特性
scroll-timeline
之后,这个动效完全就可以用纯css实现了,简单了很多
jcode
我是喜欢归纳总结前端相关知识的前端阿彬,尽力持续输出原创优质文章,欢迎点赞关注
往期文章:
# CSS魔术师Houdini,用浏览器引擎实现高级CSS效果
# ☕ 通过和vue语法逐一比对,快速上手前端框架黑马svelte
# ♀️css魔法:伪元素content ➕ css函数
# 玩转css逐帧动画,纯css让哥哥动起来
# ⛳前端进阶:SEO 全方位解决方案
# 我给自己搭建的前端导航网站,你们都别用
# 2023 最新最细 vite+vue3+ts 多页面项目架构,建议收藏备用!
# 浅谈 强制缓存/协商缓存 怎么用?
# 2023 前端性能优化清单