咱们的项目中使用到了大量的图片资源,其中绝大部分都是由简单线条组成的 icon。这些素材在 UI 设计初期就是以矢量图的形式构建的,所以很是适合使用 SVG 这样的矢量文件进行储存。目前 iOS 或 Android(配合Google Play)都使用了类 Slicing 技术,可以根据不一样设备自动选择合适的图片素材打进包里。而 Flutter 虽然在资源管理上实现了根据不一样屏幕分辨率密度自动选择合适的图片资源的功能,但 Flutter 自己还没法实现相似 Apple Slide 这样自动根据不一样设备下发不一样尺寸资源的能力,所以多套素材无疑会给 App 包大小带来不可小觑的压力。bash
咱们随机选取一个 icon 进行测试:app
Resource | Format | x1 | x2 | x3 | Total |
---|---|---|---|---|---|
![]() |
PNG | 536 bytes | 908 bytes | 1321 KB | 2765 bytes |
![]() |
SVG | 1,896 bytes |
咱们发现,得益于矢量图像的特性,SVG 格式无需像 PNG 同样为每一种分辨率都生成一套位图,所以只须要计算一个文件的大小,在磁盘占用大小上 SVG 有着绝对优点。这仍是在最大分辨率为 x3 的基础上的,随着超高分辨率屏幕和跨平台开发框架(Flutter、SwiftUI)的流行,为每套设备都切一套图的成本只会愈来愈高。框架
此外 PNG 还支持有损压缩,所以咱们通常会对 PNG 进行进一步压缩以压榨 App 的体积。不过不少人可能不了解,SVG 格式也是能够压缩的。编辑器
咱们将上图的 SVG 用文本编辑器打开,发现它使用了 XML 做为描述语言,并基于二维坐标系对矢量图形进行描述。他的头部每每会存在一些无用信息,包括转换软件遗留下的冗余信息。同时,同 js 压缩同理,咱们也能够经过去处格式化来进一步压缩文件大小。ide
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24pt" height="24pt" viewBox="0 0 24 24">
<g enable-background="new">
<clipPath id="cp0">
<path transform="matrix(1,0,0,-1,0,24)" d="M 0 24 L 24 24 L 24 0 L 0 0 L 0 24 Z " fill-rule="evenodd"/>
</clipPath>
<g clip-path="url(#cp0)">
<clipPath id="cp1">
<path transform="matrix(1,0,0,-1,0,24)" d="M -335 -613 L 40 -613 L 40 54 L -335 54 Z "/>
</clipPath>
<g clip-path="url(#cp1)">
<path transform="matrix(1,0,0,-1,0,24)" d="M -5 29 L 29 29 L 29 -5 L -5 -5 Z " fill="#ffffff" fill-opacity=".01"/>
</g>
</g>
<clipPath id="cp2">
<path transform="matrix(1,0,0,-1,0,24)" d="M -335 -613 L 40 -613 L 40 54 L -335 54 Z "/>
</clipPath>
<g clip-path="url(#cp2)">
<path transform="matrix(1,0,0,1,3.5,20.75)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#1f2329" d="M 0 .25 L 18 .25 "/>
</g>
<clipPath id="cp3">
<path transform="matrix(1,0,0,-1,0,24)" d="M 5.5 10.64008 L 5.5 7 L 9.158594 7 L 19.5 17.34593 L 15.84753 21 L 5.5 10.64008 Z "/>
</clipPath>
<g clip-path="url(#cp3)">
<clipPath id="cp4">
<path transform="matrix(1,0,0,-1,0,24)" d="M -335 -613 L 40 -613 L 40 54 L -335 54 Z "/>
</clipPath>
<g clip-path="url(#cp4)">
<path transform="matrix(1,0,0,-1,0,24)" d="M .5 26 L 24.5 26 L 24.5 2 L .5 2 Z " fill="#ffffff"/>
</g>
</g>
<clipPath id="cp5">
<path transform="matrix(1,0,0,-1,0,24)" d="M -335 -613 L 40 -613 L 40 54 L -335 54 Z "/>
</clipPath>
<g clip-path="url(#cp5)">
<path transform="matrix(1,0,0,1,5.5,3)" stroke-width="2" stroke-linecap="butt" stroke-linejoin="round" fill="none" stroke="#1f2329" d="M 0 10.35992 L 0 14 L 3.658594 14 L 14 3.654071 L 10.34753 0 L 0 10.35992 Z "/>
</g>
</g>
</svg>
复制代码
咱们使用了SVG优化工具,通过压缩,咱们能够很明显的看到,压缩后的字符数明显减小,压缩比达到了65%。虽然原理不是很复杂,但实际获得的压缩效果仍是很可观的。svg
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><clipPath id="cp0"><path transform="matrix(1 0 0 -1 0 24)" d="M0 24h24V0H0v24z" fill-rule="evenodd"/></clipPath><g clip-path="url(#cp0)"><clipPath id="cp1"><path transform="matrix(1 0 0 -1 0 24)" d="M-335-613H40V54h-375z"/></clipPath><g clip-path="url(#cp1)"><path d="M-5-5h34v34H-5z" fill="#fff" fill-opacity=".01"/></g></g><clipPath id="cp2"><path transform="matrix(1 0 0 -1 0 24)" d="M-335-613H40V54h-375z"/></clipPath><g clip-path="url(#cp2)"><path stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#1f2329" d="M3.5 21h18"/></g><clipPath id="cp3"><path transform="matrix(1 0 0 -1 0 24)" d="M5.5 10.64V7h3.659L19.5 17.346 15.848 21 5.5 10.64z"/></clipPath><g clip-path="url(#cp3)"><clipPath id="cp4"><path transform="matrix(1 0 0 -1 0 24)" d="M-335-613H40V54h-375z"/></clipPath><g clip-path="url(#cp4)"><path d="M.5-2h24v24H.5z" fill="#fff"/></g></g><clipPath id="cp5"><path transform="matrix(1 0 0 -1 0 24)" d="M-335-613H40V54h-375z"/></clipPath><g clip-path="url(#cp5)"><path stroke-width="2" stroke-linejoin="round" fill="none" stroke="#1f2329" d="M5.5 13.36V17h3.659L19.5 6.654 15.848 3 5.5 13.36z"/></g></svg>
复制代码
Resource | Format | x1 | x2 | x3 | Total |
---|---|---|---|---|---|
![]() |
PNG | 326 bytes | 654 bytes | 567 KB | 1547 bytes |
![]() |
SVG | 1234 bytes |
将两种格式都各自进行压缩操做后,会发现最终体积的差异其实不大。尤为是对于 iOS 或 Android(配合Google Play)这种支持 slicing 的原平生台,单个位图体积也要优于矢量图。 此外,位图的解析时间成本要低于矢量图(尤为是在复杂图像上)。而在 iOS 平台还会额外对 PNG 进行优化,所以 Apple 以前一直没有正式支持 SVG 资源。 不过在最新的 Xcode 12 中咱们惊讶的发现 Assets 已经支持 SVG 资源了。 wordpress
这个不痛不痒的功能,其实在以前版本的 Xcode 上就可以经过移花接木的方式曲线实现,并且还可以经过使用私有 API 的方式经过 CoreSVG 直接解析 SVG,不过不推荐这么作,会有被拒的风险。 虽然 Apple 已经在去年给出了 Symbol Image 方案,其本质也就是 SVG Path 的集合,也容许设计人员经过 SF Symbols App 设计自定义 icon,但这个学习成本可能不是每个设计人员可以达到的。工具
这个结果其实多少仍是挺让人失望的,由于隔壁 Android 早就开始支持 SVG 格式的资源了(虽然是阉割版),而咱们内部的 Flutter 项目也早早就使用上了 SVG。 我估计 Apple 是对 SVG 尚未充足的把握,毕竟 SVG 做为一个有着20年历史的古老格式,各类历史兼容性包袱、动画、性能等等,都是不能忽视的问题。Apple 对于自家的 SF Symbols 有着超强的控制力,可以保证 100% 的兼容性和性能,但你能保证大家的 UI 在画 icon 的时候是严格按照规范来实现的吗? 实际的状况可能比你想的要糟。性能
仍是拿咱们的 Flutter 项目来讲,咱们发现大量的 icon 并无严格按照轮廓来勾勒线条,而是使用了路径叠加等方式实现。这些问题不只会增大 SVG 文件体积,还会增长矢量数据到位图的转换复杂度。不过根据咱们的 Flutter 测试,对于简单的 icon ,目前的主流机型都不存在太大的解析压力。学习
我罗列了一下 SVG 格式目前可能存在的问题:
参考: