随着时间的演变,web动画的世界已经拥有了许多的工具和技术,如GSAP、Framer Motion、React Spring等库的涌现,能够帮助开发者们实现各种栩栩如生的动画。但是对于前端开发者来说,我们接触到第一个实现动画的工具,仍然是最基础已经最关键的CSS属性——transition,哪怕是资深的开发,也会经常去使用它。本文将详细的去了解CSS transition。
基础使用
首先我们展示两个最终效果一致的示例:我们通过:hover伪类,实现鼠标移动到按钮上,按钮位置向上移动20px。
示例1,无过渡属性的效果:
默认情况下,css的变化是即时发生的,按钮位置瞬间上移。
再看看示例2,添加过渡属性的效果:
通过两个示例对比可以看到,添加了过渡属性的效果,按钮位置逐渐上移,动作明显协调很多,而这只需要添加一行简单的css,就可以实现这样的效果。
transition 可以接收多个值,但是有两个值是必须要设置的:
- 我们想要过渡动画的属性值
- 过渡动画的持续时间
如果你打算给多个属性设置动画,你可以传递一个逗号分隔的列表:
.btn {
transition: transform 250ms, opacity 400ms;
}
.btn:hover {
transform: scale(1.2);
opacity: 0;
}
时间函数
当我们想要一个元素从一个位置过渡到另一个位置时,浏览器需要计算出每个“中间”位置应该是什么样子。假设我们将一个元素从左向右移动,持续时间为1秒。一个流畅的动画应该以60fps的帧率运行,这意味着我们需要在开始和结束之间分隔出60帧画面。下面我们就来看看不同时间函数的运动示意图:
1. linear 线性匀速的运动
下图是线性运动的示意图:
这个图有些抽象,每个浅色的圆圈代表着之前每一帧圆圈的位置,因为我们是匀速的运动,我们可以看到每帧当中,圆圈向前移动的位置是一样的。用时间函数来表示的话,就是下面一条直线:
css代码可以这样写:
.btn {
transition: transform 250ms;
transition-timing-function: linear;
}
或者简写成:
.btn {
transition: transform 250ms linear;
}
2. ease-out 慢速结束的运动
ease-out 开始运动速度快,然后慢慢减速直至动画结束。下面是一个运动的示意图:
在运动的示意图中,我们可以看到,刚开始速度很快,所以圆圈每一帧运动的的距离比较长,后面速度变慢,圆圈每一帧运动的距离就短了,用函数来表示圆圈的位置,它会是下图这个样子:
什么时候使用ease-out呢?它一般用于从屏幕外输入的东西(例如:模态出现)。它产生的效果是,有东西从远处匆匆而来,然后在用户面前慢慢停下来。
3. ease-in 慢速开始的运动
ease-in 和 ease-out 正好相反,它是开始很慢,但是结束很快。下面是运动的示意图:
正如我们所看到的,ease-out对于从屏幕外进入视图的场景很有用,那么ease-in对于相反的情况是有用的:将某些内容移出视口的范围。
下图用函数来表示 ease-in 运动时,元素的位置:
4. ease-in-out 慢速开始、慢速结束的运动
ease-in-out 是前两种运动时间函数的结合,慢速开始,加速,然后慢速结束。
这个时间函数式对称的,它又相等的速度进行加速和减速:
ease-in-out 对于一些反复循环的动画场景还是很有用的。
5. ease(transition 属性默认值) 慢速开始逐渐变快然后慢速结束
ease 和 ease-in-out类似,加速,然后再减速,但是又有不同,它是不对称的加速减速,开始进行短暂的加速然后慢慢进行减速直到结束。
从运动示意图上可以看到,ease 的加速过程比ease-in-out 要更快。
ease是默认值——如果我们没有指定计时函数,则使用ease,ease的时间函数图如下:
6. cubic-bezier 贝塞尔曲线
前面五种时间函数已经满足大部分的使用场景,如果遇到不满足的情况,我们也可以通过 cubic-bezier 来实现自定义的时间函数,以满足需求。
.btn {
transition:
transform 250ms cubic-bezier(0.1, 0.2, 0.3, 0.4);
}
cubic-bezier有4个数值,代表2个控制点。下面是前五个值的贝塞尔函数定义:
.btn {
/* ease-out */
transition-timing-function:
cubic-bezier(0.215, 0.61, 0.355, 1);
/* ease-in */
transition-timing-function:
cubic-bezier(0.75, 0, 1, 1);
/* ease-in-out */
transition-timing-function:
cubic-bezier(0.645, 0.045, 0.355, 1);
/* ease */
transition-timing-function:
cubic-bezier(0.44, 0.21, 0, 1);
}
我们可以通过工具网站 cubic-bezier.com 来帮助我们生成自定义的贝塞尔曲线,来实现我们想要的过渡效果。
我们也可以通过工具网站 Easing Functions Cheat Sheet (easings.net) 来进行选择,这个网站包含了许多自定义的时间函数。
动画性能
在前文中,我们提到动画以60fps运行,意味着浏览器每16.6毫秒就要绘制一帧画面,这不是一个很长的时间,而我们人眨眼的时间大约需要100毫秒到300毫秒!如果浏览器计算的时间太长,动画就会显得卡顿,出现掉帧的情况。
动画领域是一个深刻有趣的领域,这里不展开赘述,下面是关于CSS动画的一些关键部分:
- 有些CSS属性的动画制作成本要比其他属性高得多。例如,高度是一个非常昂贵的属性,因为它会影响布局。当一个元素的高度变化时,它会引起连锁反应,它后面的兄弟元素也需要进行移动,以填补空间!
- 其他属性(如background-color)的动画制作成本有些高。虽然它们不影响布局,但它们需要在每祯画面上修改新的颜色,这也不是一个消耗小的操作。
- tansform和opacity两个属性是消耗非常小的动画。如果一个动画效果是调整元素的宽度或左侧移动距离等属性,则可以通过transform来移动它(尽管并不总是可能达到完全相同的效果)来极大地优化动画性能。
- 由于我们的开发机器可能比它快很多倍,所以一定要在你的网站/应用的低端目标设备上测试你的动画性能。
硬件加速
在前面的一些例子中,由于浏览器和操作系统的不同,可能有一个奇怪的小缺陷:
注意观察按钮中的字母,它们在移动开始和结束时出现了轻微的偏移。这是因为计算机的CPU和GPU之间切换的原因。当我们使用transform和opactiy来做元素的动画时,浏览器有时会尝试优化这个动画效果。它不是在每一帧画面上重置像素,而是将所有内容作为纹理传输到GPU,GPU非常擅长做这种基于纹理的转换。因此,我们得到了一个非常光滑、非常高性能的动画。这就是所谓的“硬件加速”。这里有一个问题:GPU和CPU渲染的内容略有不同。当一方交给另一方时,你就会看到事情发生了轻微的变化。
硬件加速因此也叫 GPU 加速,是利用 GPU 进行渲染,减少 CPU 操作的一种优化方案。由于 GPU 中的 transform 等 CSS 属性不会触发重绘,所以能大大提高网页的性能。
我们可以通过设置下面这个属性来解决这个问题:
.btn {
will-change: transform;
}
will-change属性将提示浏览器,我们将动画化所选的元素,它应该针对这种情况进行优化。实际上,这意味着浏览器会让GPU一直处理这个元素,没有CPU和GPU之间的交换处理,没有更多的“快速到位”。所以will-change让我们能够自己决定哪些元素应该被硬件加速。
硬件加速还有一个好处,可以让我们进行子像素渲染(sub-pixel rendering)。下面我们来通过一个示例来对比有无硬件加速的区别:
仔细观察一下动画效果,左边盒子的移动效果比另一个要流畅得多。(如果没有观察到,可以适当降低浏览器的性能来观察)。像margin-top这样的属性不能进行子像素渲染,这意味着它们需要四舍五入到最接近的像素,从而产生一种阶梯式的效果。但是,transform可以平滑地在像素之间转换,这要归功于GPU的anti-aliasing(抗锯齿) trickery。
注意:天下没有免费的午餐。硬件加速一样,浏览器将元素的渲染委托给GPU,将会消耗更多的视频内存,尤其在低端移动设备上,它一种有限的资源。所以我们也不能滥用硬件加速。
硬件加速不是一个很新的特性,之前很长的一段时间里,它是通过使用3D变换来完成的,比如transform: translateZ(0px)。即使是0px的值,浏览器仍然会把它交给GPU来处理,因为在3D空间中移动绝对是GPU的强项。will-change的出现,为开发人员提供一种适当的、语义化的方式来提示浏览器应该优化某个元素。不过在早期,will-change有一些bug。令人高兴的是,这些问题现在都得到了解决,在现代浏览器中我们使用will-change属性可以获得最好的视觉效果。
transition 补充说明
1. 设置动画延迟
正常情况下,过渡动画都是立即触发的,但是某些情况,我们可能需要延迟短暂的时间之后再开始动画。我们可以通过transition-delay 属性来设置延迟时间:
.btn {
transition-delay: 300ms;
}
过渡延迟可以让我们短暂的保持元素状态,当时间过了之后,才开始进行变化。
2. 动画闪烁问题
有时候,我们会出现动画闪烁的问题,如下图展示:
当鼠标靠近元素的边界时,悬停效果将元素从鼠标下方移出,这将导致元素落回鼠标下方,这将导致悬停效果再次被触发,每秒多次,最后就出现了这样诡异的动画效果。为了解决这样的问题,我们可以尝试添加一个子元素,把触发悬停效果的元素和触发位移的元素分开,就可以避免这样的情况。如示例,我们给button添加了一个span子元素:
3. 取消设置动画
如果我们不想要动画效果时,可以通过下面的css样式来取消:
.btn {
transition: none;
}
4. 属性简写
前文中我们介绍了transition、transition-timing-function、transition-delay属性,这些都可以简写在transition属性中来:
.box{
transition-property: width;
transition-duration: .5s;
transition-timing-function:linear;
transition-delay: 300ms;
}
// 属性缩写
.box{
transition: width .5s linear 300ms;
}
总结
CSS transition动画是最基础的动画,可以很方便的用来制作一些简单实用的动画。虽然它已经出现了不短的一段时间,但是有时间来回顾一下也是一个不错的选择。
文章中如有错误,敬请指正!