背景

在微信小程序开发过程中难免会遇到须要应用多色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即可