【青训营】- 手动实现一个前端JS动画库

859 阅读5分钟

js动画函数封装

函数选型

image-20210827211919638

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);
            }
        });
    });
}

公式

image-20210827000322871

t时间,r距离,v速度

当时间恒定不变时,给时间加入一个特殊可变的参数,随着时间发生变化,那这个时间就变成一个可变的时间了(时间扭曲),通过时间的变化,改变了曲线的状态,随之改变了一个正常的线性运动

以下示例可以在这里看效果👉代码地址


下面就来使用我们上面封装好的函数

匀速运动

速度大小不变的运动

下图,横轴是时间t、纵轴是距离r

image-20210825231910008

  • 先定义了一个绘制动画的函数draw,通过修改css的transform来实现一个动画
  • time计算动画进度,传入一个timeFraction时间段

image-20210825231442509

重力

image-20210825231547858

  • 效果是从上往下落,通过改变translateY的值
  • timeFraction**2即为公式里t的2次方

image-20210825231555909

摩擦力(匀减速运动)

若一物体沿直线运动,且运动速度随着时间均匀减小,这个运动叫匀减速直线运动

image-20210825231835198

image-20210825231656360

平抛

image-20210825231735237

  • 对应到draw函数,就是要控制translate的两个属性X和Y
  • x轴匀速,y轴加速

image-20210825231751042

相关工作实践

开发资源

动画代码示例

codepen.io/

codesandbox.io/

设计网站

dribbble.com/

动画制作具一般都是UE、UI使用

  • 2D : Animate CC、After Effects
  • 3D :Cinema 4D、Blender、 Autodesk Maya

动画库资源

SVG

  • Snap.svg -现代SVG图形的JavaScript库。
  • Svg.js -用于操作和动画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

Lottie是可应用于Android, iOS,Web和Windows的库,通过Bodymovin解析AE动画,并导出可在移动端和web端渲染动画的json文件。

image-20210825234041123

【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无法实现,有些是因为性能问题,有些是没有做。比如:描边动画等。

动画的优化

  • 用户体验角度

    The Guide To CSS Animation: Principles and Examples

  • 性能角度

    页面渲染的一般过程为JS > CSS > 计算样式 > 布局 > 绘制 > 渲染层合并

    其中,Layout(重排)和Paint(重绘)是整个环节中最为耗时的两环,所以应该尽量避免这两个环节。从性能方面考虑,最理想的渲染流水线是没有布局和绘制环节的,只需要做渲染层的合并即可。

image-20210825234846277

怎么知道哪些CSS属性的改变会影响这两个环节?

下面就是各CSS属性与其影响的环节。可以到这个网站查看👉csstriggers.com

image-20210825234946797

在实际的应用里,比较简单的一些优化方式

  • 触发动画的开始不要用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

如果有一些元素不需要用到上述属性,但是需要触发硬件加速效果,可以使用一些小技巧来诱导浏览器开启硬件加速。

其他的优化方式

  • 算法优化
    • 线性函数代替真实计算
    • 几何模型优化
    • 碰撞检测优化
  • 内存/缓存优化
  • 离屏绘制

结语

字节青训营

本节课的讲师:字节电商前端—蒋翔

如以上有错误的地方,请在评论区中指出!


如果有收获的话,就留个鼓励一下吧!🍜