一次网页动画踩坑实录

© Young 2016-05-23 14:38
Welcome to My GitHub

概述

在富途从事前端开发以来,真正意义上就做过两次网页动画,但是每次的开发经历都很类似;刚开始感觉很简单,后来就不知不觉就掉坑里边了……为了下次能有所改观,我准备从头到尾仔细的总结一下。

需要注意的是以下所述只是我个人在开发过程中的感受和总结,有可能不完善甚至是错误的;另外补充了一些感觉有一定道理但是由于一些原因没有亲自去验证的观点,主要是为了记录下来,方便今后去验证。

设计阶段

动画一般由专门的设计人员来设计,我以前以为这个阶段和开发人员没什么关系,现在发现过往的种种痛苦经历有很大一部分是因为这种想法导致的。

如果有下次,我一定在这个阶段就好好考虑一下几个问题:

1、有没有什么动画元素是可以去掉的?

从老虎机的基本功能上来说其实三竖行就足够了(不清楚实际生活中的老虎机一般有几个竖行,这里只是从基本功能角度来考虑)。

当前积分以及可玩次数后边的数字能否使用简单的网页字体并去掉跳动效果(手机设备一般比较小,就算这里实现了动画效果,对于用户来说基本也处于隐藏状态)。

2、有没有什么动画效果是可以更换的?

有些动画效果会对浏览器产生严重的渲染负担(比如滚动),就抽奖而言是否可以换成更简单的动画而不一定采用老虎机的形式。

3、有没有什么动画元素用目前常用技术是很难实现的或者很难用目前的技术比较高效的实现?

下边按钮上有两个动效,一个是边框时暗时亮,然后就是按钮背景一直在向左侧移动,本来这两个动效可以用CSS3很简单很高效的实现,但是这个按钮不规则的形状导致很难通过CSS3构造,只能使用gif和图片,从而使资源显著增大,然后由于gif图片会出现留白、边框锯齿等情况也使实现效果不理想。

4、有没有什么动画元素过于复杂(颜色、纹理、尺寸)需要简化的?

一般来说不建议动画元素的颜色、纹理过于复杂,同时也不建议动画元素的尺寸过于大,因为这会导致动画资源太大用户浏览时需要等待很长时间,同时增加浏览器的负担(具体原因在开发阶段解释)。

以前设想过针对逐帧动画的特点只加载像素差数据,然后使用WebWorker技术还原原始图片的像素差逐帧动画https://newbieweb.lione.me/2017/02/16/%E5%83%8F%E7%B4%A0%E5%B7%AE%E9%80%90%E5%B8%A7%E5%8A%A8%E7%94%BB/;代码及示例在githubhttps://github.com/newbieYoung/PxDiffFrameAnimation

总而言之,在设计阶段,作为开发人员,其实是需要全程参与的,不断的进行预开发,从而提前发现一些不合理以及可能会踩坑的地方,而不是简单的认为设计成什么样子就实现成什么样子。

开发阶段

网页动画的实现和前端技术以及浏览器息息相关;那么要实现高性能的网页动画,就必须要熟悉浏览器机制以及各种动画方案以及其适用的场景,同时还需要了解简单的物理运动规律以及常用的缓动函数。

1、简单的物理运动与缓动函数

在现实中,事物在运动时可能加速或减速,如果加速度一定那么就是匀变速运动;如果加速度不固定,那么就是变加速运动;在做动画时,应该尽可能的遵循这些规律,从而产生更好的体验效果。

在CSS3中可以通过animation-timing-function和transition-timing-function来实现缓动;但是如果是其它没有提供相应功能的动画形式,比如JS动画,则需要采用其它第三方类库(其实也可以自己来实现)。

介绍一种曾今使用过的类库bezier-easinghttps://github.com/gre/bezier-easing,在项目中可以通过npm install bezier-easing来安装,这个类库实现了在[0,1]的范围内,数值从[0,1]的缓动变化,在实际使用时需要根据实际情况处理变化区间。

2、浏览器机制

渲染

虽然现阶段浏览器很多,但其实它们的大体流程和一些关键环节还是基本类似的,大致可以概括为[(构造dom模型+计算样式规则) -> 生成渲染模型 -> 排版/重排 -> 绘图/重绘],有些属性的改变会导致浏览器重新生成渲染模型,有些属性的改变则只会导致浏览器重排,有些属性的改变则只会导致浏览器重绘。


关于浏览器机制有太多细节,具体可以查看以下链接:

另外简单的列出了一些导致浏览器重排、重绘、合并渲染层的属性。

如果想详细了解,具体请查看以下链接:

CSS Triggers

一般来说动画尽可能使用transform、opacity这两个属性。

composite paint layout
transform background width、height、padding、margin
opacity color top、left、bottom、right
border-style、text-decoration display、float、position、overflow、vertical-align
visibility border-radius、border-width
box-shadow、border-radius font-family、font-size、text-align、font-weight、line-height、white-space
setTimeout、setInterval、requestAnimationFrame

