jQuery技术

CSS技巧:逐帧动画抖动解决方案 前端开发,JQUERY特效,全栈开发

字号+ 作者:H5之家 来源:H5之家 2018-04-23 14:06 我要评论( )

CSS技巧:逐帧动画抖动解决方案

AD:百G视频免费分享

笔者所在的前端团队主要从事移动端的H5页面开发,而团队使用的适配方案是: viewport units + rem。具体可以参见凹凸实验室的文章 – 利用视口单位实现适配布局 。

笔者目前(2017.08.12)接触到的移动端适配方案中,「利用视口单位实现适配布局」是最好的方案。不过使用 rem 作为单位会遇到以下两个难点:

  • 微观尺寸(20px左右)定位不准
  • 逐帧动画容易有抖动
  • 第一个难点的通常出现在 icon 绘制过程,可以使用图片或者 svg-icon 解决这个问题,笔者强烈建议使用 svg-icon,具体理由可以参见:「拥抱Web设计新趋势:SVG Sprites实践应用」。

    第二个难点笔者举个例子来分析抖动的原因和寻找解决方案。

    一个抖动的例子

    做一个8帧的逐帧动画,每帧的尺寸为:360×540。

    .steps_anim { position: absolute; width: 9rem; height: 13.5rem; background: url() 0 0 no-repeat; background-size: 45rem 13.5rem; top: 50%; left: 50%; margin: -5.625rem 0 0 -5.625rem; animation: step 1.2s steps(5) infinite; } @keyframes step { 100% { background-position: -45rem; } }

    观察在主流(手机)分辨率下的播放情况:

    iPhone 6
    (375×667) iPhone 6+
    (414×736) iPhone 5
    (320×568) Android
    (360×640)

    四种分辨率下,可以看到除了 ip6 其它的三种分辨率都发生了抖动。(ip6 不抖动的原因是适配方案是基本于 ip6 的分辨率订制的。)

    分析抖动

    图像由终端(屏幕)显示,而终端则是一个个光点(物理像素)组成的矩阵,换句话说图片也一组光点矩阵。为了方便描述,笔者假设终端上的一个光点代表css中的1px。

    以下是一张 9px * 3px 的sprite:


    每帧的尺寸为 3px * 3px,逐帧的取位过程如下:

    把 sprite 的 background-size 的宽度取一半,那么终端会怎么处理?
    9 / 2 = 4.5
    终端的光点都是以自然数的形式出现的,这里需要做取整处理。取整一般是三种方式:round/ceil/floor。假设是 round ,那么 background-size: 5px,sprite 会是以下三种的一个:

    情况一 情况二 情况三

    9px * 3px

    9px * 3px

    9px * 3px

    可以看到由于盈亏互补导致了三个帧的宽度不一致,亏的那一帧在动画中的表示就是抖动。

    笔者总结抖动的原因是:sprite在尺寸缩放后,帧与帧之间的盈亏互补现象导致动画抖动

    附注:1px 由几个光点表示是由以终端的 dpr 决定

    解决方案

    「盈亏互补」也可以说是「盈亏不一致」,如果尺寸在缩放后「盈亏一致」那么抖动现象可以解决。

    解决构想一

    笔者根据「盈亏一致」设计了「解决构想一」:

    根据上图,其实很容易就联想到一个简单的方案:不用雪碧图(即一帧对应一张图片)。
    这个方案确实是可以解决抖问题,不过笔者并不推荐使用它,因为它有两个负面的东西:

  • KB变大与请求数增多
  • 多余的 animation 代码
  • 这个方案很简单,这里就不赘述了。

    解决构想二

    把逐帧取位与图像缩放拆分成两个独立的过程,就是笔者的「解决构想二」:

    实现「构想二」,笔者首先想到的是使用 transform: scale(),于是整理了一个实现方案A:

    .steps_anim { position: absolute; width: 360px; height: 540px; background: url() 0 0 no-repeat; background-size: 1800px 540px; top: 50%; left: 50%; transform-origin: left top; margin: -5.625rem 0 0 -5.625rem; transform: scale(.5); animation: step 1.2s steps(5) infinite; } @keyframes step { 100% { background-position: -1800px; } } /* 写断点 */ @media screen and (width: 320px) { .steps_anim { transform: scale(0.4266666667); } } @media screen and (width: 360px) { .steps_anim { transform: scale(0.48); } } @media screen and (width: 414px) { .steps_anim { transform: scale(0.552); } }

    这个实现方案A存在明显的缺陷:scale 的值需要写很多断点代码。于是笔者结全一段 js 代码来改善这个实现方案B:

    css:

    .steps_anim { position: absolute; width: 360px; height: 540px; background: url("http://www.jqhtml.com/wp-content/uploads/2017/09/m.png") 0 0 no-repeat; background-size: 1800 540px; top: 50%; left: 50%; transform-origin: left top; margin: -5.625rem 0 0 -5.625rem; animation: step 1.2s steps(5) infinite; } @keyframes step { 100% { background-position: -1800px; } } // 以下代码放到 <head></head> 中 <script> document.write("<style>.steps_anim {scale(.5); }</style>"); function doResize() { scaleStyleSheet.innerHTML = ".steps_anim {-webkit-transform: scale(" + (document.documentElement.clientWidth / 750) + ")}"; } window.onresize = doResize; doResize(); </script>

    通过改善后的方案 CSS 的断点没了,感觉是不错了,不过笔者觉得这个方案不是个纯粹的构建方案。

    我们知道 <img> 是可以根据指定的尺寸自适应缩放尺寸的,如果逐帧动画也能与 <img> 自适应缩放,那就可以从纯构建角度实现「构想二」。

    SVG刚好可以解决难题!!!SVG 的表现与 <img> 类似同时可以做动画。以下是笔者的实现方案C。

    html:

     

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    相关文章
    网友点评