聊聊有关SVG那些事儿

2019-06-11 13:16:13 浏览数 (1)

前言

前几天,老伙计推送微信的一篇文章,文章标题为:【Android微信上的SVG】,正好LZ借此机会学习了解下有关SVG的相关内容。

微信文章阅读后感

首先,微信提出了几个论点:

  • “清晰”和“体积”的矛盾与麻烦 面对android的各种dpi某事,想要所有设备上的图片都能有最清晰的效果,就意味着每种dpi模式都必须提供一份对应尺寸的资源,除非你不在乎安装包的体积有多大,所以这显然是不可能去做的。(想要清晰,每种dpi都需要放置对应的图片,相应的缺点就是造成Apk会随着图片越来越多而越来越大)在过去的几年里andorid从mdpi发展到xxxhdpi,每当微信想让相同的图片在更清晰的屏幕上显示我们想要的效果时,我们总要重新提供一份体积更大的高清png并且删掉可能不太多使用的小分辨率图片。(android从m发展到xxx,想要适配,而且Apk要尽可能小,只能兼容主流大的,舍弃使用频率小的)只保留一种分辨率图片的方法确实比所有dpi都来一份体积要小一点,然后只是用一份资源还需要承担的负面效果则是当向其他dpi模式scale时,图片也会变得模糊,并且你还要决定自己什么时候该更换上更大分辨率的图片了。(保留一种图片,Apk大小确实减小了,相对应的兼容性却大大降低,这时候,就主要考虑兼容性的问题)
  • 矢量图SVG 栅格图自身特点导致了高清资源同安装包体积之间的矛盾。这方面矢量图存在明显的优势,它可以在表达清晰图片的同时,不增加文件体积。而且只要你不重新设计图片,就用不着再去适配高dpi模式,矢量图什么分辨率都可以自适应。(矢量图SVG,在表达清晰图片的同时还不增加文件体积,并且兼容所有分辨率)我们认为SVG是比较合适的矢量化资源方案,因为它相比目前android上的一些矢量化方案更成熟、周边工具支持更好。像VectorDrawable、ttf这样的方案总有这不尽人意的地方,对于UI同学来说这两个模式也不太好操作,不能轻易生成的资源会牺牲大家的工作效率是明显得不偿失的。(另外,VectorDrawable经过我们测试发现性能并不理想,这受限于他的实现方法。)(SVG是比较合适的矢量化资源方案,相对来说方案更成熟、周边工具支持更好)

而微信上的SVG亟需解决的俩个问题如下:

  • 性能问题 理论上讲,SVG的效率可能会不如PNG好,这是因为它需要运行时的计算和对应平台的渲染绘制。而且对于PNG来说的另一优势是在开启硬件加速的设备上,绘制Bitmap一个非常快速的过程。可以想象,让SVG不比PNG慢将是一件很有挑战的事情。(SVG对CPU的挑战比较大,不过对于现在的手机来说,这点问题属于小意思了)
  • 开发者的使用成本问题 SVG并不是android支持的标准资源格式,android资源框架自然不可能天然支持SVG的资源加载,而修改框架和提供支持很可能意味着会增加后面使用SVG的开发同学的学习成本和使用成本。因此必须要考虑如何即可以用SVG但又不增加开发负担

让我们一起来见证下,经过微信团队的优化后,真实有效的数据吧!

  • 清晰度

两张xxhdpi资源在OPPO R7Plus上的显示结果。左边SVG,右边PNG。

  • 体积 在之前的一次灰度中我们替换了130个资源,这使得最终体积减小了211KB,平均每个减小1.6KB。后面微信会将所有可以矢量化的资源全部替换成SVG,预计这将减小大约1.5MB左右的体积,对比目前压缩后全部约7MB的png,这是个不小的节约。
  • 性能

SVG在加载的过程中得到非常大优势,而Draw的时候因为没有硬件渲染导致性能远不如PNG。但通过在加载阶段的大幅提升,让SVG在整体耗时上赢了PNG。

而关于以上,微信提出了如下:

  • 为什么我们可以将“加载”和“渲染”相加在一起来比较?

