github地址:github.com/visualfanat…
在webpack、Vue CLI、Nuxt中使用都有介绍,可以去看文档,在这里就不做展开了。
Vue CLI 中使用去掉 svg 颜色的自定义loader svgo-remove-single-fill.js
// 使用vue-svg-loader加载图标,用于<x-icon>扩展图标
const svgPath = resolve('src/assets/svg');
config.module
.rule('svg')
.exclude.add(svgPath)
.end();
config.module
.rule('icons')
.test(/\.svg$/)
.include.add(svgPath)
.end()
.use('babel-loader')
.loader('babel-loader')
.end()
.use('vue-svg-loader')
.loader('vue-svg-loader')
.options({
svgo: {
plugins: [{
// 去除单色图标的fill,使svg可通过css修改颜色
svgoRemoveSingleFill: require('./loaders/svgo-remove-single-fill'),
}],
},
})
.end();
svgo-remove-single-fill.js 的实现如下:
/**
* svgo插件,去掉单色svg的fill,使svg支持用css修改颜色
*/
const ignoreEl = ['defs']; // 需要忽略的节点
function fn(data) {
// 1、获取所有节点最终解析的fill和stroke值
const finals = getFinalFillStroke(data);
const allStroke = new Set(// 所有有效stroke值
finals.filter(item => item.stroke && item.stroke !== 'none' && parseInt(item.strokeWidth) > 0).map(item => item.stroke),
);
const allFill = new Set( // 所有有效fill值
finals.filter(item => item.fill && item.fill !== 'none').map(item => item.fill),
);
// 2、判断有效的stroke与fill是否唯一且相等
if (allStroke.size <= 1 && allFill.size <= 1) {
const stroke = allStroke.values().next().value,
fill = allFill.values().next().value;
if ((!stroke || isSimpleColor(stroke)) &&
(!fill || isSimpleColor(fill)) &&
(!stroke || !fill || stroke === fill)) {
// 3、去掉非none的fill
setFinalFill(data);
}
}
return data;
};
// 获取节点(及后代节点)最终解析的fill和stroke值
function getFinalFillStroke(el, parentFill, parentStroke, parentStrokeWidth = 1) {
const fill = getFill(el, parentFill);
const { stroke, strokeWidth } = getStroke(el, parentStroke, parentStrokeWidth);
if (el.content) { // 存在子节点
return el.content.reduce((sum, child) => {
if (ignoreEl.includes(child.elem)) {
return sum;
} else {
return sum.concat(getFinalFillStroke(child, fill, stroke, strokeWidth));
}
}, []);
} else {
return {
fill,
stroke,
strokeWidth,
};
}
}
// 在判断为单色图标后,设置节点(及后代节点)的fill
function setFinalFill(el, parentFill) {
if (ignoreEl.includes(el.elem)) return;
const fill = getFill(el);
if (el.content) { // 存在子节点
el.content.forEach(child => {
setFinalFill(child, fill || parentFill);
});
// 存在子节点,则去掉自身的fill
if (el.attrs) {
delete el.attrs.fill;
}
} else {
if (fill && fill !== 'none') {
if (el.attrs) delete el.attrs.fill; // 去掉有效的fill
} else if (!fill && parentFill === 'none') {
setDeep(el.attrs, 'fill', { // 最终解析fill为none
local: 'fill',
name: 'fill',
prefix: '',
value: 'none',
});
}
}
}
// 获取当前节点的有效fill值
function getFill(el, parentFill) {
return getDeep(el.attrs, 'fill', 'value') || parentFill;
}
// 获取当前节点的有效stroke和strokeWidth
function getStroke(el, parentStroke, parentStrokeWidth) {
return {
stroke: getDeep(el.attrs, 'stroke', 'value') || parentStroke,
strokeWidth: getDeep(el.attrs, 'stroke-width', 'value') || parentStrokeWidth || 1,
};
}
// 判断颜色是否为简单颜色
function isSimpleColor(c) {
return !c || /^(#[\da-f]+|[a-z]+)$/i.test(c);
}
// 深度获取对象的属性值
function getDeep(obj, ...keys) {
let v = obj;
if (!v) return v;
for (let k of keys) {
v = v[k];
if (v === null || v === undefined) return;
}
return v;
}
// 深度设置对象的属性值
function setDeep(obj, ...keys) {
for (let i = 0, k; i < keys.length - 2; i++) {
k = keys[i];
if (!obj[k]) {
obj[k] = {};
};
obj = obj[k];
}
obj[keys[keys.length - 2]] = keys[keys.length - 1];
}
module.exports = {
name: 'removeSvgSingleFill',
type: 'full',
description: 'remove svg single fill',
fn,
};
目前不支持多颜色复杂svg的颜色处理