项目里的功能越多,用到的svg就会越多,以至于写一个标准化svg图标内容的脚本变得很有必要。
根据svg复用的方式,采用symbol和use来引用svg。
<svg class="icon" aria-hidden="true">
<use xlink:href="#icon-xxx"></use>
</svg>
- 这样写的前提就是需要有js脚本提前注入,这是在node服务中模拟的获取脚本文件的接口,目的是为了生成这么一个js,在执行的时候,将所有格式化后的svg图标拼接并注入到svg中而达到直接use使用的目的。
async downloadSvgJs(iconIdList,prefix) {
let js=`
let symbolSvgs = '<symbol>template</symbol>';
let svgSprite = "<svg>" + symbolSvgs + "</svg>";
let script = function () {
let scripts = document.getElementsByTagName("script");
return scripts[scripts.length - 1];
}();
let shouldInjectCss = script.getAttribute("data-injectcss");
let before = function (el, target) {
target.parentNode.insertBefore(el, target);
}
let prepend = function (el, target) {
if (target.firstChild) {
before(el, target.firstChild);
} else {
target.appendChild(el);
}
};
// 最终返回一个append方法
function appendSvg() {
let div, svg;
div = document.createElement("div");
div.innerHTML = svgSprite;
svgSprite = null;
svg = div.getElementsByTagName("svg")[0];
if (svg) {
svg.setAttribute("aria-hidden", "true");
svg.style.position = "absolute";
svg.style.width = 0;
svg.style.height = 0;
svg.style.overflow = "hidden";
prepend(svg, document.body);
}
}
if (shouldInjectCss && !window.__iconfont__svg__cssinject__) {
window.__iconfont__svg__cssinject__ = true;
try {
document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")
} catch (e) {
console && console.log(e)
}
}
export default appendSvg;`
// 以下为模拟的从数据库获取所有图标的操作,可自行跳过
if (iconIdList.length > 0) {
const sqlitedb = new SqliteDB(dateBase);
let sql = `SELECT * FROM icon WHERE`
iconIdList.forEach((item, index) => {
index === 0 ? sql += ` ICON_ID =${item}` : sql += ` OR ICON_ID =${item}`
})
let res = await sqlitedb.selectAll(sql);
let allSvg=''
res.map(item => {
item.ICON_CONTENT = svgFormat(item.ICON_CONTENT).replace(/svg/g, 'symbol').replace(/[\r\n]/g, "")
item.ICON_CONTENT=item.ICON_CONTENT.slice(0,7)+` id="svg-${item.ICON_KEY}" `+item.ICON_CONTENT.slice(7)
allSvg+=item.ICON_CONTENT
})
let result=js.replace('<symbol>template</symbol>',allSvg)
return Promise.resolve(Buffer.from(result, 'utf-8'))
} else {
return Promise.resolve(Buffer.from(js.replace('<symbol>template</symbol>',''), 'utf-8'))
}
}
- svg格式化:上面的代码中,svgFormat方法对每个svg图标进行了格式化,用到了一些简单的辅助函数,具体操作如下:
/**
* @svgFormat 格式化svg字符串
* @param {String} string svg字符串
*/
function svgFormat(string) {
let viewBoxAttr = getAttr('viewBox', string)
let styleAttr = getAttr('style', string)
let styleTag = ''
if (string.indexOf('<style') > 0) { //分割出该svg的style标签
styleTag = getTag('<style type="text/css">', '</style>', string)
}
// 分割出<svg...></svg>标签内所有内容
let svgBeginStr = string.match(/<svg[^<>]+>/g)[0] //匹配出<svg ...>
let begin = string.indexOf(svgBeginStr) + svgBeginStr.length
let lastIndex = string.lastIndexOf('</svg')
content = string.substr(begin, lastIndex).split('</svg>')[0] //获取svg图像内容
//去掉style标签
let styleLeft = content.indexOf('<style')
let styleRight = content.indexOf('</style>')
content = content.indexOf('<style') > 0 ? content.substr(0, styleLeft) + content.substr(styleRight + '</style>'.length) : content
//手动插入class样式
let classObj = {}
let classArr = styleTag.match(/.st\d{.+}/g) //匹配出style标签所有class
if (classArr && classArr.length > 0) {
classArr.map(item => {
let key = item.split('{')[0].split('.')[1]
let value = item.split('{')[1].split('}')[0]
classObj[key] = value
})
for (let key in classObj) { //找出对应class 并拼接style 字符串
content = getContent(key, classObj, content, 0) //递归插入有相同class的标签style
}
}
let res =
`<svg class="icon" style="${styleAttr} " viewBox="${viewBoxAttr}" version="1.1" xmlns="http://www.w3.org/2000/svg">${content}</svg>`
return res
}
/**
* @getContent 递归插入有相同class的标签style
* @param {String} key 类名
* @param {Object} classObj 类名和style映射对象
* @param {String} content 要处理的字符串内
* @param {String} startIndex 开始查找的index
*/
function getContent(key, classObj, content, startIndex) {
let classIndex = content.indexOf(`"${key}"`, startIndex)
if (classIndex > 0) { // 若后面还有同一class则继续递归插入style
let insertIndex = classIndex + key.length + 2
let res = content.slice(0, insertIndex) + ` style="${classObj[key]}"` + content.slice(insertIndex)
return getContent(key, classObj, res, insertIndex)
} else { //没有 则返回内容
return conten
}
}
/**
* @getTag 获取svg标签及内容
* @param {String} start 开始标签
* @param {String} end 结束标签
* @param {String} str svg字符串
* @param {Boolean} getAll 是否获取多个该标签
*/
function getTag(start, end, str, getAll = false) {
let begin = str.indexOf(start)
let temp = str.substr(begin)
if (getAll) {
let lastIndex = temp.lastIndexOf(end)
return temp.substr(0, lastIndex) + end
} else {
return temp.split(end)[0] + en
}
}
/**
* @getAttr 获取svg单个属性
* @param {String} attr 属性名
* @param {String} str svg字符串
*/
function getAttr(attr, str) {
let begin = str.indexOf(`${attr}="`)
let temp = str.substr(begin)
let res = temp.split(`"`)[1]
return res
}
经过最前面downloadSvgJs方法获取的js文件,在工程中引入执行该脚本,就可以对所有格式化后的svg进行复用,再也不用在工程目录下看到一大坨的svg文件了。(ps:如果svg无法正常显示,那就要问问你们的UI小姐姐提供的svg是不是有点什么毛病了)