js动画函数封装
函数选型
JavaScript动画应该通过requestAnimationFrame
因为该内置方法允许设置回调函数
以在浏览器准备重绘时运行。通常这很快,但确切的时间取决于浏览器。当页面在后台时,根本没有重绘,所以回调不会运行:动画将被暂停,并且不会消耗资源。
requestAnimationFrame是浏览器用于定时循环操作的一个接口,主要用途是按帧对网页进行重绘,类似于setTimeout,只是不需要设置时间间隔而已。
- requestAnimationFrame使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。
- 该回调函数会被传入
DOMHighResTimeStamp
参数,该参数与performance.now()
的返回值相同,它表示requestAnimationFrame()
开始去执行回调函数的时刻。 - 默认情况下,requestAnimationFrame执行频率是1000/60,即1秒钟60次(大约每16.7毫秒一次))
函数实现
参数说明:
- timing - 计算动画进度的功能。获取从0到1的时间分数,返回动画进度,通常是从0到1。
- duration - 以毫秒为单位的总动画时间。(动画打算执行的时间)
- draw - 绘制动画的函数(就是绘制动画的方式,可以是css,js,svg)。
function animate ({timing, draw, duration}) {
//performance.now()和其他时间函数(如Date.now())的区别,performance.now()会以恒定速度自增,精确到微妙级别,不易被篡改。
let start = performance.now();
return new Promise(resolve => {
requestAnimationFrame(function animate(time) {
//timeFraction为时间段,值为0-1之间 (timeFraction goes from 0 to 1)
let timeFraction = (time - start) / duration;
if (timeFraction > 1) timeFraction = 1;
// 计算当前的动画状态(calculate the current animation state)
let progress = timing(timeFraction)
draw(progress); //绘制(draw it)
if (timeFraction < 1) {
requestAnimationFrame(animate);
} else {
resolve(timing);
}
});
});
}
公式
t时间,r距离,v速度
当时间恒定不变时,给时间加入一个特殊可变的参数,随着时间发生变化,那这个时间就变成一个可变的时间了(时间扭曲),通过时间的变化,改变了曲线的状态,随之改变了一个正常的线性运动
以下示例可以在这里看效果👉代码地址
下面就来使用我们上面封装好的函数
匀速运动
速度大小不变的运动
下图,横轴是时间t、纵轴是距离r
- 先定义了一个绘制动画的函数
draw
,通过修改css的transform来实现一个动画 - time计算动画进度,传入一个timeFraction时间段
重力
- 效果是从上往下落,通过改变translateY的值
- timeFraction**2即为公式里t的2次方
摩擦力(匀减速运动)
若一物体沿直线运动,且运动速度随着时间均匀减小,这个运动叫匀减速直线运动
平抛
- 对应到draw函数,就是要控制translate的两个属性X和Y
- x轴匀速,y轴加速
相关工作实践
开发资源
动画代码示例
设计网站
动画制作具一般都是UE、UI使用
- 2D : Animate CC、After Effects
- 3D :Cinema 4D、Blender、 Autodesk Maya
动画库资源
SVG
js
- GSAP - JavaScript动画库。
- TweenJS -一个简单但功能强大的JavaScript补间/动画库。CreateJS 库套件的一部分 。
- Velocit.js .-加速的JavaScript动画。
CSS
Animate.css - CSS动画的跨浏览器库。像一件简单的事情一样容易使用
canvas
- EaselJS - EaselJS是一个用于在HTML5中构建高性能交互式2D内容的库。
- Fabric.js -支持动画的JavaScript画布库。
- Paper.js -矢量图形脚本的瑞士军刀- Scriptographer使用HTML5,Canvas移植到JavaScript和浏览器。
- Pixijs -使用最快、最灵活的2DWebGL渲染器创建精美的数字内容。
工作实践
当接到一个新的动画需求时,过程大致是这样的👉【动画帧->代码 & 设计文件>代码的转换】
其中可能会有这几种情况
- 需要完全前端自己开发👉(可以使用已经封装好的动画库,从开发成本和体验角度出发进行取舍。)
- 设计不是很有空👉(清晰度,图片格式可以指定,动画尽量给出示意或者相似案例参考。索要精灵资源、资源等需要帮忙压缩。)
- 设计资源充足👉(要求设计导出lottie格式文件)。
Lottie是可应用于Android, iOS,Web和Windows的库,通过Bodymovin解析AE动画,并导出可在移动端和web端渲染动画的json文件。
【lottie优点】
Lottie方法方案是由设计师出动画,导出为json,给前端播放。所以,使用Lottie方案的好处在于
- 动画由设计使用专业的动画制作工具Adobe After Effects来实现,使动画实现更加方便,动画效果也更好;
- 前端可以方便的调用动画,并对动画进行控制,减少前端动画工作量;
- 设计制作动画,前端展现动画,专业人做专业事,分工合理;
- 卖家秀即买家秀,还原程度百分之百;
- 使用lottie方案,json文件大小会比gif文件小很多,性能也会更好。
【lottie不足】
-
lottie-web文件本身仍然比较大,lottie.js大小为513k,轻量版压缩后也有144k,经过gzip后,大小为39k。所以,需要注意lottie-web的加载。目前H5项目有离线包,PC项目也会上PWA,会对其进行缓存,保证加载速度。
-
lottie动画其实可以理解为svg动画/canvas动画,不能给已存在的html添加动画效果;
-
动画json文件的导出,目前是将AE里面的参数一一导出成json内容,如果设计师建了很多的图层,可能仍然有json文件比较大(20kb)的问题。需要设计师遵循一定的规范。
-
有很少量的AE动画效果,lottie无法实现,有些是因为性能问题,有些是没有做。比如:描边动画等。
动画的优化
-
用户体验角度
-
性能角度
页面渲染的一般过程为
JS > CSS > 计算样式 > 布局 > 绘制 > 渲染层合并
。其中,Layout(重排)和Paint(重绘)是整个环节中最为耗时的两环,所以应该尽量避免这两个环节。从性能方面考虑,最理想的渲染流水线是没有布局和绘制环节的,只需要做渲染层的合并即可。
怎么知道哪些CSS属性的改变会影响这两个环节?
下面就是各CSS属性与其影响的环节。可以到这个网站查看👉csstriggers.com
在实际的应用里,比较简单的一些优化方式
-
触发动画的开始不要用display:none属性值。(因为它会引起Layout、Paint环节, 通过切换类名就已经是一种很好的办法。)
-
translate属性值来替换top/left/right/bottom的切换,scale属性值替换width/height, opacity属性替换display/vibility等等。
CSS3硬件加速又叫做GPU加速,是利用GPU进行渲染,减少CPU操作的一种优化方案。由于GPU中的transform等CSS属性不会触发repaint,所以能大大提高网的性能。
CSS中的以下几个属性能触发硬件加速
- transform
- opacity
- filter
- Will-change
如果有一些元素不需要用到上述属性,但是需要触发硬件加速效果,可以使用一些小技巧来诱导浏览器开启硬件加速。
其他的优化方式
- 算法优化
- 线性函数代替真实计算
- 几何模型优化
- 碰撞检测优化
- 内存/缓存优化
- 离屏绘制
结语
本节课的讲师:字节电商前端—蒋翔
如以上有错误的地方,请在评论区中指出!
如果有收获的话,就留个赞鼓励一下吧!🍜