All Projects → convertSvg → svgtodataurl

convertSvg / svgtodataurl

Licence: other
SVG 作为 data URI使用

Programming Languages

CSS
56736 projects

探究 dataURI 中使用 SVG 正确姿势

为了减少首页的请求数量,按照以往的思路,会直接将 SVG 转换为 base64 后插入了 CSS 文件中。代码可能是这样的:

.svg {
    background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMjUwOCIgaGVpZ2h0PSIyNTIuNyIgdmlld0JveD0iMCAwIDI1MDggMjUyLjciPjxwb2x5Z29uIHBvaW50cz0iNCwyNTIuNyAyNTA0LDAgMjUwNCwyNTIuNyIgc3R5bGU9ImZpbGw6I2U2ZWJlYSIgLz48L3N2Zz4=');
}

初步开发完成后,为了进一步优化代码,在查询资料时读到了这篇文章:Optimizing SVGs in data URIs。参考文章内容进行优化之后:

.svg {
    background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.1'%3E%3Cpolygon points='4,252.7 2504,0 2504,252.7' style='fill:%23e6ebea'/%3E%3C/svg%3E");
}

对比这两段代码,最明显的效果是代码量减少了一半多,而且代码非常清晰,几乎就是 SVG 原代码,日后如果有一些细微的需求变更,比如更改填充色,可以直接在代码里修改无需进行编码转换。

在 dataURI 中使用 SVG 的最佳方法 Optimizing SVGs in data URIs

不久前,CSS-Tricks 发表了 "Probably Don't Base64 SVG",得出结论:如果你在 data URI 中直接使用 SVG,数据量会比转化成 base64 编码格式时小。

这个观点是正确的,但是这里还有一些复杂的地方以及可优化的空间。

更好的浏览器兼容性

例如下面这段代码:

.bg {
    background: url('data:image/svg+xml;utf8,<svg ...> ... </svg>');
}