事实上,SVG渲染过程使用了Picture进行绘制。Picture并不支持硬件加速,因此必须要将View的LayerType设为Software,而这个操作的意义就是为View创建了一个Bitmap将Picture绘制其上,同时缓存起来。所以,我们可以将“加载”和“渲染”放在一起进行比较,就是因为只有第一次的加载和渲染上我们同PNG是不同的。在这之后,一旦创建好了SoftwareLayer用的Bitmap,绘制过程就同PNG图片一样,可以用硬件渲染来画Bitmap了。

所以,我们得到了比PNG快上70%的SVG矢量化资源

而相对应的优点背后,也存在着不可避免的牺牲:

由于我们实现方式的原因,启动进程时每个SVG将额外消耗掉280us左右的时间。大概就是当我们替换完1000个资源后,我们的启动时间可能会增加280ms。

这样做是有原因的,一方面是因为我们必须这么做来实现框架的无感知,另外也是为了使SVG的整体效率更高(因为生成了一些代码使得后面通过ResourceID免除了反射查找一些类的时间)。而事实上即便我们把这个时间加回到每次加载平均值中,SVG也依旧领先于PNG的整体耗时。

好了,有关微信官方讲述的内容介(kao)绍(bei)到此结束,下面,我们将从概念、过程以及使用的方面去讲解说明有关SVG的基本操作流。

Hi,SVG

可能上面微信介绍大家有点懵,没关系,LZ也是一样懵,我们只需要了解如下几点即可:

  1. SVG的优势相比PNG,SVG有着更好的兼容性以及相比使用PNG,SVG体积更小;
  2. SVG并不是android支持的标准资源格式;
  3. SVG相对PNG,则性能尤其对CPU消耗比较大,不过针对于目前的手机而言,还是比较能接受。

而关于SVG,它究竟是什么?到现在我们了解甚少,不过,没关系,慢慢来呗~

SVG,即Scalable Vector Graphics 可伸缩矢量图形。而关于详细,请查阅:W3School官方

首先要解释下矢量图像以及位图图像?

  • 矢量图象 SVG是W3C 推出的一种开放标准的文本式矢量图形描述语言,他是基于XML的、专门为网络而设计的图像格式。So,SVG是一种采用XML来描述二维图形的语言,所以它可以直接打开xml文件来修改和编辑。
  • 位图图像 位图图像(Bitmap)的存储单位是图像上每一点的像素值,因而文件会比较大,像GIF、JPEG、PNG等都是位图图像格式。 而Android中的矢量图,便是Vector,在Android中也被称为Vector Drawable。

而这里需要注明的一点:

Vector图像刚发布的时候,是只支持Android 5.0 的,自从AppCompat 23.2之后,Vector可以使用于Android 2.1以上的所有系统,只需要引用com.android.support:appcompat-v7:23.2.0以上的版本就可以了。(所谓的兼容也就是表里不一,即低版本非真实使用SVG,而是生成PNG图片)

还要说明的是,Android 5.0发布的时候,Google提供了Vector的支持,即:Vector Drawable类

Vector Drawable,Hello

Vector Drawable相对于普通的Drawable来说,有以下几个好处:

(1)Vector图像可以自动进行适配,不需要通过分辨率来设置不同的图片;

(2)Vector图像可以大幅减少图像的体积,同样一张图,用Vector来实现,可能只有PNG的几十分之一;

(3)使用简单,很多设计工具,都可以直接导出SVG图像,从而转换成Vector图像 功能强大;

(4)不用写很多代码就可以实现非常复杂的动画 成熟、稳定,前端已经非常广泛的进行使用了。

Vector 语法简介

主要通过使用Path标签,我们几乎可以完成所有操作。

而关于Path指令,具体解析如下所示:

  1. M = moveto(M X,Y) :将画笔移动到指定的坐标位置,相当于 android Path 里的moveTo();
  2. L = lineto(L X,Y) :画直线到指定的坐标位置,相当于 android Path 里的lineTo();
  3. H = horizontal lineto(H X):画水平线到指定的X坐标位置;
  4. V = vertical lineto(V Y):画垂直线到指定的Y坐标位置 ;
  5. C = curveto(C X1,Y1,X2,Y2,ENDX,ENDY):三次贝赛曲线 ;
  6. S = smooth curveto(S X2,Y2,ENDX,ENDY): 同样三次贝塞尔曲线,更平滑 ;
  7. Q = quadratic Belzier curve(Q X,Y,ENDX,ENDY):二次贝赛曲线 ;
  8. T = smooth quadratic Belzier curveto(T ENDX,ENDY):映射 同样二次贝塞尔曲线,更平滑 ;
  9. A = elliptical Arc(A RX,RY,XROTATION,FLAG1,FLAG2,X,Y):弧线 ,相当于arcTo();
  10. Z = closepath():关闭路径(会自动绘制链接起点和终点);

