css3 animation && filter: blur()引发的动画性能问题排查

2020-04-08 16:05:32 浏览数 (1)

这篇文章记录了自己排查动画问题时的思路,最后的解决有一些侥幸,也是因为最近刚好学习了部分安卓代码,技术视野稍微开阔了些

我们在工作中经常会遇到一些动画卡顿的问题,往往是一些性能比较差的安卓手机,笔者最近就遇到了这样的情况,这里也记录下本次排查问题的过程。

因为页面并不复杂,所以看到页面动画卡顿之后,能够很快速的猜想到是哪些css属性引起的卡顿,通过注释掉代码后,就能够很快的验证自己的推论,这次排查的页面里,导致页面卡顿的是下面这两个属性。

代码语言:javascript复制
bg-img {
    filter: blur(10px);
}
btn {
    animation: scaleAnimation linear 1.5s 1000 2s;
}
@keyframes scaleAnimation {
    0% {
        transform: scale(1);
    }
    12.5% {
        transform: scale(1.05);
    }
    25% {
        transform: scale(1);
    }
    37.5% {
        transform: scale(1.05);
    }
    50% {
        transform: scale(1);
    }
    62.5% {
        transform: scale(1.3);
    }
    75% {
        transform: scale(1);
    }
    87.5% {
        transform: scale(1.05);
    }
    100% {
        transform: scale(1);
    }
}

一开始猜想可能是在1.5s中,定义不同阶段的动画间隔太短,导致了按钮的卡顿, 但是当我只保留了scaleAnimation中的3个阶段后,发现动画还是能看出来卡顿, 因此应该不是scaleAnimation的问题,同时我又将filter样式注释掉后,发现动画变得流畅了。

那最初的结论就是因为filter样式导致了动画的卡顿。

那么浏览器filter是怎么实现的呢,为什么会造成这个卡顿呢?

后来就搜到了这篇文章, blur会根据周围像素的值,根据权重计算一个中心点的高斯模糊值,很显然,我们并不是要去优化这个算法,那只能换一个思路:

是否是因为动画,导致每次动画重新渲染时,也引发了背景图片的重绘? 在这个过程中,这篇文章介绍的很清楚, 样式优化会涉及到下面几个环节: style -> layout -> paint -> composite 一般会有下面3种方式的情况:

  • 1.修改了一个DOM的layout (影响了布局),比如width,height,那么浏览器就会进行reflow(重排),然后再进行重绘。
    1. 修改了页面的"paint only",比如颜色,阴影这种,那么浏览器就会跳过布局,只会绘制和渲染层合并。
  • 3.如果你修改一个非样式且非绘制的CSS属性,那么浏览器完成样式计算之后,会跳过布局和绘制的过程,直接进行渲染层合并。

从我们遇到的问题来看,我们需要优化的是第3种情况,也就是渲染层合并。那么有没有可能是因为我们的背景图片和按钮渲染在了同一渲染层,导致filter每次都要进行重新计算呢?

于是打开chrome的控制台发现,通过translate3d,目前的按钮已经是一个单独的图层了

因此这个按钮图层再触发repaint操作的时候是只会更新自己,不会影响我们的背景图片。 但是为什么开启了硬件加速的动画,会卡顿呢?

目前h5能做的优化内容看起来已经都做了,这个时候难道真的是安卓手机性能太差吗? 于是基本上已经放弃的我想做最后一次验证,就是客户端是否已经开启了硬件加速,因为跑在我们客户端的webview上,我们还是要确认下到底是否开启了硬件加速,不然h5做的这些优化都是白费。

也是最近刚好涉及了一些简单的客户端的开发,很快的在性能差的手机上构建了sdk demo, 再打开webview前加入了这一行代码

代码语言:javascript复制
endCardLayout.isHardwareAccelerated();

发现返回居然是false,

看到android官网上介绍,下面这部分代码可以开启窗口级别的硬件加速

代码语言:javascript复制
getWindow().setFlags(
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
        WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

查看android代码后,确实onBeforeCreate已经开启了硬件加速, 但是看到我们继续定位到webview容器的layout时,发现调用了这么一行代码

代码语言:javascript复制
    myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

原来客户端专门针对endcard这个view,关闭了硬件加速

so ga!!!! 成功破案

chrome devtool
  • 1、Show paint rectangles 显示绘制矩形
  • 2、Show composited layer borders 显示层的组合边界(注:蓝色的栅格表示的是分块)
  • 3、Show FPS meter 显示 FPS 帧频
  • 4、Enable continuous page repainting 开启持续绘制模式 并 检测页面绘制时间
  • 5、Show potential scroll bottlenecks 显示潜在的滚动瓶颈

0 人点赞