使用SVG构建你自己的图标库
前言
大家好,我是jay。在项目开发的过程中,很多时候设计师会给你一个SVG
图标,让你用到项目里当作字体icon
。可能你现有的项目中已经有前辈搭建好这套体系,你直接把这个文件放入某个文件夹中,然后run
一条命令,就可以十分方便的用例如之类的写法把一个
icon
渲染出来。那假如现在什么都没有,让你自己去搭建这么一套体系,你会怎么做呢?我们一起往下看吧。
以下图标素材都是我在网上找的,在这里仅当作学习用途,侵删~实战操作 首先使用
vite
搭建一个工程:npm init
生成package.json
文件,内容如下:
{ "name": "font", "version": "1.0.0", "description": "font project", "main": "index.js", "scripts": { "dev": "vite" }, "author": "jayliang", "license": "ISC", }
npm i vite -D
安装vite
- 根目录下新建
index.html
和index.js
,并在index.html
中如下引入index.js
:
assets
文件夹,用来存放SVG
文件。然后来看如下代码://index.js
const app = document.querySelector('#app')app.innerHTML = render()function render() {
return `Hello SVG
${renderIcon('search', { color: 'red', fontSize: 30 })}`
}function renderIcon(name, options = { color: 'black', fontSize: 16 }) {
return ''
}
在主渲染逻辑中,我们实际上要实现的是
renderIcon
方法。看上面这架势,renderIcon
看来是要直接返回SVG
对应的HTML
片段了。我们现在手里只有一批SVG
文件,怎么让他返回代码片段呢?在浏览器环境开发的时候虽说可以动态引入文件,但是读取文件的原文还是比较难搞的事情毕竟没有fs.readFile
之类的API
。这里我们可以写一个简单的脚本预处理一下,先把
SVG
文件的内容读出来,根目录新建一个icons
文件夹来存放脚本处理的结果。这个预处理脚本要处理的事情如下:- 读取所有的
SVG
文件内容,改造成字符串导出 - 把
SVG
文件中的width
、height
、fill
字符串提取出来,后续作为参数传入。 - 生成一个入口文件暴露所有
SVG
文件。
icons
文件夹大概长成这个样子:index.js // 入口文件
script.js //生成文件脚本
home.js //home.svg生成的文件
search.js //search.svg生成的文件
脚本实现 下面一起来看一下
script.js
脚本的实现const path = require('path')
const fs = require('fs')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const assetsDirPath = path.resolve(__dirname, '../assets') //存放SVG文件的目录
const assets = fs.readdirSync(assetsDirPath)
const currentPath = path.resolve(__dirname, './') //当前目录,即icons目录
assets.forEach(asset => {
const assetPath = `${assetsDirPath}/${asset}`
let res = fs.readFileSync(assetPath, { encoding: 'utf8' })
const reg = /[\s\S]*<\/svg>/ //将SVG标签过滤出来
let svg = reg.exec(res)[0]
const dom = new JSDOM(`${svg}`) //方便操作节点对象
const document = dom.window.document;
const container = document.querySelector('#container');
const svgDom = container.querySelector('svg')
svgDom.setAttribute('width', '${fontSize}') // width与height属性处理
svgDom.setAttribute('height', '${fontSize}')
const paths = svgDom.querySelectorAll('path')
for (let i = 0;
i < paths.length;
i++) {
const path = paths[i]
path.setAttribute('fill', '${color}') //path属性处理
}
svg = container.innerHTML
const fileName = asset.split('.')[0] + '.js'
//导出函数实现
const string = `
export default function({color,fontSize}){
return \`${svg}\`
}
`
fs.writeFileSync(currentPath + '/' + fileName, string)
})//入口文件拼接
let importStr = ``
let exportStr = ``assets.forEach(asset => {
const fileName = asset.split('.')[0]
importStr += `import ${fileName} from './${fileName}';
\n`
exportStr += `${fileName},\n`
})
const content = `
${importStr}export default {
${exportStr}
}
`
fs.writeFileSync(currentPath + '/index.js',content)
任意一个
SVG
文件经过处理后转成的JS
文件内容是这样子的://home.jsexport default function({color,fontSize}){
return `
`
}
最后生成的入口文件
index.js
内容是这样子的:
import home from './home';
import search from './search';
import set from './set';
export default {
home,
search,
set,
}
renderIcon
实现
在上述预处理好icon
文件与入口脚本之后,icon
的render
函数就十分简单了,实现如下:import icon from './icons'
function renderIcon(name, options = { color: 'black', fontSize: 16 }) {
const iconRenderFunc = icon[name]
if (!iconRenderFunc || typeof iconRenderFunc !== 'function') {
throw new Error(`icon:${name} is not found`)
}
const res = iconRenderFunc(options)
return res
}
来试一下渲染效果是否符合预期:
`
${renderIcon('search', { color: 'red', fontSize: 30 })}
${renderIcon('home', { color: 'pink', fontSize: 50 })}
${renderIcon('set', { color: 'black', fontSize: 16 })}
`
文章图片
由上图可以看出基本上渲染是没问题的,我们还需要做的一个事情是给渲染出来的
SVG
标签暴露一个选择器以及对其加上鼠标移上效果处理。改造renderIcon
方法如下:function renderIcon(name, options = { color: 'black', fontSize: 16 }, mouseEnterOptions = {}) {
// ......
const id = genId()
svg.id = id
svg.classList += ` icon-${name}`if (Object.keys(mouseEnterOptions).length > 0) {
setTimeout(() => {
const dom = document.querySelector(`#${id}`)
const { color, fontSize } = mouseEnterOptions
const { color: originColor, fontSize: originFontsize } = options
let resetPathColor = null
let resetFontSize = null
dom.addEventListener('mouseenter', () => {
if (color) {
setPathColor(dom, color)
resetPathColor = setPathColor
}
if (fontSize) {
setSvgFontsize(dom, fontSize)
resetFontSize = setSvgFontsize
}
})
dom.addEventListener('mouseleave', () => {
resetPathColor && resetPathColor(dom, originColor)
resetFontSize && resetFontSize(dom, originFontsize)
})
}, 0)
}
}function setSvgFontsize(svg, fontSize) {
svg.setAttribute('width', fontSize)
svg.setAttribute('height', fontSize)
}function setPathColor(svg, color) {
const paths = svg.querySelectorAll('path');
[...paths].forEach(path => {
path.setAttribute('fill', color)
})
}
加多一个
mouseEnterOptions
参数定义鼠标移入的参数,然后监听mouseenter
和mouseleave
事件即可。文章图片
当然你用框架可以封装成
这样的使用形式,会更加的优雅。字体图标库 我们上面利用了
node.js
预处理SVG
文件+渲染逻辑基本实现了一个能满足大多数业务场景的图标库。那么业界更普遍的做法其实是把SVG
当成字体来用,也就是我最一开始说的也许你只要
就能渲染一个图标,下面我们一起来看一下是如何实现的。我们会用到一个十分牛逼的字体操作库————font-carrier,是在
GitHub
上star
有1.5k
的明星第三方包,可以利用它很方便地使用SVG
生成字体。先来安装一下npm i font-carrier -D
,然后在根目录新建一个fonts
目录,在这个目录下新建一个script.js
,内容编写如下:const fontCarrier = require('font-carrier')
const path = require('path')
const fs = require('fs')
const assetsDirPath = path.resolve(__dirname, '../assets')
const assets = fs.readdirSync(assetsDirPath)
const font = fontCarrier.create()
let initValue = https://www.it610.com/article/0xe000
for (let i = 0;
i < assets.length;
i++) {
const assetPath = `${assetsDirPath}/${assets[i]}`
const res = fs.readFileSync(assetPath).toString()
initValue += 1
const char = String.fromCharCode(initValue)
font.setSvg(char, res)
}font.output({
path:'./iconfonts'
})
默认会输出
.eot
、.svg
、.ttf
、.woff
、.woff2
,默认会输出这几个字体文件,究其原因是各个浏览器对字体的实现不一样,所以这是为了兼容大多数的浏览器。然后我们再定义一个iconfonts.css
文件,主要为了定义字体,内容如下:@font-face {
font-family: 'iconfont';
src: url('iconfonts.eot');
/* IE9*/
src: url('iconfonts.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('iconfonts.woff') format('woff'), /* chrome、firefox */
url('iconfonts.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
url('iconfonts.svg#uxiconfont') format('svg');
/* iOS 4.1- */
}.iconfont {
font-family: "iconfont";
font-size: 16px;
font-style: normal;
}
定义完之后,引入这个
CSS
文件,然后就可以如下使用了:
文章图片
伪类 上面我们是直接使用了字体所对应的
unicode
编码,其实也可以使用CSS
伪类的形式,这也是业界用的最多的形式。在上面的基础上,只要生成多一个icon.css
记录这些伪类信息就行。代码如下:const iconMap = {}
for (let i = 0;
i < assets.length;
i++) {
//......
iconMap[assets[i]] = '\\' + initValue.toString(16).toUpperCase()
}
let content = ``Object.keys(iconMap).forEach(key => {
const name = key.replace('.svg','')
const value = https://www.it610.com/article/iconMap[key]
content += `
.icon-${name}::before {
content:'${value}'
}`
})
fs.writeFileSync('./icon.css',content)
生成的
icon.css
内容如下:.icon-home::before {
content: '\E001'
}.icon-search::before {
content: '\E002'
}.icon-set::before {
content: '\E003'
}
我们就能通过
这样的方式来使用图标了。最后 【使用SVG构建你自己的图标库】以上就是本篇文章的全部内容,你平时项目开发过程中是如何使用这样的图标库的呢?欢迎留言讨论。如果觉得有趣或者对你有帮助的话,留下一个赞吧~
推荐阅读
- 华为云GaussDB亮相DAMS峰会,分享构建开放生态与数据库国产化经验
- python|正则表达式在python中的简单使用(附带例子(正则表达式匹配+数据清洗))
- 使用Go语言web框架wego实现用户登录功能
- 教你如何使用|教你如何使用 Python 向手机发送通知!
- coding4fun|使用Python制作中文词云
- python开发企业微信应用,使用Python发送企业微信消息
- 笔记|查看电脑使用日志------电脑干了些什么,别人是否动了你的电脑
- python|python数据分析基础003 -numpy的使用(详解)
- Web3|web3js使用metamask调用合约查询和发起交易
- 笔记|使用Vue时候报错,怎么查看错误位置,如何解决,方法,有哪些