在那些流行于 web 开发者中的浏览器中是有效的,但是在 IE 中则无法正常工作。因为从技术角度来说这是一种畸形的 data URI,而 IE 很严格(原文: This is because technically it's a malformed data URI, and IE is being strict.)。

RFC 2397 定义了 data URI:

URL 的形式:

  data:[<mediatype>][;base64],<data>

<mediatype> 描述数据的 MIME 类型,;base64 的出现意味着数据被编码成 base64 格式。如果没有声明;base64,对于 URL 安全字符使用 ASCII 编码,而安全范围以外的字符则使用十六进制数编码为%xx格式。如果省略<mediatype>,默认为text/plain;charset=US-ASCII

换句话说,根据标准,只有如下两种编码 data URI 的方法是有效的:

  • 1.data:mime/type;base64,[actual data]:base64 编码,更适合于二进制数据(PNG,fonts,SVGZ 等等)
  • 2.data:mime/type;charset=[charset],[actual data]:URL 编码的普通文本,更适合与文本标记语言(SVG,HTML等)

所以,把一个 SVG 文件编码为 data URL 的正确方式为 data:image/svg+xml;charset=utf8,[actual data]。我猜大部分浏览器对是否存在charset=字符串比较宽容,但是在 IE 浏览器里是必须的。为了代码的最大兼容性(例如一些小众浏览器,邮件客户端,等等),它应该被包含在内。

但这并不是全部。记得这段话么?

如果没有声明;base64,对于 URL 安全字符使用ASCII 编码,而安全范围以外的字符则使用十六进制数编码为%xx格式。如果省略<mediatype>,默认为text/plain;charset=US-ASCII

<?xml version="1.0" encoding="utf-8"?>
<!-- Generated by IcoMoon.io -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" viewBox="0 0 512 512"><g id="icomoon-ignore">
</g>
<path d="M224 387.814v124.186l-192-192 192-192v126.912c223.375 5.24 213.794-151.896 156.931-254.912 140.355 151.707 110.55 394.785-156.931 387.814z"></path>
</svg>

svg优化方面,我们可使用 SVGO 来优化我们的 SVG 文件(如果你更习惯图形界面,GUI 版本:SCGOMG)。结果是这样的:

<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 512 512"><path d="M224 387.814V512L32 320l192-192v126.912C447.375 260.152 437.794 103.016 380.93 0 521.287 151.707 491.48 394.785 224 387.814z"/></svg>

文件小了很多!而且如果你打算用 CSS 来设定图像的尺寸,你还可以去掉widthheight属性让代码更简洁。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M224 387.814V512L32 320l192-192v126.912C447.375 260.152 437.794 103.016 380.93 0 521.287 151.707 491.48 394.785 224 387.814z"/></svg>

现在,我们把精简后的 SVG 丢进 URL 编码器,会得到这样的东西:

%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%3Cpath%20d%3D%22M224%20387.814V512L32%20320l192-192v126.912C447.375%20260.152%20437.794%20103.016%20380.93%200%20521.287%20151.707%20491.48%20394.785%20224%20387.814z%22%2F%3E%3C%2Fsvg%3E

目前这是唯一能在 IE 中工作的版本。非常明显,这甚至比 base64 编码过后的 SVG 都要长:

PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBkPSJNMjI0IDM4Ny44MTRWNTEyTDMyIDMyMGwxOTItMTkydjEyNi45MTJDNDQ3LjM3NSAyNjAuMTUyIDQzNy43OTQgMTAzLjAxNiAzODAuOTMgMCA1MjEuMjg3IDE1MS43MDcgNDkxLjQ4IDM5NC43ODUgMjI0IDM4Ny44MTR6Ii8

“引号是关键”

你可能注意到了,Chris 使用单引号(')来界定 data URIs。这是因为他的 SVG 文件未编码时使用双引号(")来包裹属性值,为了避免冲突而使用了单引号来代替。这一点点微小的改变其实是真正精简 data URI 的关键。

"' 都是有效的属性分隔符(即:attribute="value"attribute='value' 都有效),但是只有'可以直接在 URL 中使用而无须编码转换。现在我们替换双引号,编码<>,得到:

%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M224 387.814V512L32 320l192-192v126.912C447.375 260.152 437.794 103.016 380.93 0 521.287 151.707 491.48 394.785 224 387.814z'/%3E%3C/svg%3E

所以,当你把 SVG 作为 data URI使用时:

用单引号替换包裹属性值的双引号 编码 <>#,和剩余的 " (例如在文本内容中的双引号),以及其他一直的不安全 URL 字符(例如 %) 使用双引号来分隔 data URI(<img src="">url("")

我们通过 SASS 中实现了这个算法,使整个流程变得非常简单:

@function svg-url($svg){
    @if not str-index($svg,xmlns) {
        $svg: str-replace($svg, '<svg','<svg xmlns="http://www.w3.org/2000/svg"');   
    }           
    $encoded:'';
    $slice: 2000;
    $index: 0;
    $loops: ceil(str-length($svg)/$slice);
    @for $i from 1 through $loops {
        $chunk: str-slice($svg, $index, $index + $slice - 1); 
        $chunk: str-replace($chunk, '%', '%25');
        $chunk: str-replace($chunk, '"', '%22');
        $chunk: str-replace($chunk, "'", '%27');
        $chunk: str-replace($chunk, '&', '%26');
        $chunk: str-replace($chunk, '#', '%23');       
        $chunk: str-replace($chunk, '{', '%7B');
        $chunk: str-replace($chunk, '}', '%7D');         
        $chunk: str-replace($chunk, '<', '%3C');
        $chunk: str-replace($chunk, '>', '%3E');     
        
        $encoded: #{$encoded}#{$chunk};
        $index: $index + $slice; 
    }
    @return url("data:image/svg+xml,#{$encoded}");   
}
            
@mixin background-svg($svg){
    background-image: svg-url($svg);        
}        
            
@function str-replace($string, $search, $replace: '') {
    $index: str-index($string, $search); 
    @return if($index, 
        str-slice($string, 1, $index - 1) + $replace + 
        str-replace(str-slice($string, $index + 
        str-length($search)), $search, $replace), 
        $string); 
}                          

以上就是如何得到能够在 IE (以及标准)中使用最精简的 data URI。总结一下:

base64 编码

data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBkPSJNMjI0IDM4Ny44MTRWNTEyTDMyIDMyMGwxOTItMTkydjEyNi45MTJDNDQ3LjM3NSAyNjAuMTUyIDQzNy43OTQgMTAzLjAxNiAzODAuOTMgMCA1MjEuMjg3IDE1MS43MDcgNDkxLjQ4IDM5NC43ODUgMjI0IDM4Ny44MTR6Ii8+PC9zdmc+

完全 URL 编码

data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20512%20512%22%3E%3Cpath%20d%3D%22M224%20387.814V512L32%20320l192-192v126.912C447.375%20260.152%20437.794%20103.016%20380.93%200%20521.287%20151.707%20491.48%20394.785%20224%20387.814z%22%2F%3E%3C%2Fsvg%3E

最大程度优化的 URL 编码

data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E%3Cpath d='M224%20387.814V512L32 320l192-192v126.912C447.375 260.152 437.794 103.016 380.93 0 521.287 151.707 491.48 394.785 224 387.814z'/%3E%3C/svg%3E

我们测试发现,结果它在 IE9+ 以及 安卓3.x 以上的浏览器中都能够完美显示。

参考资料:

Optimizing SVGs in data URIs

Probably Don’t Base64 SVG

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].