一直觉得 内联SVG 是 HTML 的延伸,但是内联SVG往往会造成HTML结构的臃肿。于是笔者尝试着把SVG 移植到 CSS中,如下:
background: url( <svg xmlns="http://www.w3.org/2000/svg" width="393.969" height="28.219" viewBox="0 0 393.969 28.219"> <path d="M0.969,11.313 C-20.207,69.711 314.183,-47.984 393.969,24.312 "/> </svg> )
目前(2017.01.12)而言, 没有任何浏览器的 CSS 支持内联SVG代码 ,所以上述的代码是一段非法的css。尽管任何浏览器的 CSS 都不支持内联SVG代码,但是 所有的现代浏览都是支持内联SVG文件 ,如下:
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3NTAgNDAwIj4gCQkJPHBhdGggZD0iTTAsMCBMNzUwLDAgTDc1MCw0MDAgTDM4OCw0MDAgTDM3NSwzODcsIEwzNjIsNDAwIEwwLDQwMFoiIGZpbGw9IiMwZjAiPjwvcGF0aD4gCQk8L3N2Zz4=);
A new URL scheme, “data”, is defined. It allows inclusion of small data items as “immediate” data, as if it had been included externally.
摘录自: https://tools.ietf.org/html/rfc2397
简单地说,Data-URI 是一种将小文件转换成直接字面数据的方案(scheme)。Data-URI 的语法如下:
data:[<media type>][;base64],<data>
<media type>
: 即指定嵌入数据的MIME,对于PNG的图片,其格式是:image/png,如果没有指定,默认是:text/plain;
[;base64]
: base64编码扩展,非必选项。如果没指定 base64 编码扩展,后面的 <data>
将使用 URL
编码(即百分号编码)。
<data>
: 编码后的小文件数据。 图片文件则是对二进制文件流进行编码,文本文件则是对文本进行编码。
附注:Data-URI 是正式术语,日常中更常用的名字是 Base64。
sass-svg
是一个 将SVG代码转化为 Data-URI 的 Sass 库 。
了解 Data-URI 后,sass-svg 的作用其实就是把 SVG 进行编码的过程。
在 JS
中可以使用 encodeURI
或 encodeURIComponent
来对字符进行 URL编码,而 Sass 并没有相对就的编码方法。
了解一下 URL编码的知识点:
0x61
,那么 URL 编码后得到 %61
。 a-zA-Z
)、数字( 0-9
)、 -_.~4
个特殊字符以及保留字符( ! * ' ( ) ; : @ & = + $ , / ? # [ ]
)。 尽管 Sass 不提供获取 ASCII 码的函数,但创建一张 ASCII字符 与 ASCII码 的对照表 (map) 却是廉价的。标准的ASCII字符一共是 128 个,剔除 rfc3986 规定的不需要编码的字符(84个),再把扣除一些控制符,实际上需要编码的字符只有 13 个,如下:
// ascii码 与 16进制对照表 $asciiHexMap: ( " ": "%09", " ": "%20", "/"": "%22", "%": "%25", "/": "%2f", "<": "%3c", ">": "%3e", "//": "%5c", "^": "%5e", "`": "%60", "{": "%7b", "|": "%7c", "}": "%7d" );
实现 URL编码要求 sass 版本3.3以上,因为需要用到三个内置函数: str_index
、 str_slice
和 str_insert
。 实现URL编码的过程不详述,笔者已经将相关代码放到 GIT 仓库,有兴趣的同学可以访问 「 sass-svg 」。
注意:本文所描述的编码仅针对 US-ASCII 字符集。中文或其它字符不讨论
Base64 使用 US-ASCII 子集的64个字符,即大小写的26个英文字母,0~9,+,/。如下:
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
Base64编码总是基于3个字符,每个字符用8位二进制表示,因此一共24位,再分为4四组,每组6位表示一个 Base64 的值。如果不足3个字符,仍将每个字符对应的二进制串起,再按每组6位表示一个 Base64的值,不足6位的用 0
补全,最后生成的 Base64码不足4位用 =
补全。
举个例子来感受一下:
与 URL编码相似,SASS 无法直接获取 ASCII字符对应的二进制,也无法将6位二进制转成 Base64码。这意味着需要手动创建两张对照表(map)。如下:
// ascii字符 与 8位二进制 对照表 $asciiMap: ( " ": "00001001", " ": "00100000", "!": "00100001", "/"": "00100010", "#": "00100011", "$": "00100100", "%": "00100101", "&": "00100110", "'": "00100111", "(": "00101000", ")": "00101001", "*": "00101010", "+": "00101011", ",": "00101100", "-": "00101101", ".": "00101110", "/": "00101111", "0": "00110000", "1": "00110001", "2": "00110010", "3": "00110011", "4": "00110100", "5": "00110101", "6": "00110110", "7": "00110111", "8": "00111000", "9": "00111001", ":": "00111010", ";": "00111011", "<": "00111100", "=": "00111101", ">": "00111110", "?": "00111111", "@": "01000000", "A": "01000001", "B": "01000010", "C": "01000011", "D": "01000100", "E": "01000101", "F": "01000110", "G": "01000111", "H": "01001000", "I": "01001001", "J": "01001010", "K": "01001011", "L": "01001100", "M": "01001101", "N": "01001110", "O": "01001111", "P": "01010000", "Q": "01010001", "R": "01010010", "S": "01010011", "T": "01010100", "U": "01010101", "V": "01010110", "W": "01010111", "X": "01011000", "Y": "01011001", "Z": "01011010", "[": "01011011", "//": "01011100", "]": "01011101", "^": "01011110", "_": "01011111", "`": "01100000", "a": "01100001", "b": "01100010", "c": "01100011", "d": "01100100", "e": "01100101", "f": "01100110", "g": "01100111", "h": "01101000", "i": "01101001", "j": "01101010", "k": "01101011", "l": "01101100", "m": "01101101", "n": "01101110", "o": "01101111", "p": "01110000", "q": "01110001", "r": "01110010", "s": "01110011", "t": "01110100", "u": "01110101", "v": "01110110", "w": "01110111", "x": "01111000", "y": "01111001", "z": "01111010", "{": "01111011", "|": "01111100", "}": "01111101", "~": "01111110" );
// 二进制 与 base64码 对照表 $base64map: ( "000000": 'A', "000001": 'B', "000010": 'C', "000011": 'D', "000100": 'E', "000101": 'F', "000110": 'G', "000111": 'H', "001000": 'I', "001001": 'J', "001010": 'K', "001011": 'L', "001100": 'M', "001101": 'N', "001110": 'O', "001111": 'P', "010000": 'Q', "010001": 'R', "010010": 'S', "010011": 'T', "010100": 'U', "010101": 'V', "010110": 'W', "010111": 'X', "011000": 'Y', "011001": 'Z', "011010": 'a', "011011": 'b', "011100": 'c', "011101": 'd', "011110": 'e', "011111": 'f', "100000": 'g', "100001": 'h', "100010": 'i', "100011": 'j', "100100": 'k', "100101": 'l', "100110": 'm', "100111": 'n', "101000": 'o', "101001": 'p', "101010": 'q', "101011": 'r', "101100": 's', "101101": 't', "101110": 'u', "101111": 'v', "110000": 'w', "110001": 'x', "110010": 'y', "110011": 'z', "110100": '0', "110101": '1', "110110": '2', "110111": '3', "111000": '4', "111001": '5', "111010": '6', "111011": '7', "111100": '8', "111101": '9', "111110": '+', "111111": '/', "======": '=' // base64占位符 );
实现过程不详述。有兴趣可以访问「 sass-svg 」。
开头的非法 CSS,通过 sass-svg
可以改写成:
// scss background: url( sass-svg-base64('<svg viewBox="0 0 393.969 28.219"> <path d="M0.969,11.313 C-20.207,69.711 314.183,-47.984 393.969,24.312 "/> </svg>') )
通过编译最终会生成:
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzOTMuOTY5IDI4LjIxOSI+IAkJICA8cGF0aCBkPSJNMC45NjksMTEuMzEzIEMtMjAuMjA3LDY5LjcxMSAzMTQuMTgzLC00Ny45ODQgMzkzLjk2OSwyNC4zMTIgIi8+IAkJPC9zdmc+");
感谢阅读本文。关于 sass-svg
的应用,笔者会有后续文章介绍,如果喜欢本文请关注一下我们『凹凸实验室』的微信公众号: AOTULabs