一、CSS内联 SVG
一直觉得 内联 SVG 是 HTML 的延伸,但是内联 SVG 往往会造成HTML结构的臃肿。于是笔者尝试着把 SVG 移植到 CSS 中,如下:
background: url(
)
目前(2017.01.12)而言,没有任何浏览器的 CSS 支持内联 SVG 代码,所以上述的代码是一段非法的 css。尽管任何浏览器的 CSS 都不支持内联 SVG 代码,但是所有的现代浏览都是支持内联 SVG 文件,如下:
background: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3NTAgNDAwIj4gCQkJPHBhdGggZD0iTTAsMCBMNzUwLDAgTDc1MCw0MDAgTDM4OCw0MDAgTDM3NSwzODcsIEwzNjIsNDAwIEwwLDQwMFoiIGZpbGw9IiMwZjAiPjwvcGF0aD4gCQk8L3N2Zz4=);
内联 SVG 文件即 Data-URI。
二、Data-URI 简介
A new URL scheme, “data”, is defined. It allows inclusion of small data items as “immediate” data, as if it had been included externally.
摘录自:tools.ietf.org/html/rfc239…
简单地说,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
sass-svg
是一个将 SVG 代码转化为 Data-URI 的 Sass 库。
了解 Data-URI 后,sass-svg 的作用其实就是把 SVG 进行编码的过程。
1. URL编码
在 JS
中可以使用 encodeURI
或 encodeURIComponent
来对字符进行 URL 编码,而 Sass 并没有相对就的编码方法。
了解一下 URL编码的知识点:
- URL 编码的原理:ASCII 字符 = % + 两位 ASCII 码(十六进制)。例如,字符 a 对应的 ASCII 码为
0x61
,那么 URL 编码后得到%61
。 - 「rfc3986」规定 URL 只允许英文字母(
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 字符集。中文或其它字符不讨论
2. Base64 编码
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」。
3. sass-svg 的一个实例
开头的非法 CSS,通过 sass-svg
可以改写成:
// scss
background: url(
sass-svg-base64('
')
)
通过编译最终会生成:
background: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzOTMuOTY5IDI4LjIxOSI+IAkJICA8cGF0aCBkPSJNMC45NjksMTEuMzEzIEMtMjAuMjA3LDY5LjcxMSAzMTQuMTgzLC00Ny45ODQgMzkzLjk2OSwyNC4zMTIgIi8+IAkJPC9zdmc+");
结语
感谢阅读本文。关于 sass-svg
的应用,笔者会有后续文章介绍,如果喜欢本文请关注一下我们『凹凸实验室』的微信公众号:AOTULabs
参考资料
- Data URI scheme - Wikipedia
- RFC 2397 - The “data” URL scheme
- Uniform Resource Identifier (URI): Generic Syntax
- Uniform Resource Locators (URL)
- Base64编码解码算法
- Module: Sass::Script::Functions
- URL编码与解码原理
- Base64编码解码算法