使用requestAnimationFrame(回调函数会被传入一个DOMHighResTimeStamp参数,这个参数指示当前被 requestAnimationFrame序列化的函数队列被触发的时间)时,一般推荐基于时间来制作动画,如果某些设备渲染性能很差,则可以根据情况改为基于帧数来制作动画。

GPU加速

3、动画方案

JavaScript动画

通过定时器每隔一段时间来改变元素的样式。

优点:可控性很强;兼容性高。

缺点:在不使用第三方动画库时,实现高性能动画需要一定的知识积累,同时实现较复杂动画还要编写大量代码,导致难度增加。

JavaScript动画同样也可以利用GPU加速,用一个3D特性的触发器(比如translate3d()或者matrix3d())来让浏览器为这个元素开辟一个GPU层。

第三方库以及适用场景:(待了解)

CSS3动画

CSS3主要有两种动画实现方式,一种是transition,另外一种是animation;animation比transition复杂,但同时也提供更多可控制的功能。

优点:实现简单;性能佳;有些CSS属性在CSS3动画中能够获得GPU加速,但是不是所有的CSS属性在CSS3动画中都能够获得GPU的加速,只有与文档流无关的属性才能真正被移交给另一个线程,而转移线程的过程中也是有开销的,在大多数动画中,图像的渲染和文档的展现已经耗费了大多数的处理器资源,因此转移线程所带来的好处已经微乎其微了。

缺点:可控性较差;存在一些兼容性问题,CSS3动画在IE9及之前版本的浏览器中都无效,另外很多属性需要添加前缀;两个CSS3动画切换时有可能出现卡顿现象(貌似可以通过will-change来避免)。

第三方库以及适用场景:(待了解)

Canvas动画

HTML5的新元素,类似画板,拥有多种绘制路径、矩形、圆形、字符以及添加图像的方法,本身是没有绘图能力的,所有的绘制工作必须依赖 JavaScript 完成。

优点:可以由Flash直接生成。

缺点:需要一定的知识积累,否则开发的动画可能会很糟糕。

第三方库以及适用场景:(待了解)

GIF图片

优点:容易;简单(对开发人员来说);不存在兼容性的问题,制作方法多而且有成熟工具支持。

缺点:图片透明效果差会存在锯齿和白边。

适用场景:(待了解)

Video视频

优点:制作视频有很多强大的工具支持,流式加载以及各种优秀的压缩算法导致只需要较少资源即可实现复杂的效果。

缺点:存在一些兼容性问题,特别是现阶段Android系统中QQ、微信等应用使用的是腾讯X5内核浏览器。

第三方库以及适用场景:(待了解)

WebGl动画(待了解)
SVG动画(待了解)

调试阶段

其实我一直觉得编程就是发现问题解决问题,所以调试能力是很重要的一环,对前端来说最棒的调试工具莫过于Chrome浏览器的dev-tool了,虽然我没有找到系统详细介绍这款工具的教程,但是还是搜罗了一些文章,链接如下:

同时会在接下来通过一个简单的例子来说明我是如何通过dev-tool来发现问题并解决的。

首先我通过发现很多long frame的composite是耗时最多的,由此可见页面层数太多,但这不是最主要的原因,起初我以为这是最主要的原因,后来发现去掉不必要的绝对定位和固定定位之后依然卡顿很严重。

后来发现在子线程中有大量的rasterize paint操作,这个操作主要是GPU加速时,CPU往GPU转移数据;PC上由于Chrome会开启多个子线程所以不会出现卡顿的现象,但是在移动端Chrome只会开启一个,这时候大量的rasterize paint操作挤在一起就会产生卡顿现象。

知道原因之后接下来需要定位元素了,通过Paint profile栏发现目标元素的尺寸是26*26,同时long frame的时间间隔大致为100毫秒,就定位了老虎机头部灯。

开发的时候我问设计要图片,但是她给的圆不是一个严格的圆,转动起来总有飘动的感觉,后来我就切出了两种灯的状态,然后一个个定位,然后就出现了大量的绝对定位元素,而且这些元素动画时透明度会不断变化,透明度的变化会被Chrome浏览器启动GPU加速,GPU加速时会给当前创建一个单独的渲染层,导致渲染层增多而且会有大量转移操作,在子线程只有一个的移动端就会出现卡顿的现象了,改进措施就是按以前的方案,用严格圆替代单独小点。

还做了一个优化措施,把动画元素和非动画元素分开了,以前动画元素和非动画元素的父元素是同一个dom元素,这个dom元素有一个很大的背景,我发现每一帧都会绘制这个背景,所以就把它们分开了,但是效果不太明显。

不断优化直到iphone4没有卡顿现象后,timeline概况如下,longframe明显减少,另外之所以启动阶段有大量longframe时因为启动时大量动画同时开启而且还是执行JS脚本处理事件等;

最后之所以还有一些long frame是因为代码里边有使用定时器,但是影响不大也就没有再改了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注