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

   2023-02-08 学习力0
核心提示:笔者所在的前端团队主要从事移动端的H5页面开发,而团队使用的适配方案是: viewport units + rem。具体可以参见凹凸实验室的文章 – 利用视口单位实现适配布局 。笔者目前(2017.08.12)接触到的移动端适配方案中,「利用视口单位实现适配布局」是最好的方

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

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

  • 微观尺寸(20px左右)定位不准
  • 逐帧动画容易有抖动

第一个难点的通常出现在 icon 绘制过程,可以使用图片或者 svg-icon 解决这个问题,笔者强烈建议使用 svg-icon,具体理由可以参见:「拥抱Web设计新趋势:SVG Sprites实践应用」。

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

一个抖动的例子

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

 
 
 
 
 
JavaScript
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
;
;
;
//misc.aotu.io/leeenx/sprite/m.png) 0 0 no-repeat;
;
;
;
;
;
}
{
{
;
}
}

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

iPhone 6
(375×667)
iPhone 6+
(414×736)
iPhone 5
(320×568)
Android
(360×640)
CSS技巧:逐帧动画抖动解决方案 CSS技巧:逐帧动画抖动解决方案 CSS技巧:逐帧动画抖动解决方案 CSS技巧:逐帧动画抖动解决方案

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

分析抖动

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

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

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

每帧的尺寸为 3px * 3px,逐帧的取位过程如下:
CSS技巧:逐帧动画抖动解决方案

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

情况一 情况二 情况三
CSS技巧:逐帧动画抖动解决方案 CSS技巧:逐帧动画抖动解决方案 CSS技巧:逐帧动画抖动解决方案

理论上,5 / 3 = 1.666...。但实际上光点取整后,三个帧的宽度都不可能等于 1.666...,而是有一个帧的宽度降级为 1px(亏),另外两个宽度升级为 2px(盈),笔者把这个现象称作「盈亏互补」。

再看一下盈亏互补后,逐帧的取位过程:

情况一 情况二 情况三
CSS技巧:逐帧动画抖动解决方案 CSS技巧:逐帧动画抖动解决方案 CSS技巧:逐帧动画抖动解决方案

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

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

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

解决方案

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

解决构想一

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

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

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

  • KB变大与请求数增多
  • 多余的 animation 代码

这个方案很简单,这里就不赘述了。

解决构想二

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

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

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
{
;
;
;
//misc.aotu.io/leeenx/sprite/m.png) 0 0 no-repeat;
;
;
;
;
;
;
;
}
{
{
;
}
}
/* 写断点 */
{
{
;
}
}
{
{
;
}
}
{
{
;
}
}

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

css:

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
;
;
;
;
;
;
;
;
;
;
}
{
{
;
}
}

javascript:

 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
// 以下代码放到<head></head>中// <![CDATA[
 
.steps_anim {scale(.5); }
 
 
;
{
;
}
;
;
// ]]>

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

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

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

html:

 
 
 
 
 
 
JavaScript
 
1
2
3
>
/>
>

css:

 
 
 
 
 
JavaScript
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
;
;
;
;
;
;
{
;
}
}
{
{
;
}
}

 

方案C的改良

实现方案C很好地解决了方案A和方案B的缺陷,不过方案C也有它的问题:不利于自动化工具去处理图片

自动化工具一般是怎么处理图片的?
自动化工具一般是扫描 CSS 文件找出所有的 url(...) 语句,然后再处理这些语句指向的图片文件。

如果 可以改用 CSS 的 background-image 就可以解决这个问题,不过 SVG 不支持 CSS 的 background-image。但是,SVG有一个扩展标签:foreignObject,它允许向 插入 html 代码。在使用它前,先看一下它的兼容情况:

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

iOS 与 Android 4.3 一片草绿兼容情况算是良好,笔者实机测试腾讯 X5 内核的浏览器兼容仍旧良好。以下是改良后的方案。

html:

 
 
 
 
 
JavaScript
 
1
2
3
4
5
>
>
>
/foreignObject>
>

css:

 
 
 
 
 
JavaScript
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
;
;
;
;
;
;
}
{
;
;
}
{
;
;
//misc.aotu.io/leeenx/sprite/m.png) 0 0 no-repeat;
;
;
}
{
{
;
}
}

