基于 css 的滚动和视图驱动动画:animation-timeline

647 阅读12分钟

每日思考:每个人都在玩叠房子的游戏,扔房子的每一步都会有些偏差,误差的累积一定有,等到了某个临界点,房子一定会坍塌,我们只能确保走的每一步尽可能扎实,等到误差积累到临界值的时候坍塌的房子不会太多,不至于整栋房子都坍塌。所以低谷一定会有,我们要做的只有两件事,减少房子的坍塌,以及如何快速从坍塌后重新建立新房子。来自职场访谈:其实你可以不用那么着急「重启人生」 【刘同x姜Dora】

如果对你有帮助的话,别忘了点个赞哦✨

本质作用

将原本 基于动画时间控制的动画效果交给滚动或者视图的驱动来控制,不再由动画时间控制动画效果。页面滚动多少,动画执行多少,元素在视图中的交叉占比多少,动画执行多少。

应用场景

广泛场景

几乎以前任何需要 JS 监听滚动或者监听视图和元素交叉的交互都可以由纯 CSS 实现。

具体场景

  • 页面顶部进度条:进度条随着页面滚动的进度(页面滚动进度可以是0-100%,也可以0-40%)而改变,无需再借助js监听页面滚动(js监听滚动会异步执行滚动回调,容易存在页面动画卡顿的问题)。
  • 滚动吸顶效果:页面的一个动画元素随着滚动容器滚动而滚动,期间不断执行某种动画,直到该动画元素滚动一部分距离(可以通过animation-range实现,具体使用见后文~~)之后就吸顶在滚动容器上,这时候动画完成度100%,动画就停止啦。
  • 滚动路径动画:由页面滚动驱动路径动画,如 www.cnblogs.com/coco1s/p/17…

驱动分类

无滚动或者视图驱动,传统 css 动画

1.animation-timeline:auto | none,表示不设置任何滚动驱动

值1:none

该动画与时间轴不关联, 和auto一样的效果。

值2: auto

表示默认文档时间轴,该时间轴随时间推移而前进。这是传统上与CSS动画相关联的时间轴,也就是不采用任何滚动或者视图驱动,由动画的动画时间控制动画从文档加载开始就自动完成动画。

滚动驱动

通过在设定的滚动容器上的顶部和底部(或左侧和右侧)之间滚动可滚动元素(滚动条)来进行。滚动范围中的位置将转换为进度百分比。

1.animation-timeline: scroll(scroller,dirction) | 自定义的--timeline_name(在定义了动画的元素上可以设置):表示用于控制CSS动画进度的时间轴,意思是该动画元素的动画效果的时间控制权交给谁,值为希望控制这个元素动画效果的控制方式(scroll还是view)以及 控制容器(root还是其他元素)。作用是将滚动进度或者视图交叉进度映射到动画进度上

值1:scroll(scroller,dirction):

  • scroller可以为 root,代码表示为 scroll(root) ,表示动画元素的动画效果交给 body 元素的滚动进行控制
  • scroller 可以为 self,代码表示为scroll(self),表示动画元素的动画效果交给元素本身的滚动进行控制
  • scroller 可以为 nearest,代码表示为 scroll(nearest),表示动画元素的动画效果交给最近的祖先滚动容器的滚动进行控制
  • scolller 可以为空,表示默认值为 nearest,代码表示为 scroll(),动画元素的动画效果交给最近的祖先滚动容器的滚动进行控制

  • dirction 可以为 block,表示滚动容器的 块级轴方向的滚动 控制动画元素的动画效果
  • dirction 可以为 inline,表示滚动容器的 内联轴方向的滚动 控制动画元素的动画效果
  • dirction 可以为 y,表示滚动容器的 y 方向的滚动 控制动画元素的动画效果
  • dirction 可以为 x,表示滚动容器的 x 方向的滚动 控制动画元素的动画效果
  • dirction 可以为默认表示block方向,表示滚动容器的块级轴方向的滚动控制动画元素的动画效果

💡💡💡 scroller 和 dirction 的定义顺序没有严格要求,不论是scroll( scroller,dirction)还是 scroll(direciton,scroller)的顺序都可以被识别

