svg转base64
昨天研究水印生产,其中使用到svg转base64,要用到 btoa 时,但是发现调用 btoa 之前都会再执行一个转码的api (注:有个跟它相反的** atob,两者功能正好相反)
window.btoa(unescape(encodeURIComponent(svgStr)))
我测试了一个例子,发现 unescape(encodeURIComponent(svgStr)) 貌似会对字符串里面的中文进行转码,后面查了一下 btoa 发现它接收的参数必须ASCII 码,如果里面有中文或者特殊字符会报错
btoa('我')
// Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range.
如果是普通的字符串转base64,直接调用 encodeURIComponent 即可,但是像转svg这种就不能用这个方法,因为这样就无法显示图片了,所以需要对svg字符串里面的特殊字符转 ASCII 码,注意是只针对特殊字符
encodeURIComponent 针对非ASCII码返回的是utf-8 转义序列与%拼接的字符串,ASCII码则返回%与16进制数字,在编码ASCII时,encodeURIComponent 与 escape 返回是一样的
escape 编码非ASCII码时,返回的 %u与16进制的数据,这是它跟 encodeURIComponent 最大的区别。当前二者编码范围也不一样:
encodeURIComponent 不编码范围 A-Z a-z 0-9 - _ . ! ~ * ’ ( )
escape 不编码范围 A-Z a-z 0-9 @*_±./
encodeURIComponent('<') === escape('<') === '%3c'
encodeURIComponent('我') // "%E6%88%91",由此可以看出utf-8编码中一个汉字占了三个字节(有些汉字占4个字节)
escape('我') // '%u6211'
看了上面编码的对比以及svg里面的字符串的格式(svg标签以及标签内的属性都是ASCII码)。现在只需要解决非ASCII码的编码问题,最简单的方式:
unescape(encodeURIComponent(svgStr))
通过正则的方式:
// 过滤ASCII码外的字符,并转成浏览器可识别的转义序列,16进制的转义序列前缀是 &#x
// ASCII编码范围是 0-255,用16进制表示就是 00-ff
// 16进制转10进制 parseInt('ff', 16) // 255
function filterUnicodeToAscii(string) {
return string.replace(/[^\x00-\xff]/g, function (str) {
return '&#x' + str.charCodeAt().toString(16) + ';'
})
}
filterUnicodeToAscii(svgStr)
以上就是全部内容了,其实关于字符串还有很多知识,但是文章有点乱,就不补充了。ps:本篇文章断断续续写了一周
以下是常用的字符串转码方法:
中文字符转Unicode码
function toUnicode(str){
return str.replace(/[^\u0000-\u00FF]/g,function($0){
return escape($0).replace(/(%u)(\w{4})/gi,"\\u$2");
});
}
//escape 已经从标准中删除,但是Unicode主要是获取字符的16进制,可以用 charCodeAt().toString(16) 来获取
//'我'.charCodeAt().toString(16) // 6211
//如果获取的16进制小于4位,则需要补0,下面这种情况就要补0
//'?'.charCodeAt().toString(16) // 3f
// 下面是完整的代码
function toUnicode(str){
if(!str && typeof str === string) {
throw('请传入长度大于1的字符串')
}
const prefixed = '\u';
const codes = '';
for(let s of str) {
codes += prefixed + s.charCodeAt().toString(16).padStart(4, '0');
}
return codes;
}
10进制与16进制互转
// 数字转16进制
const n = 233;
n.toString(16) // e9
// 16进制转数字
const s = 'e9'
parseInt(s, 16) // 233
回忆一下encodeURIComponent 和 encodeURI
encodeURIComponent 和 encodeURI 区别在于前者会额外对特殊字符进行编码,比如下面这种情况就只能用encodeURIComponent,如果没有对这部分进行编码,则查询的时候后面的部分就无法查询到
const value = 'test&a=1'
'http://www.baidu.com/s?word=' + encodeURIComponent(value)
encodeURIComponent(value) // "test%26a%3D1"
encodeURI(value) // "test&a=1"