首页 > 技术文章 > CSS中内联SVG图片有比Base64更好的形式

facenano 2021-10-21 11:14 原文

一、之前SVG背景图的做法

在写这篇文章之前,对于SVG图标或图形,如果作为CSS背景使用,只要尺寸不超过2K,很多时候我都是直接内联在CSS代码中的,采用的形式是base64格式。

相关CSS代码如下:

.icon-arrow-down {
    width: 20px; height: 20px;
    background: url(data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyMDAiIHZpZXdCb3g9IjAgMCAyMDAgMjAwIj48cGF0aCBmaWxsPSIjMjQ4NmZmIiBkPSJNMTQ1LjY1OSw2OC45NDljLTUuMTAxLTUuMjA4LTEzLjM3Mi01LjIwOC0xOC40NzMsMEw5OS40NzksOTcuMjMzIEw3MS43NzIsNjguOTQ5Yy01LjEtNS4yMDgtMTMuMzcxLTUuMjA4LTE4LjQ3MywwYy01LjA5OSw1LjIwOC01LjA5OSwxMy42NDgsMCwxOC44NTdsNDYuMTgsNDcuMTRsNDYuMTgxLTQ3LjE0IEMxNTAuNzU5LDgyLjU5OCwxNTAuNzU5LDc0LjE1NywxNDUuNjU5LDY4Ljk0OXoiLz48L3N2Zz4NCg==) no-repeat center/100%;
}

 

矢量,少了个文件请求,渲染几乎无延时,自认为性价比不错。

然而,今天我发现,对于SVG图形,使用Base64格式进行内联,并不是最佳的做法,还有更好的实现方式,就是直接使用SVG XML格式代码,无需进行base64转换。

二、SVG直接内联

方法就是把SVG代码直接内联在CSS的url()方法中,语法就是data:image/svg+xml;utf8,加上完整的SVG代码即可!例如比较常用的background-imageurl()方法,代码如下:

.icon-arrow-down {
    width: 20px; height: 20px;
    background: url('data:image/svg+xml;utf8,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200"><path fill="#00A5E0" d="M145.659,68.949c-5.101-5.208-13.372-5.208-18.473,0L99.479,97.233 L71.772,68.949c-5.1-5.208-13.371-5.208-18.473,0c-5.099,5.208-5.099,13.648,0,18.857l46.18,47.14l46.181-47.14 C150.759,82.598,150.759,74.157,145.659,68.949z"/></svg>') no-repeat center;
    background-size: 100%;
}

使IE浏览器也支持的处理

首先,我们对所有SVG字符进行URL-encode肯定是OK的,例如执行:

encodeURIComponent('<svg version="1.1" ...</svg>')

得到如下CSS:

.icon-arrow-down {
    width: 20px; height: 20px;
    background: url(data:image/svg+xml,%3Csvg%20version%3D%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22200%22%20height%3D%22200%22%20viewBox%3D%220%200%20200%20200%22%3E%3Cpath%20fill%3D%22%2300A5E0%22%20d%3D%22M145.659%2C68.949c-5.101-5.208-13.372-5.208-18.473%2C0L99.479%2C97.233%20L71.772%2C68.949c-5.1-5.208-13.371-5.208-18.473%2C0c-5.099%2C5.208-5.099%2C13.648%2C0%2C18.857l46.18%2C47.14l46.181-47.14%20C150.759%2C82.598%2C150.759%2C74.157%2C145.659%2C68.949z%22%2F%3E%3C%2Fsvg%3E) no-repeat center;
    background-size: 100%;
}

结果IE浏览器下SVG图标出现了(包括IE9浏览器)

 

但是,完全URL-encode后也会带来另外的问题,和转义前原始SVG相比,可读性差了很多,想改个颜色都找不到在哪里。那有没有可以进一步提升的空间呢?

有!

部分URL encode IE浏览器也兼容

实际上,完全URL encode是没有必要的,也就是,我们无需让所有的字符都encode也能让IE浏览器正确识别。根据前辈的实践,只需要对下面这些字符转义就能满足绝大多数的场景,它们是:"%#{}<>

其中,由于XML中属性值双引号单引号没有区别,因此我们可以改成单引号',省去转换还增加了可读性,然后url()中使用双引号"包起来。

于是,下面的CSS加内嵌SVG代码就能在IE浏览器下正常工作:

.icon-arrow-down {
    width: 20px; height: 20px;
    background: url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' width='200' height='200' viewBox='0 0 200 200'%3E%3Cpath fill='%2300A5E0' d='M145.659,68.949c-5.101-5.208-13.372-5.208-18.473,0L99.479,97.233 L71.772,68.949c-5.1-5.208-13.371-5.208-18.473,0c-5.099,5.208-5.099,13.648,0,18.857l46.18,47.14l46.181-47.14 C150.759,82.598,150.759,74.157,145.659,68.949z'/%3E%3C/svg%3E") no-repeat center;
    background-size: 100%;
}

结果IE9+浏览器下SVG背景也能正常显示

 

可以看到,部分转义后的SVG代码既满足的兼容性要求,同时可读性也有了进一步的提高。

例如,我们想改变此SVG图标的颜色,就简单了,直接把fill='%2300A5E0'这里的00A5E0改成我们希望的十六进制颜色值即可。要是以前的Base64格式,改颜色是不可能的,需要该原始SVG并重新生成一下,特麻烦。

而且,直接原始SVG内嵌,即使有部分转义,其字符大小也明显比Base64格式要小。因此,理论上,CSS中内联SVG背景图,没有使用Base64格式的理由。但,目前大规模使用还有一个阻碍,就是缺少转换的工具,总不可能每次我们都手动修改需要转义的字符吧。

不要担心,工具是会有的,我这就给大家写一个,自带压缩和转义。


转义的JS代码其实很简单,示意如下:

var encodeSvg = function (str) {
    return "data:image/svg+xml," + str.replace(/"/g,"'").replace(/%/g,"%25").replace(/#/g,"%23").replace(/{/g,"%7B").replace(/}/g,"%7D").replace(/</g,"%3C").replace(/>/g,"%3E");
}

 

推荐阅读