注意,’M’ 处理时,只是移动了画笔, 没有画任何东西。

而关于以上的语法,我们只需要了解即可。

因为相对于编写过程中的消耗,我们都可以借助于一些工具来完成。

SVG初使用

关于编辑以及绘制SVG,我们可以通过如下网站搭配使用:

•SVG生成工具;

•SVG转为VectorDrawable

下面开始演示之路:

SVG生成:

接下来,SVG转为VectorDrawable:

之后,将下载完成后的xml拷贝到drawable目录下。

首先,我们简单的解析部分的语法,好让大家以后真正遇到后不会那么棘手。

代码语言:javascript复制
android:pathData="
M 287 87.5 
C 351.893458105 87.5 404.5 138.091823269 404.5 200.5 
C 404.5 262.908176731 351.893458105 313.5 287 313.5 
C 222.106541895 313.5 169.5 262.908176731 169.5 200.5 
C 169.5 138.091823269 222.106541895 87.5 287 87.5 
Z"

首先,将坐标移动到 287 87.5 位置,接着,分别绘制三次贝赛曲线,最后关闭路径。从而形成了一个图形。

首先,我们先当作一个图片直接使用:

代码语言:javascript复制
   <ImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:src="@drawable/method_draw_image" />

5.0 上显示效果如下:

不知道大家还记得,之前LZ说的,所谓的兼容,并不是真正的让低版本去使用SVG,而是在低版本上面讲SVG转为了png,不信你瞧:

有的小伙伴说,忒麻烦了,还有其他的方式么?简单点的?

Android Studio 创建 Vector Asset

右击res下的drawable目录,选择new,点击Vector Asset。

你可以调整透明度,也可以设置图片大小,如下:

当然,你也可以选择系统内容的图片,如下:

使用方式如上述一致,首先我们看一下生成的xml文件内容:

代码语言:javascript复制
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp" // 宽度
    android:height="24dp" // 高度
    android:alpha="0.80" // 透明度
    android:viewportHeight="24.0" // 高度平均分为24等份
    android:viewportWidth="24.0"> // 宽度平均分为24等份
    <path
        android:fillColor="#FF000000"
        android:pathData="M6,18c0,0.55 0.45,1 1,1h1v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L11,19h2v3.5c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5L16,19h1c0.55,0 1,-0.45 1,-1L18,8L6,8v10zM3.5,8C2.67,8 2,8.67 2,9.5v7c0,0.83 0.67,1.5 1.5,1.5S5,17.33 5,16.5v-7C5,8.67 4.33,8 3.5,8zM20.5,8c-0.83,0 -1.5,0.67 -1.5,1.5v7c0,0.83 0.67,1.5 1.5,1.5s1.5,-0.67 1.5,-1.5v-7c0,-0.83 -0.67,-1.5 -1.5,-1.5zM15.53,2.16l1.3,-1.3c0.2,-0.2 0.2,-0.51 0,-0.71 -0.2,-0.2 -0.51,-0.2 -0.71,0l-1.48,1.48C13.85,1.23 12.95,1 12,1c-0.96,0 -1.86,0.23 -2.66,0.63L7.85,0.15c-0.2,-0.2 -0.51,-0.2 -0.71,0 -0.2,0.2 -0.2,0.51 0,0.71l1.31,1.31C6.97,3.26 6,5.01 6,7h12c0,-1.99 -0.97,-3.75 -2.47,-4.84zM10,5L9,5L9,4h1v1zM15,5h-1L14,4h1v1z" />
</vector>

而运行的效果则是:

GitHub查看地址

https://github.com/HLQ-Struggle/SVGStudy

参考资料

1.微信官方SVG介绍;

2.W3School官方;

0 人点赞