前情提要
因为项目需求,最近需要做一个动画效果。动画由两个部分构成:
- 一个是几张卡片中的关键字由不同位置移动到中心点;
- 其次在中心点开始播放序列帧动画,完成之后安静退场,一切仿佛没发生过一样。
实现步骤
因为用户众多,这个动画在移动端展现。制作之前其实并没想到移动端有很多的问题。
动画实现
文字效果
序列帧效果
之所以使用序列帧,是因为采用这个方法实现动画能很大的定制化你的动画,比如暂停,循环。
序列帧就是一张张的图片,往往是UE通过工具生成给你。之后我们需要将这些图片压缩,在转成雪碧图。
对于雪碧图我们一般都是通过background-position来实现精准的定位或者动画。
但是由于我们是一个正式的前端工程师了,在考虑解决方案的时候需要考虑性能以及可配置性。尤其是移动端需要考虑不同分辨率的适配,这时候使用 background-size的话,就需要根据rem的计算,来改变宽高的比例,显得特别的麻烦。
而我选择了新建一个父容器+图片节点,控制父容器的宽高(设置一定的宽高比,还有最小的宽高),将图片的高度设置成父容器的高度,宽度就是帧数*单图的宽度
,将父节点设置成绝对定位,使之成为一个复合图层,这样产生重绘的时候,影响的节点就比较少,提高性能。
图像通过新建一个Image对象
来实现预加载,因为序列帧的雪碧图往往都**>1m**,然后使用setInterval
改变图片节点的transform:translateX
来实现位置的变化。之所以使用CSS3的属性,是因为这样会触发浏览器的GPU加速,能减少浏览器重绘是CPU的压力。
在不考虑请求数量的时候,我们亦可以通过图片节点的增删来实现序列帧动画。DOM的结构和上面那种方法一样,实现的步骤:
- 预加载图片序列
- 通过定时器,循环图片序列,往容器中增删图片节点。 实现一种效果的方式很多,选择最适合你的方式(投入和产出)
动画衔接 & 进场退场
因为此动画是一个彩蛋,只在特定的场景引入,所以就涉及到动画的进场。进场机制是在页面所有资源加载完毕之后,通过外链的方式引入脚本,脚本需要提供初始化方法和销毁方法
来实现进场退场。所以我们创建的定时器,dom,全局变量都要在进场方法中产生,销毁方法中去除,做到无污染去残留。
因为有两个动画,动画之间的衔接也是通过初始化和销毁
来衔接的,将两个动画,写在两个对象,或者构造函数中,返回两个方法,以及一些可调节参数。我个人喜欢写成对象方式,可以有效的防止变量的污染还有参数的调节。
之所以要个两个动画都返回初始化和销毁方法,是因为动画要能够随时退场。
为什么想到转行
因为移动设备可用的浏览器太多了,而且版本不可控。对于css3或requestAnimationFrame
的兼容性都不清楚,在加上移动设备的性能,内存占用情况,网络状况等情况都能对动画的效果产生大的影响。导致我再也不愿尝试web动画了。实在是太难受了,调试的时候手上的设备也只有一台6s和OPPO,测试覆盖率也不够,对于UA的判断也不清晰。所以作为前端,最希望的事情,应该就是端的标准化。
总结
对于web动画这个领域,如果你的受众比较固定,使用的设备性能好,那我们可以直接使用HTML5新出的一些动画API,或者webGL技术。 但是如果你对浏览器适配有要求,最好的方案,还是做好向后兼容,简单来说就是多写代码多判断。不同的性能,展现不同质量的动画,比如减少帧数,或者取消动画。因为性能不好的设备展现动画,用户体验非常的差。
关键技术点
requestAnimationFrame
transform
实现序列帧new Image() img.src = xxx
实现预加载- 接口化,可配置化的对象编程方式
- 向后兼容,技术和场景的妥协
- 测试