学习了,CSS中内联SVG图片有比Base64更好的形式

这篇文章发布于 2018年08月19日,星期日,19:50,归类于 CSS相关, SVG相关。 阅读 97086 次, 今日 19 次 57 条评论

 

一、之前SVG背景图的做法

CSS内联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%;
}

您可以狠狠地点击这里:SVG代码直接写在CSS中demo

可以看到在Chrome浏览器下,小箭头图标显示出来了:

Chrome浏览器下的下拉箭头

更新于2021-01-06

感谢XboxYan的反馈,目前Chrome浏览器也需要进行转义才能显示了。


然后,不要高兴地太早,我们打开IE浏览器一看,心凉了半截,一片空白!

IE edge下小图标不显示

莫非IE浏览器不支持这种表示方法?

大家莫急莫慌且淡定,IE浏览器是支持直接内联的,只是,IE浏览器出于安全考虑,需要对一些字符进行安全转义。

使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浏览器):

IE下SVG图标完全转义效果截图

眼见为实,您可以狠狠地点击这里:CSS中SVG代码完全转义demo

但是,完全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背景也能正常显示:

IE部分转义SVG显示截图

眼见为实,您可以狠狠地点击这里:SVG转义后的代码直接写在CSS显示demo

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

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

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

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

补充于2020-10-14
转义的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");
}

三、在线SVG压缩和转义工具

SVG在线压缩合并工具

哎呀,不容易呀,周五晚周六晚加周日下午这个页面才弄好。

当当当当,隆重介绍下:SVG在线压缩合并工具

SVG压缩页面截图

支持选择SVG文件上传,支持拖拽上传,也支持直接粘贴SVG代码进行压缩。

例如这个名为download.svg的SVG文件(下载自iconfont.cn),我们上传压缩下,结果有:

压缩结果示意

其中,左边的多行文本域中是压缩后的SVG代码,右边的文本域中是压缩并转义的SVG代码,可以直接用在CSS代码中,也就是本文介绍的这种内联方式,来,实地测试下,点击文本域右上方的复制图标按钮,复制转义代码,放在CSS中,如下:

.icon-download {
    width: 40px; height: 40px;
    background: url("data:image/svg+xml,%3Csvg class='icon' viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='128' height='128'%3E%3Cdefs%3E%3Cstyle/%3E%3C/defs%3E%3Cpath d='M332.32 464.112l144 160a47.952 47.952 0 0 0 71.36 0l144-160a48 48 0 0 0-71.36-64.224L560 466.912V176a48 48 0 1 0-96 0v290.912l-60.32-67.024a48 48 0 1 0-71.36 64.224zM880 560a48 48 0 0 0-48 48v192H192V608a48 48 0 1 0-96 0v240a48 48 0 0 0 48 48h736a48 48 0 0 0 48-48V608a48 48 0 0 0-48-48z'/%3E%3C/svg%3E") no-repeat center/100%;
}

效果棒棒哒!


本工具还支持实时SVG Sprites合并,有时候一些小项目不想搭笨重的Node任务流,就可以试试这个敏捷轻便在线小工具。

四、结束语

这篇文章从周一写到周日,大部分时间就在产出最后那个SVG在线压缩工具上,压缩底层使用的是著名的SVG优化工具svgo(万级别Star项目,很强),所以压缩的质量大家不用担心。

工具页面还有个设置按钮,点击会展开参数配置列表,可以对几十个优化参数进行配置,按钮位置参见下图:

设置按钮

感谢LuLu UI提供UI界面支持,另外点击按钮直接下载SVG文件是纯前端实现的,有兴趣可以参考这篇文章:“JS前端创建html或json文件并浏览器导出下载”。


最后,对本文内容总结下:CSS中内联SVG的时候,没有使用Base64格式的任何理由,请使用部分转义的SVG原始代码代替,如何得到转义SVG代码,可以使用这个SVG在线压缩合并工具

哦,对了,好像微信小程序中Base64的SVG背景并不支持,但是直接SVG原始代码是支持的。