值2:一个自定义的--timelinename,--timelinename的具体名称可以由开发人员自定义,标识以前用scroll-timeline-name 或 view-timeline-name属性(或scroll-timeline或view-timeline速记属性)声明的命名时间轴。如果没有找到与给定名称匹配的时间轴,则动画不与时间轴相关联。如果两个或多个时间线共享相同的名称,则将使用级联中最后声明的时间线

比如可以将animation-timeline自定义成--my-scroller,作用是手动指定哪个容器元素的滚动来控制本动画元素的动画效果,要使用自定义名称的animation-timeline,除了在动画元素上 .animation 设置animation-timeline: --my-scroller;还需要在我们希望选定的滚动容器 .scroller 上设置如下代码

.scroller {
  scroll-timeline-name: --my-scroller;//标记滚动容器
  scroll-timeline-axis: inline;//设置容器的哪个滚动方向控制动画元素的动画效果

   -------------------------------------------------

  // scroll-timeline-name和 scroll-timeline-axis可以简写成:
  scroll-timeline: --my-scroller inline;
}

.animation {
  animation: animate linear;//定义动画元素的动画
  animation-timeline: --my-scroller;//定义本动画元素的动画效果将要被哪个滚动容器的滚动效果控制

}

2.animation-range(在定义了动画的元素上可以设置):作用是设置容器元素滚动的一部分范围(animation-range-startValue 到 animation-range-endValue)来映射整个动画(0-100%)

.back {

  animation: back-progress 1s linear forwards;

  animation-timeline: scroll();

  animation-range: 0 100px; 
  // 这样就实现了滚动容器从0px滚动100px,动画就从0-100%完全执行完毕
  // 如果不加这句话,该动画元素就要等到滚动容器从0滚动100%的时候,也就是容器从顶部滚动到底部的时候,动画才会从0100%执行完。

}
视图驱动

animation-timeline:view() | 自定义的--timeline_name(在定义了动画的元素上可以设置):动画的时间线,表示该动画元素的动画效果的控制方式(是视图控制吗?哪个视图容器控制?),作用是将元素的自身位置在视图中的展示进度映射到动画进度上

值1:view(dirction,inset)。和滚动驱动不同的是无法通过view()指定滚动的视图容器,如果只使用view()那么将基于动画元素在最近的父容器视图中的位置变化而映射动画元素的动画进度。

  • dirction可以为 block,表示动画元素在滚动容器的块级轴方向的视图出现和视图离开来控制动画元素的动画效果
  • dirction可以为 inline,表示动画元素在滚动容器的内联轴方向的视图出现和视图离开来控制动画元素的动画效果
  • dirction可以为 y,表示动画元素在滚动容器的 y 方向的视图出现和视图离开来控制动画元素的动画效果
  • dirction可以为 x,表示动画元素在滚动容器的 x 方向的视图出现和视图离开来控制动画元素的动画效果
  • dirction可以为空,默认表示block方向,表示动画元素在滚动容器的块级轴方向的视图出现和视图离开来控制动画元素的动画效果

  • inset为空,默认值就为auto,表示view(0%,100%),分别表示动画元素的动画结束和动画开始的时候对应的动画元素在视图元素的百分之多少的位置,0%代表动画元素刚好由于滚动完全从下到上离开了视图元素,100%表示动画元素由于视图滚动刚好将要从下到上将要进入视图元素,即动画元素将要进入视图元素前到完全离开视图元素后的过程进度映射到动画元素的动画效果上。

  • inset可以为单个或者两个固定或者不定宽度,如40px 100%,当动画元素从下(动画元素的top位置在视图的100%)到上滚动到视区上面 40px(动画元素的bottom位置到达视图的 40px 位置,视图位置从下到上为100%-0%)的时候就完成了动画(动画进度此时为100%)。

  • inset可以为单个或者两个不定宽度,如100% 100%,表示当动画元素从下到上滚动到视区上面 100%(元素的bottom位置到达视图的100%位置,视图位置从下到上为100%-0%)的时候就完成了动画(动画进度此时为100%)。也就是动画元素本身从下到上滑动刚要进入视图到本身刚好完全进入视图这一过程映射成动画0%-100%的进度。

  • inset可以为单个或者两个固定宽度,如100px 20px

  • inset功能还可以使用单独的css语句:view-timeline-inset单独来表示,这种用法要搭配命名的滚动视图容器的方法一起使用:


