cover
2022年9月28日

嘿,朋友,其实 CSS 动画超简单的 - 渐变动画篇

经常遇到有同学看到动画时会疑问动画是怎么做出来的,或是为什么自己写不起作用,或是为什么和预想的不一样等问题。于是决定写一写关于 CSS 动画的系列文章,本篇为第一篇。

首先 CSS 动画可以分为两类:

  • 一类是渐变 transition 动画,一般用途是优化元素状态切换的效果等
  • 一类是 animation 动画,用途更为广泛,功能更多,但是也稍微更难学习

本文先主要讲一下渐变动画,animation 动画留到下篇。

常见使用场景

渐变动画在实际开发中有广泛运用,虽然他的功能没有 animation 那么强大,却胜在方便。常见的大部分非循环播放的动画都是由渐变动画来实现的,如:

  • 颜色主题切换时的平滑过渡效果
  • 图片预览时的放大缩小
  • 按钮等元素 hoveractive 时的颜色、阴影变化
  • 页面切换时的转场动画

等等,使用渐变动画即可轻松实现,实际使用中和 animation 动画的使用场景边界一般还算比较清晰,如果不清楚就按照 渐变动画能实现的就不使用 animation 动画 即可。

渐变动画两大要素

渐变动画其实很简单,主要是元素的属性变化时,定义 CSS 让浏览器使用开发者定义的渐变效果来实现属性的变化,让浏览器平滑的变化属性从而产生动画效果。

所以产生渐变动画的两个必备要素:

  • 一个是某种原因导致的属性的变化
  • 一个是渐变属性 transition 的声明

属性变化

这里要注意属性变化这个要素,之前有看到有同学把掘金个人主页的头像旋转彩蛋代码直接复制结果看不到旋转的就是这个原因了。

属性变化指的是 CSS 规则变动导致某个属性值产生变化时,注意值一定要产生变化,如果值从 nn 是不会产生动画的。

常见的属性变化包括但不限于:

  • width,height,padding,margin,font-size,border-width,left,right,top,bottom 等尺寸
  • color,background-color,border-color 等颜色
  • border-radius,box-shadow 等较为特殊的属性
  • transform 的各种旋转、偏移等

以上值的变化就是产生渐变动画的一大重要要素。

而要让值产生变化有很多种方法,包括但不限于:

  • 使用伪类,在元素状态变化时属性产生变化,如 hover 时字体变大变蓝
  • 使用 js 更改元素的类名
  • 或直接使用 js 更改元素的对应属性

渐变属性声明

transition 属性包含 4 个子属性:

  • transition-delay:产生渐变动画时延迟,属性变化后不会立刻开始动画,而是会等待一定时间后再进行,默认值为 0s
  • transition-duration:产生的渐变动画的时长,默认值为 0s
  • transition-property:声明哪些属性的变化需要使用渐变动画,默认值为 all,即所有属性均会触发
  • transition-timing-function:渐变动画的时间函数,可以使用时间函数来达到一些让人眼前一亮的效果,如慢慢加速、超出回弹等效果,默认值为 ease

而要产生渐变动画,唯一必要的属性就是 transition-duration。所以一个最简单的 transition 规则只需要:transition: 1s

而编写 transition 规则时需要注意属性顺序,需要按照 property, duration, timing-function, delay 的顺序。

此外 transition 还支持声明多组规则,可以针对不同的属性设置不同的 delaytiming-functionduration,如:transition: color 2s, width 1s; 可以让颜色动画更缓慢,宽度动画更迅速。

实战解读掘金彩蛋

我们再借助上面讲到的知识来讲解一下掘金的个人主页头像彩蛋实现。

.user-info-block .avatar[data-v-8ed09ee0]:hover {
    transform: rotate(666turn);
    transition-delay: 1s;
    transition-property: all;
    transition-duration: 59s;
    transition-timing-function: cubic-bezier(0.34, 0, 0.84, 1);
}

其实核心代码就这么一段,当 hover 时,将会触发 transform rotate 的变化,然后浏览器检测到 transition 属性的定义,达到形成渐变动画的两大要素。

从上面的代码可以看出来:

  • 彩蛋需要 hover 1s 以后才会触发,所以很多同学一直没发现
  • 彩蛋其实是在 59s 时间里让头像旋转 666 圈,并不会一直旋转下去,只是一般人没这么无聊(没错我就是那个很无聊的人),不会等他转那么久可能很难发现
  • 而鼠标移出后,由于 transition 声明都是放在 hover 中,所以此时无法构成渐变动画需要的要素,头像会立刻恢复原状,而不会使用渐变动画恢复
  • 借助 Chrome 查看 timing-function 曲线,可以看出,动画前期曲线上升较慢,然后逐渐变快,这也是彩蛋中头像旋转逐渐加速的原因。

时间曲线后面会专门写一篇来讲解,有兴趣的可以关注下。

小技巧

  • 借助 transition-delay 可以适当延时渐变动画,避免如用户移动鼠标时频繁触发路径中元素的 hover 渐变动画,一定程度上避免不必要的性能损耗
  • 可以分别为两个状态设置单独的 transition 属性,比如当展开时慢慢展开达到更好的效果,但是关闭时快速的关闭让用户感觉操作更顺滑
  • 使用时间函数可以达到很多效果,如上面提到的动画慢慢加速,甚至可以做到超出回弹的效果

注意点

需要注意的是,动画虽香,却要谨慎使用,对于会引起重排的属性一定要慎重,如 widthheight 等,在动画过程中,浏览器会需要一直重排,会比较耗费资源,可以采用 BFC 包裹等方式来减少重排影响范围。

需要注意的是 transition 动画由于依赖于状态,而常见的 hover 状态很容易因为动画引起状态丢失,导致动画中断甚至抖动,此时最简单的方案一般是直接在动画元素外层套上一层容器,然后将 hover 伪类挂到外层父容器,这样即可避免状态的丢失。此问题算是比较常见的一类问题,站内前段时间看到几篇文章中的动画就存在这样的问题,如 y 轴旋转后鼠标 hover 丢失导致一直在旋转抖动。

总结

借助渐变动画可以让我们的页面中的各种切换、状态变化更流畅,获得更好的用户体验。并且渐变动画使用简单,开发过程中随手就可以加上,性价比超高的,赶快用起来吧。