批量处理 SVG 图标文件,最终自动生成可直接在前端项目中使用的 TypeScript 图标库,仅仅只是个思路。
入口代码
TypeScript
import fs from 'fs-extra'
import path from 'path'
improt { optimize } from 'svgo'
import xml from 'xml-js'
import prettier from 'prettier'
const iconsPath = path.resolve(basePath, type);
let funcResult = `...`; // 初始导入语句
fs.readdirSync(iconsPath).forEach((item) => {
// 读取SVG
const str = fs.readFileSync(...);
// 压缩svg
const temp = optimize(str, { plugins: ['preset-default', 'convertStyleToAttrs'] });
// SVG 转 JSON 对象
const ele = xml.xml2js(temp.data) as Element;
// 自动生成可配置函数
const { colors, funcStr, primaryColor, backgroundColor } = generateSvg(ele, type);
});
// 写入最终的 TS 文件
fs.writeFileSync(path.resolve(basePath, `${type}-icons.ts`), prettier.format(funcResult,
{ parser: 'typescript' }));
- readdirSync:同步读取目录内容,一次性读取指定目录下的所有文件 / 子目录名称,并以数组形式返回,执行时会阻塞后续代码,直到目录读取完成
- readFileSync:同步读取文件内容,一次性读取指定文件的全部内容到内存中,并返回文件内容(字符串 / Buffer),执行时会阻塞后续代码,直到文件读取完成
- optimize :
optimize是svgo库的核心压缩方法,对 SVG 做针对性优化,核心目的是:去除冗余内容,精简 SVG 体积、统一格式、降低后续处理复杂度。preset-default是默认优化插件,convertStyleToAttrs是样式转属性插件 - xml.xml2js :
xml.xml2js是xml-js库(你代码里简写为xml)提供的核心方法,作用是:把 XML 格式的字符串(比如 SVG 文本)转换成 JavaScript 对象。
自动生成可配置函数(generateSvg)
接收一个 SVG 元素和类型标识,分析 SVG 内部的颜色、尺寸信息,然后生成一个可复用的 JavaScript 函数。这个生成的函数能根据传入的配置(如尺寸、主色、背景色)动态生成带 base64 编码的 SVG 数据 URL,让 SVG 图标可以灵活调整样式。
TypeScript
// 遍历svg对象节点
interface Visitor {
(temp: Element): void;
}
const svgVisitor = (svg: Element, visitor: Visitor): Element => {
const clone = lodash.cloneDeep(svg);
const visit = (temp: Element) => {
visitor(temp);
if (temp.elements) {
temp.elements.forEach((item) => {
visit(item);
});
}
};
visit(clone);
return clone;
};
// 生成获取svg的base64函数
const generateSvg = (ele: Element, type: string) => {
// 获取svg里所有的颜色
const colors = getSvgAllColors(ele);
let primaryColor;
let backgroundColor;
const result = svgVisitor(ele, (visitorEle) => {
if (visitorEle.attributes?.fill) {
const hex = color(visitorEle.attributes?.fill).hex();
const index = colors.findIndex((item) => {
return item.hex === hex;
});
// 设置主色
if (index === 0) {
primaryColor = hex;
visitorEle.attributes.fill = '${ primaryColor }';
}
// 设置背景色
if (index === 1 && colors.length === 2) {
backgroundColor = hex;
visitorEle.attributes.fill = '${ backgroundColor }';
}
}
// 设置宽度
if (visitorEle.attributes?.width) {
visitorEle.attributes.width = type === 'pipe' ? '${ sizeNumber*2 }px' : '${ sizeNumber }px';
}
// 设置高度
if (visitorEle.attributes?.height) {
visitorEle.attributes.height = '${ size }';
}
});
const funcStr = `
function svgFunction(options?: EntitySvgIconOptions) {
const size = options?.size ?? '64px';
const sizeNumber = Number(size.slice(0,-2))
const primaryColor = options?.primaryColor ?? ${JSON.stringify(primaryColor)};
${
backgroundColor
? `
const background = options?.background ?? true;
let backgroundColor = options?.backgroundColor ?? ${JSON.stringify(backgroundColor)};
backgroundColor = background ? backgroundColor : 'rgba(255, 255, 255, 0)';
`
: ''
}
return \`data:image/svg+xml,${xml.js2xml(result)}\`.replace(/"/g, "'").replace(/#/g, '%23');
}
`;
return {
colors,
funcStr,
primaryColor,
backgroundColor,
};
};
获取svg里所有的颜色(getSvgAllColors )
获取svg里所有的颜色,这里根据业务判断,深色的为主色、浅色的为背景色,所以根据颜色深浅排了个序,好为后续的颜色设置做准备(这部分代码可以自定义)
TypeScript
const getSvgAllColors = (ele: Element) => {
const colors: Array<{
hex: string;
rgb: number[];
grayLevel: number;
}> = [];
svgVisitor(ele, (visitorEle) => {
if (visitorEle.attributes?.fill) {
const res = color(visitorEle.attributes.fill);
const hex = res.hex();
const rgb = res.rgb().array();
colors.push({
hex,
rgb,
// 颜色深浅判断算法,数值越小颜色越深
grayLevel: rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114,
});
}
});
// 根据颜色的深浅排序
colors.sort((a, b) => {
return a.grayLevel - b.grayLevel;
});
return colors;
};