改良后的方案DEMO: http://jdc.jd.com/fd/promote/leeenx/201708/svg-sprite.html

总结

感谢阅读完本文章的读者。本文是笔者的个人观点,希望能帮助到有相关问题的朋友,如果本文有不妥之处请不吝赐教。


参考资料:

https://***.com/questions/9946604/insert-html-code-inside-svg-text-element
https://www.w3.org/TR/SVG/extend.html
https://developer.mozilla.org/en-US/docs/Web/SVG/Element/foreignObject

 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • css实现弹出框 css弹出菜单
    弹出框也是前端里面经常使用的一个应用场景了,最开始想到的是用js实现这个效果,看到codepen上面有用css实现的。其实就是用到了css里面的一个:target选择器+visibility属性。URL 带有后面跟有锚名称 #,指向文档内某个具体的元素。这个被链接的元素就是目标
    03-08
  • jfinal框架页面找不到相关css,js文件404
    在JFinalConfig中添加配置: @Overridepublic void configHandler(Handlers handlers) {handlers.add(new ContextPathHandler());}页面中添加:base href="${CONTEXT_PATH}/"/之前页面找不到资源,把tomcat工程路径去掉了(path=""),这样暂时解决了问题推荐
    03-08
  • 纯CSS3实现的一些酷炫效果 css实现炫酷背景
    纯CSS3实现的一些酷炫效果 css实现炫酷背景
      之前在网上看到一些用纯CSS3实现的酷炫效果,以为实现起来比较困难,于是想看看具体是怎么实现的。一、笑脸猫动画实现效果如下:这个实现起来确实比较麻烦,很多地方需要花时间,有耐心地调整。1.先看下页面结构:bodydiv class="container"!-- 脸 --div
    03-08
  • 移动端CSS底部固定和fixed定位在ios上的bug
    fixed定位在ios上的bugcss三栏布局假设我们页面的 HTML 结构是这样: div class="wrapper"div class="content"!-- 页面主体内容区域 --/divdiv class="footer"!-- 需要做到 Sticky Footer 效果的页脚 --/div/div方法1.:absolute通过绝对定位处理应该是常见的
    03-08
  • css实现图片翻转 css使图片旋转
    !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"html xmlns="http://www.w3.org/1999/xhtml"headmeta http-equiv="Content-Type" content="text/h
    03-08
  • css3 渐变gradient
    css3 渐变gradient
           CSS3引入了背景渐变、background-origin、background-clip、background-size、遮罩等多个属性。这里将记录我从网上和书上学习的笔记和心得体会。   首先是渐变,我这里讲述的渐变不仅仅是背景色的渐变还将包括透明度渐变。以前做这种背景色渐
    03-08
  • CSS3中的px,em,rem,vh,vw辨析 css3 vw
    1、px:像素,精确显示2、em:继承父类字体的大小,相当于“倍”,如:浏览器默认字体大小为16px=1em,始终按照div继承来的字体大小显示,进场用于移动端          em换算工具:http://www.runoob.com/tags/ref-pxtoemconversion.html3、rem:与em类似,
    03-08
  • gulp自动化构建工具--自动编译less为css--学习
     1.安装      命令:npm install gulp-less 或者 cnpm install gulp-less2.编写文件 //获取gulpvar gulp = require('gulp')//获取gulp-less模块var less = require("gulp-less")//编译less//在命令行输入gulp less启动此任务gulp.task('less',function(){
    03-08
  • 关于动画Animate.css的使用方法
        首先贴个官网:https://daneden.github.io/animate.css/  1、引入animate css文件 1 head2 link rel="stylesheet" href="animate.min.css"3 /head   2、给指定的元素加上指定的动画样式名div class="animated bounceOutLeft"/div    这里包
    03-08
  • selenium Firefox禁用js,css.falsh,缓存,设置
    1 profile = FirefoxProfile() 2 #请求头 3 #profile.set_preference("general.useragent.override", util.http_agent_insert(xinhuaUtil.agent_path)) 4 # 激活手动代理配置(对应着在 profile(配置文件)中设置首选项) 5 profile.set_preference("network
    03-08
点击排行