.animation {

view-timeline-name: --my-scroller;//将容器设置为自身,动画元素本身

animation-timeline: --my-scroller;//定义视图容器为--my-scroller

view-timeline-inset: 20% 200px;

}

值2:一个自定义的--timeline_name,--timeline_name的具体名称可以由开发人员自定义,标识以前 view-timeline-name属性(view-timeline速记属性)声明的命名时间轴。

比如可以将animation-timeline自定义成--my-scroller((这里的名字随便起什么都可以,只要和视图容器的名字一样就行)),作用手动指定动画元素在将要因为在哪个容器视图元素滚动,通过动画元素在该视图元素的视图占比来来控制本动画元素的动画效果。要使用自定义名称的animation-timeline,除了在动画元素上 .animation 设置animation-timeline: --my-scroller;还需要在我们希望选定的视图容器 .scroller 上设置如下代码


JavaScript .scroller { 

view-timeline: --my-scroller block; 

//将容器的名称设置为--my-scroller,容器的滚动方向为block

// 只有当滚动容器在block方向上滚动导致动画元素在block方向上与视图产生的交叉的变化会控制动画元素的动画效果。

// 如果同时指定了这两个值,则必须按顺序指定。

// 值必须以--开头。有助于避免与标准CSS关键字的名称冲突

view-timeline-inset: 20% 200px;

// 在命名视图容器的情况下,才可以使用view-timeline-inset 
// 设置动画元素在视图元素的一定范围内(从下往上200px-20%)的视图交叉变化映射成动画元素的动画进度(0-100%)
} 
.animation { 
animtion-timeline:--my-scroller;
// 定义视图容器为--my-scroller
}
  • animation-range(在定义了动画的元素上可以设置):

    animationa-range的值由6个关键字组成,其值可以是这6个关键词的两两组合,或者单个组合。

    • cover:不显示指定范围的话,默认是0%-100%,元素首次开始进入滚动容器可见范围(0%)到完全离开的过程(100% ),代表元素只需要和可视范围有交集,元素可以不完整的与视图有交叉

    • contain:不显示指定范围的话默认是0%-100%,元素完全进入滚动容器可见范围(0%)到刚好要离开的过程(100% ),代表整个元素必须完全在视图的可见范围才会触发

    • entry:元素进入滚动容器可见范围的过程,刚进入是 0%,完全进入是 100%,和view(100% 0)实现的效果相同。

    • exit:元素离开滚动容器可见范围的过程,刚离开是 0%,完全离开是 100%

    • entry-crossing:和entry比较类似,暂时没有发现明显差异

    • exit-crossing:和exit比较类似,暂时没有发现明显差异

    使用示例


JavaScript

-   animation-range: entry 10% 90%:
-   animation-range:cover;// 等价于cover 0% cover100%
-   animation-range: contain; // 等价于 contain 0% contain 100%
-   animation-range: cover 20%; // 等价于 cover 20% cover 100%
-   animation-range: entry exit;// 等价于 entry 0% exit 100%
-   animation-range: cover cover 200px; // 等价于 cover 0% cover 200px
-   animation-range: entry 10% exit;// 等价于 entry 10% exit 100%
-   animation-range: entry; // 只在进入过程中有效
-   animation-range: entry,exit; // 进入过程执行一个动画,离开过程执行另外一个动画,要求动画元素设置两个动画,另外,还可以将animation-range: entry,exit合并到同一个动画中,在关键帧前面加上entry这些关键词,这样就无需指定animation-range,相当于:

div{ 

animation: animate-in-and-out 1s linear forwards;

animation-timeline: view(); 

}

@keyframes animate-in-and-out { 

entry 0% {

opacity: 0; 

transform: scaleX(0);


}

entry 100% { 

opacity: 1;

transform: scaleX(1);

}

exit 100% { 

opacity: 0;

transform: scaleX(0);
}

exit 0% {

opacity: 1; 

transform: scaleX(1);

} 

}

支持情况

结论:animation-timeline 相关的 css 属性的浏览器支持情况基本一样,chrome、edge、opera、opera mobile、android browser、samsung internet 均已支持,其他浏览器如 firefox 目前还未正式支持(需要手动启动)

animation-timeline

image.png

animation-range

image.png

scroll-timeline-name

image.png

animation-timeline-axis

image.png

参考资源

最后,如果对你有帮助的话,别忘了点个赞哦✨✨