Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 741|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-14 10:11:01 | 显示全部楼层 |阅读模式

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

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

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

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

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

    一个抖动的例子

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

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

    iPhone 6
    (375×667)
    iPhone 6+
    (414×736)
    iPhone 5
    (320×568)
    Android
    (360×640)
    20170815-ip6 20170815-ip6+ 20170815-ip5-3 20170815-android

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

    分析抖动

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

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

    20170814-1

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

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

    情况一 情况二 情况三
    20170814-3 20170814-4 20170814-5

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

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

    情况一 情况二 情况三
    20170814-3 20170814-4 20170814-5

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

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

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

    解决方案

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

    解决构想一

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

    20170814-6

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

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

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

    解决构想二

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

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

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

    css:

    javascript:

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

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

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

    html:

    css:

     

    方案C的改良

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

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

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

    caniuse

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

    html:

    css:

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

    总结

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


    参考资料:

    https://stackoverflow.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

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-1-22 23:41 , Processed in 0.060755 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表