以上~

感谢阅读,欢迎交流与反馈!

(本篇完)

分享到:


发表评论(目前57 条评论)

  1. senlin说道:

    很奇怪,转换工具里rotate生效,但是我项目里不生效.. 不太理解 不知道是不是vue对其编译时做了转换

  2. cpt说道:

    一个个下载太麻烦了,希望加个一键下载的功能

  3. Gwyneth Llewelyn说道:

    I am impressed! I had no idea that this worked so well (and I really, really have those Base64-embedded images…). The “lightly escaped” version — the one that works under IE9+ but is still reasonably readable — is working flawlessly in February 2023 in the latest versions of Brave (= Chrome), Microsoft Edge, Firefox, and Safari, under macOS Big Sur 11.7.3.

    It also works perfectly under iOS 16.2!

    Thank you so much for writing this article (and all the examples you made showing that this does work)!

  4. Youth.霖说道:

    赞,还想要一个将编码后的 base64 还原为 svg 的工具 (逃

    • Gwyneth Llewelyn说道:

      I’m not sure if Google Translator understood your comment fully, but to restore a Base64-encoded SVG, you should be able to copy & paste the Base64-encoded string, and just decode it — say, using an online Base64 decoder: https://decodebase64.com/

      If you use an online decoder, make sure that you use a JavaScript version that does the decoding in your local browser — not on their remote server!

      On the Mac/Linux command line, you should also have the base64 tool (use -d to decode).

  5. 晒壳的小乌龟说道:

    如果想要改变icon的背景颜色还能适用吗?比如说实心三角icon 不同场景的背景颜色不同,这种情况下可以复用一份svg文件吗?

  6. 某某某说道:

    %3Csvg%20width%3D%222%22%20height%3D%222%22%3E%3Cellipse%20ry%3D%220%22%20rx%3D%220%22%20id%3D%22svg_1%22%20cy%3D%222%22%20cx%3D%222%22%20fill%3D%22%23000%22%2F%3E%3C%2Fsvg%3E

    报告 Bug。 上面的代码解码后拖入压缩器,图形消失。

  7. perry.hong说道:

    压缩的时候对于lineargradient id冲突有什么好的解决办法么

  8. 颜海镜说道:

    搞个postcss插件来自动解决ie的兼容性问题?更好的方案?

  9. Kaka说道:

    看到大家都说img、background直接引用可以使用,研究了半天还是不显示啊,不知道什么情况,有没有大神遇到过

  10. provenr说道:

    Chrome 版本 84.0.4147.105(正式版本) (64 位)

    css 直接 添加 svg 不显示, 文中第一个例子就不显示,
    IE 的可以显示,
    工具转化出的 CSS chrome 中也不显示

  11. 波波说道:

    当添加多个文件时,能否增加一键下载所有压缩图片的功能

  12. 我的大名,必填说道:

    赞一个
    这种比较简单的图标,我比较喜欢直接复制Unicode图标。⌄ ˬ ˅ ˅ ➥ ? ⮟

  13. 追少说道:

    妈妈西亚妈妈西亚妈妈西亚妈妈西亚妈妈西亚妈妈西亚

    张老师,你好。好像不支持textPath。

  14. hjc说道:

    background: url(“data:image/svg+xml,%3Csvg ……….这种写法在ios13中会引起页面样式崩溃,不仅仅是这一个dom不显示的问题

  15. lxl说道:

    我在支付宝小程序里使用,结果不带小括号的svg是可以显示的,里面一有小括号,比如有transform=”translate(18 19.791667)”就显示不出来了,这时只有用base64才能显示出来

  16. Maplor说道:

    新版Chrome(77+)中,直接内联svg无法显示,全转义、部分转义、Base64是可以展示的。似乎也是被安全拦截了。

  17. 骆雨说道:

    如果做成一个svg sprite 然后在
    background-image:url(“sprite.svg#xxx”)
    中这样引用会不会更方便呢?

  18. buzz说道:

    我有个疑问,我现在用的比如.icon-arrow-down设置的width和height单位是rem,如果在svg内部定义了width和height,是否有共存的必要?我目前是把svg内的width和height去除掉了。

  19. Tyler说道:

    好像移除ID和viewBox的选项失效了

  20. kouxin说道:

    请问一下,ie9直接写svg元素没有任何问题,但是转成base64就背景图出现麻点,是怎么回事

  21. Kincau2说道:

    強!不留言支持一下不行??

  22. 可仁说道:

    写的真的很棒,不留言支持一下说不过去。

  23. Mac chrome73,无法显示未转码的 svg,转码的 svg 可以正常显示

  24. Panda说道:

    报一个“在线压缩转义工具”的BUG,重现方法:
    1、打开“在线压缩转义工具”
    2、上传一个SVG(可以看到Base64是正常的)
    3、点击页面上的“压缩参数设置-重置所有参数”
    4、Base64变成了转义的CSS代码了,并非是Base64

  25. Nick说道:

    不考虑低版本浏览器,直接background: url(…/XXX.svg) ,会有什么不好的地方么?

  26. Qiang说道:

    您好!最新版本谷歌浏览器(73)无法显示svg,有什么办法可以解决这个问题吗

  27. 思远说道:

    个人觉得此类图标还是做成字体小图标较好

  28. 光源说道:

    svg里面嵌入了一个image标签,转码之后firefox与chrome都无效果,格式检查了,没有错,会不会是因为嵌入的image的问题?

  29. zhifei说道:

    经测试,有点小问题就是,svg用做背景图,如果设置了background-size的话,svg会有问题,而png不存在。

  30. RS说道:

    演示所使用的 SVG 自身就精度有点高……出这个的美工肯开浏览器按 F12 对着 SVG 用删除和撤销去小数点的话,应当可以再小好多。

  31. wingmeng说道:

    去年也捣鼓过这个,但进行到 IE 不显示那一步就没辙了,以为 IE 不支持就放弃了,改回 Base64 了……
    感谢旭哥揭秘!

  32. vfv001说道:

    强赞

  33. buer说道:

    火狐浏览器不支持啊

  34. whj说道:

    目前这种箭头,一般我会采用gradient去实现,相比之下svg、base64和gradient哪种方案会更好?

    • RS说道:

      愿意多一个 div 的话,两个倒三角。
      body { background: white; }
      /*
      .div1, div2 {
      width: 0;
      height: 0;
      }
      */
      .div1 {
      border-left: 8px solid transparent;
      border-top: 8px solid lightblue;
      border-right: 8px solid transparent;
      z-index: 1;
      }
      .div1 > .div2 {
      position: relative;
      top: -8px;
      left: -4px;
      border-left: 4px solid transparent;
      border-top: 4px solid white;
      border-right: 4px solid transparent;
      z-index: 2;
      }

      一切能由 一条边水平或竖直放置的三角形 组成的图形都能这么画出来,只可惜渐变之类效果就不一定好办了。

  35. jackpan说道:

    请问下,这个svg需要转义是IE几,还是所有的版本

  36. fape说道:

    功德无量!!

  37. 前端码农说道:

    https://www.zhangxinxu.com/wordpress/2018/08/js-base64-atob-btoa-encode-decode/
    我在文章回复提到过,也做了个简单的转码工具,不过张大大整成整套一条龙服务,赞!

  38. 广建说道:

    写的很不错,学到了一点

  39. 三月三荷说道:

    nice~

  40. 赵继锐说道:

    旭哥 看完之后也理解了使用svg原始代码的好处 提高了性能而且兼容性也挺好,很感谢。我还有个问题 就是每次换颜色哪个class类名都需要重新写一个吗例如
    icon-arrow-down-red/icon-arrow-down-blue这样的么,你们平时怎么规范的,我就是一个工作经验比较低的小白,希望你别介意。

  41. lulushow说道:

    请问小程序中字体图标用什么方式最佳?