背景
在微信小程序开发过程中难免会遇到须要应用多色icon的场景,我的项目中的icon个别寄存在iconfont上。
iconfont有三种援用形式(参考https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.d8cf4382a&helptype=code)
- unicode援用:因为是字体,所以不反对多色
- font-class援用:实质上还是应用的字体,所以多色图标还是不反对的
- symbol援用:反对多色,应用svg渲染
然而微信小程序iconfont并不反对<svg>
标签,只反对以background模式渲染.svg文件。基于此,能够应用Node编写脚本,将iconfont我的项目里的多色icon下载到我的项目中,生成
.arrow-down--colorful { background: url('./arrow-down.[hash].svg');}// 或者有CDN的能够.arrow-down--colorful { background: url('//[cdn]/imgs/arrow-down.[hash].svg');}
步骤
步骤一、 上传icon至iconfont
留神在上传时,带色彩的图标须要有固定后缀来辨别。例如一般icon为icon-name,则带色彩的icon须要为icon-name--colorful
步骤二、脚本下载icon_**.css
应用Node下载我的项目对应的css脚本,格局为:
@font-face {font-family: "iconfont"; src: url('//at.alicdn.com/t/font_**.eot?t=1571134856668'); src: url('//at.alicdn.com/t/font_385238_**.eot?t=1571134856668#iefix') format('embedded-opentype'), url('data:application/x-font-woff2;**') format('woff2'), url('//at.alicdn.com/t/font_**.woff?t=**') format('woff'), url('//at.alicdn.com/t/font_**.ttf?t=**') format('truetype'), url('//at.alicdn.com/t/font_385238_**.svg?t=**#iconfont') format('svg');}.iconfont { font-family: "iconfont" !important; ...}.icon-name:before { content: "\e600";}.icon-name--colorful:before { content: "\e653";}
删除其中无用的font格局,以及带--colorful的css定义。应用脚本将其格式化为
@font-face {font-family: "iconfont"; src: url('data:application/x-font-woff2;**') format('woff2');}.iconfont { font-family: "iconfont" !important; ...}.icon-name:before { content: "\e600";}
步骤三、脚本下载icon_**.js
应用Node下载我的项目对应的js脚本,格局为:
!function(o){var t,l='<svg><symbol id="icon-name” viewBox="0 0 1024 1024"><path …>…</path></symbol>symbol id="icon-name—colorful” viewBox="0 0 1024 1024"><path …>…</path></symbol></svg>’,…}(window);
提取出其中带色彩(id以--colorful结尾)的<symbol></symbol>标签,应用xml解析工具,失去
<path d="..." fill="..." ></path><path d="..." fill="..." ></path>
留神:如果步骤一不能实现依照--colorful辨别是否是多色icon,则须要剖析各个path的色彩是否统一来辨别是否是多色。
拼接上<svg></svg>
标签
function getHash(cont: string) { return cryptoNode .createHash('md5') .update(cont) .digest('hex') .substr(0, 8);}
步骤四、生成hash值及图片文件
最终失去的svgStr的hash值
function getHash(cont: string) { return cryptoNode .createHash('md5') .update(cont) .digest('hex') .substr(0, 8);}
应用hash值是为了防止更换icon然而不换名字的场景
生成本地图片文件
const hash = getHash(svgStr);const fileName = `${iconId}.${hash}.svg`;const filePath = `${stylePath}${fileName}`;fs.writeFileSync(filePath, svgStr);
步骤五、将图片上传至CDN并删除本地文件(可选)
如果有CDN资源能够将图片上传至CDN,能够节约打包出的我的项目体积
uploadFileToCDN(filePath);/** 将pathWithHash文件上传到CDN */async function uploadFileToCDN(pathWithHash: string) { return new Promise((resolve, reject) => { exec( `${此处为上传至CDN的命令行}`, (error: any, stdout: any, stderr: any) => { if (error) { console.error(`exec error: ${error}`); reject(error); return; } resolve(''); /** 删除文件 */ fs.unlinkSync(pathWithHash); } ); });}
步骤六、生成残缺的css内容并写入本地
给步骤二的css文件拼接上带色彩图片的css内容
@font-face {font-family: "iconfont"; src: url('data:application/x-font-woff2;**') format('woff2');}.iconfont { font-family: "iconfont" !important; ...}.icon-name:before { content: "\e600";}.icon-name--colorful:before { background: url('cdnUrl');}
并写入本地
fs.writeFileSync(cssPath, fileContent.replace(/"/g, "'"), 'utf8');
残缺的脚本文件示例
const http = require('http');const cryptoNode = require('crypto');const { exec } = require('child_process');const xml2js = require('xml2js');const fs = require('fs');const config = { url: '//at.alicdn.com/t/font_***.js'};// 上传至CDN后的前缀const cdnPath = '***';/** 生成的css文件path */const cssPath = 'src/styles/iconfont.scss';const stylePath = 'src/styles/';const { parseString } = xml2js;// 替换iconfont.scssconst iconfontUrl = config.url.replace(/.*\/\//, 'http://');const iconfontUrl_css = iconfontUrl.replace('.js', '.css');let fileContent = '';type IPath = { $: { [key: string]: string; };};type ISymbol = { $: { id: string; viewBox: string; }; path: IPath[];};/** 读取css文件,去掉近程连贯 */async function generateFile() { const cssData = await readRemoteFile(iconfontUrl_css); fileContent = cssData.replace(/[^,;]*at.alicdn.com.*\*\//g, ''); // 替换掉woff fileContent = fileContent.replace(/[^)]*at.alicdn.com.*\),/g, ';'); // 替换掉colorful fileContent = fileContent.replace(/[^}]*colorful[^}]*}/g, ''); // 替换src fileContent = fileContent.replace('url(', 'src: url('); // 加换行 fileContent = fileContent.replace('{font-family', '{\n font-family'); // 加换行 fileContent = fileContent.replace(') format(', ')\n format('); // 去除最初一个换行 fileContent = fileContent.replace(/\n$/, ''); // 有色彩的图标生成background: url('cdnUrl') const fontXml = await fetchXml(iconfontUrl); fontXml.svg.symbol.forEach((item: ISymbol) => { const iconId = item.$.id; if (/colorful/.test(iconId)) { let svgStr = ''; item.path.forEach((path: IPath) => { const keys = Object.keys(path.$); let attrStr = ''; keys.forEach(k => { attrStr += `${k}="${path.$[k]}" `; }); svgStr = `${svgStr}<path ${attrStr}></path>`; }); svgStr = `<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em">${svgStr}</svg>`; const hash = getHash(svgStr); const fileName = `${iconId}.${hash}.svg`; const filePath = `${stylePath}${fileName}`; fs.writeFileSync(filePath, svgStr); uploadFileToCDN(filePath); svgStr = `\n.${iconId} {\n background: url('${cdnPath}${fileName}');\n}\n`; fileContent += svgStr; } });}function getHash(cont: string) { return cryptoNode .createHash('md5') .update(cont) .digest('hex') .substr(0, 8);}async function uploadAndUpdate() { // 写文件 fs.writeFileSync(cssPath, fileContent.replace(/"/g, "'"), 'utf8');}generateFile().then(() => { uploadAndUpdate();});function readRemoteFile(remoteUrl: string): Promise<string> { let _data = ''; return new Promise(resolve => { http.get(remoteUrl, (res: any) => { //数据 res.on('data', function(chunk: string) { _data += chunk; }); res.on('end', function() { resolve(_data); }); }); });}async function fetchXml( url: string): Promise<{ svg: { symbol: ISymbol[]; };}> { const data = await readRemoteFile(url); const matches = String(data).match(/'<svg>(.+?)<\/svg>'/); return new Promise((resolve, reject) => { parseString(`<svg>${matches ? matches[1] : ''}</svg>`, (err: any, result: any) => { if (err) { reject(err); } else { resolve(result); } }); });}/** 将pathWithHash文件上传到CDN */async function uploadFileToCDN(pathWithHash: string) { return new Promise((resolve, reject) => { exec(`${此处为上传至CDN的命令行}`, (error: any, stdout: any, stderr: any) => { if (error) { console.error(`exec error: ${error}`); reject(error); return; } resolve(''); /** 删除文件 */ fs.unlinkSync(pathWithHash); console.log(`stdout: ${stdout}`); console.error(`stderr: ${stderr}`); }); });}
再在package.json
中的scripts中配置
"icon": "ts-node scripts/iconfont.ts"
则每次更新iconfont后,更新对应的url,再运行npm run icon
即可