# 深入 svg
# 理解命名空间
使用 xmlns 来指定命名空间(xml namespace),用于告诉解析器在这个 xml 标签内执行的是什么标准,不同的标准有着其特有的标签集合和属性,如下:
分别指定 html 和 svg 的命名空间。
<html xmlns="http://www.w3.org/1999/xhtml">
<body>
<!-- 在这里放置一些 XHTML 标签 -->
<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="200px">
<!-- 在这里放置一些 SVG 标签 -->
</svg>
<!-- 在这里放置一些 XHTML 标签 -->
</body>
</html>
也相当于
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:svg="http://www.w3.org/2000/svg">
<body>
<h1>SVG embedded inline in XHTML</h1>
<svg:svg width="300px" height="200px">
<svg:circle cx="150" cy="100" r="50" fill="#ff0000"/>
</svg:svg>
</body>
</html>
# 扩展命名空间
扩展命名空间,是命名空间的子集。标签有其执行标准,属性也有其执行标准,扩展命名空间一般就是指定某个属性的执行标准,如下:
- 使用
xmlns指定了命名空间, - 使用
xmlns:xlink指定了 xlink 属性的执行标准, xlink:href使用这个属性,它遵循上述执行标准
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<script xlink:href="cool-script.js" type="text/ecmascript"/># <symble> 和 <use>
symble定义一个图形对象,但它本身是不显示的。use引用并实例化刚定义的图形对象,它显示。
如下:(注意 xlink:href 的值为 symble.id的值)
<svg>
<!-- symbol definition NEVER draw -->
<symbol id="sym01" viewBox="0 0 150 110">
<circle cx="50" cy="50" r="40" stroke-width="8" stroke="red" fill="red"/>
<circle cx="90" cy="60" r="40" stroke-width="8" stroke="green" fill="white"/>
</symbol>
<!-- actual drawing by "use" element -->
<use xlink:href="#sym01"
x="0" y="0" width="100" height="50"/>
<use xlink:href="#sym01"
x="0" y="50" width="75" height="38"/>
<use xlink:href="#sym01"
x="0" y="100" width="50" height="25"/>
</svg>
- 它不仅可以在单个
svg标签中完成定义和引用,还可以引用别的svg所定义的内容,iconfont 正是利用了这点特性。
# iconfont.js 干了什么?
iconfont.js 是引入 svg 文件到网页的方法之一。它的原理就是用 symble将一堆 svg 定义为模板对象,并插入到文档中。当要使用 svg 时,使用 use标签对其引用。
// Iconfont SVG 字符串,这里有两个图标,每个symble对应一个
window.iconfontSvgString =
'<svg><symbol id="icon-SEND" viewBox="0 0 1024 1024"><path d="M633.6 938.666667c-8.533333 0-17.066667-4.266667-19.2-12.8l-157.866667-356.266667-358.4-160c-8.533333-4.266667-12.8-10.666667-12.8-19.2s6.4-17.066667 14.933334-19.2l810.666666-283.733333c8.533333-2.133333 17.066667 0 21.333334 4.266666s8.533333 14.933333 4.266666 21.333334l-283.733333 810.666666c-2.133333 8.533333-8.533333 14.933333-19.2 14.933334z m-469.333333-546.133334L480 533.333333c4.266667 2.133333 8.533333 6.4 10.666667 10.666667l140.8 315.733333L883.2 140.8 164.266667 392.533333z" fill="#333333" ></path><path d="M471.466667 573.866667c-6.4 0-10.666667-2.133333-14.933334-6.4-8.533333-8.533333-8.533333-21.333333 0-29.866667L902.4 91.733333c8.533333-8.533333 21.333333-8.533333 29.866667 0s8.533333 21.333333 0 29.866667L486.4 567.466667c-4.266667 4.266667-8.533333 6.4-14.933333 6.4z" fill="#333333" ></path></symbol><symbol id="icon-messagexinxi" viewBox="0 0 1024 1024"><path d="M303.845193 890.605897c-2.653433 0-4.635577-0.669242-5.957689-1.991354-6.618744-2.644223-9.93221-7.236821-9.93221-13.897521L287.955294 731.715097 129.064494 731.715097c-18.541285 0-33.760918-5.965875-45.684482-17.880229C71.464635 701.919491 65.50797 686.733627 65.50797 668.158572L65.50797 191.484126c0-18.541285 5.957689-33.770128 17.872043-45.684482 11.923564-11.914354 27.143197-17.872043 45.684482-17.872043l762.678296 0c18.533099 0 33.760918 5.957689 45.684482 17.872043 11.914354 11.914354 17.872043 27.143197 17.872043 45.684482l0 476.673423c0 18.575054-5.957689 33.760918-17.872043 45.676295-11.923564 11.914354-27.151383 17.880229-45.684482 17.880229L484.582557 731.714074 313.77638 886.631376C311.12397 889.317555 307.818691 890.605897 303.845193 890.605897zM129.064494 159.706375c-21.185508 0-31.777751 10.627035-31.777751 31.777751l0 476.673423c0 21.185508 10.592242 31.778774 31.777751 31.778774l174.780699 0c10.593266 0 15.888875 5.296633 15.888875 15.888875l0 123.138528 148.959613-135.062092c2.65241-2.644223 5.957689-3.966335 9.93221-3.966335l413.116899 0c21.185508 0 31.778774-10.593266 31.778774-31.778774L923.521564 191.484126c0-21.151739-10.593266-31.777751-31.778774-31.777751L129.064494 159.706375z" fill="#231815" ></path></symbol></svg>';
// 在页面中插入 Iconfont SVG
(function(global) {
var script = (scripts = document.getElementsByTagName("script"))[
scripts.length - 1
],
injectCss = script.getAttribute("data-injectcss"),
disableInjectSvg = script.getAttribute("data-disable-injectsvg");
if (!disableInjectSvg) {
var insertSvg = function(svg, el) {
el.parentNode.insertBefore(svg, el);
};
if (injectCss && !global.__iconfont_svg_css_inject__) {
global.__iconfont_svg_css_inject__ = 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);
}
}
var injectSvg = function() {
var container = document.createElement("div");
container.innerHTML = global.iconfontSvgString;
var svg = container.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";
var el = document.body.firstChild;
el ? insertSvg(svg, el) : document.body.appendChild(svg);
}
};
if (document.addEventListener) {
~["complete", "loaded", "interactive"].indexOf(document.readyState)
? setTimeout(injectSvg, 0)
: document.addEventListener("DOMContentLoaded", injectSvg, false);
} else if (document.attachEvent) {
var onStateChange = function() {
"complete" == document.readyState &&
(document.detachEvent("onreadystatechange", onStateChange),
injectSvg());
};
document.attachEvent("onreadystatechange", onStateChange);
}
}
})(window);
# vite-svg-plugin
在 webpack 时通常是使用 svg-sprite-loader 完成 svg 的导入和 symble 工作,而这个插件在 vite 中好像无法使用。
可以选择使用 vite-svg-plugin插件,虽然用户比较少,但作者热心地放了开发过程,可以放心使用。
npm: vite-svg-plugin (opens new window) github: vite-svg-plugin (opens new window) juejin: vite-svg-plugin (opens new window)
在vite.config.js中配置:
// 插件配置
plugins: [
svgBuilder({
path: resolve(__dirname, "../src/assets/svg") + "/",// svg文件夹路径
prefix: "icon",
}), // use xlink:href = #{prefix}-{文件名}
];
# 配套写一个 Icon 组件
Icon.vue
<template>
<i :class="['svg-icon', `svg-icon-${name}`, className]" :style="svgStyle">
<svg fill="currentColor" stroke-width="2" aria-hidden="true" width="100%" height="100%">
<use :xlink:href="iconName" />
</svg>
</i>
</template>
<script>
import { defineComponent } from "vue";
export default defineComponent({
name: "icon",
props: {
name: {
// 图标名,如 fe-search
type: String,
required: true,
},
className: {
// 自定义类名
type: String,
default: "",
},
color: {
// 图标颜色
type: String,
},
size: {
// 图标大小,宽=高
type: [Number, String],
},
},
computed: {
iconName() {
return `#${this.name}`;
},
svgStyle() {
const { color, size } = this;
const style = {};
if (color) {
style.color = color;
}
if (size) {
style.width = `${size}px`;
style.height = `${size}px`;
}
return style;
},
},
});
</script>
<style lang="scss">
.svg-icon {
overflow: hidden;
display: inline-block;
width: 1em;
height: 1em;
fill: currentColor;
font-size: 16px;
vertical-align: middle;
}
svg:not(:defined) {
fill: currentColor;
}
</style>
# 记录一些细节
- 发现从 iconfont 下载的 svg 有 fill 属性时(非 currentColor),无法在外层 css 通过 color 来修改颜色,需要删除掉 fill 属性。
← 字体