preface
Hi, I’m Jay. Many times during project development, designers will give you an SVG icon to use as a font icon in the project. If you already have this system in your existing projects, you can simply put the file into a folder and run a command to render an icon using something like . What would you do if you had to set up such a system without anything? Let’s read on.
The following icon material is I found on the Internet, here only for learning purposes, delete ~
Practical operation
First use Vite to build a project:
npm init
generatepackage.json
File, as follows:{ "name": "font"."version": "1.0.0"."description": "font project"."main": "index.js"."scripts": { "dev": "vite" }, "author": "jayliang"."license": "ISC",}Copy the code
npm i vite -D
The installationvite
- Create one in the root directory
index.html
andindex.js
And, inindex.html
Is introduced as followsindex.js
:<script type="module" src="./index.js"></script> Copy the code
First we create an Assets folder in the root directory to store the SVG file. Then look at the following code:
//index.js
const app = document.querySelector('#app')
app.innerHTML = render()
function render() {
return `
<div class="container">
<h1>Hello SVG</h1>
<span>${renderIcon('search', { color: 'red', fontSize: 30 })}</span>
</div>
`
}
function renderIcon(name, options = { color: 'black', fontSize: 16 }) {
return ' '
}
Copy the code
In the main render logic, what we actually want to implement is the renderIcon method. RenderIcon appears to return the CORRESPONDING HTML fragment of SVG directly. We only have a bunch of SVG files. How do we get them to return snippets? While it is possible to import files dynamically in a browser environment, reading the text of a file is a bit of a hassle without an API like fs.readfile.
Here we can write a simple script to preprocess, first read the content of the SVG file, and create an ICONS folder from the root directory to store the results of the script processing. The preprocessor script does the following:
- Read all
SVG
File content, transformed into a string export - the
SVG
In the filewidth
,height
,fill
The string is extracted and passed in as a parameter. - Generate an entry file to expose all
SVG
File.
The ICONS folder looks something like this:
Index.js // entry file script.js // Generated file Script home.js //home.svg generated file search.js //search.svg generated fileCopy the code
Script implementation
Let’s take a look at the implementation of script.js
const path = require('path')
const fs = require('fs')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const assetsDirPath = path.resolve(__dirname, '.. /assets') // Directory for storing SVG files
const assets = fs.readdirSync(assetsDirPath)
const currentPath = path.resolve(__dirname, '/') // The current directory, namely the ICONS directory
assets.forEach(asset= > {
const assetPath = `${assetsDirPath}/${asset}`
let res = fs.readFileSync(assetPath, { encoding: 'utf8' })
const reg = /<svg.*>[\s\S]*<\/svg>/ // Filter out the SVG tags
let svg = reg.exec(res)[0]
const dom = new JSDOM(`<div id="container">${svg}</div>`) // To facilitate manipulation of node objects
const document = dom.window.document;
const container = document.querySelector('#container');
const svgDom = container.querySelector('svg')
svgDom.setAttribute('width'.'${fontSize}') // Handle width and height attributes
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 attribute processing
}
svg = container.innerHTML
const fileName = asset.split('. ') [0] + '.js'
// Export function implementation
const string = `
export default function({color,fontSize}){
return \`${svg}` \ `}
fs.writeFileSync(currentPath + '/' + fileName, string)
})
// import file splice
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)
Copy the code
Any SVG file that is processed into a JS file looks like this:
//home.js
export default function({color,fontSize}){
return ` < SVG t = "1648047237899" class = "icon" viewBox = "0 0 1024 1024" version = "1.1" XMLNS = "http://www.w3.org/2000/svg" p-id="1683" xmlns:xlink="http://www.w3.org/1999/xlink" width="${fontSize}" height="${fontSize}">
<path d="....." fill="${color}"></path>
</svg>`
}
Copy the code
The resulting entry file index.js looks like this:
import home from './home';
import search from './search';
import set from './set';
export default {
home,
search,
set,
}
Copy the code
renderIcon
implementation
After preprocessing the icon file and entry script, the render function of icon is very simple and implemented as follows:
import icon from './icons'
function renderIcon(name, options = { color: 'black', fontSize: 16 }) {
const iconRenderFunc = icon[name]
if(! iconRenderFunc ||typeoficonRenderFunc ! = ='function') {
throw new Error(`icon:${name} is not found`)}const res = iconRenderFunc(options)
return res
}
Copy the code
To see if the render is as expected:
`
<span>${renderIcon('search', { color: 'red', fontSize: 30 })}</span>
<span>${renderIcon('home', { color: 'pink', fontSize: 50 })}</span>
<span>${renderIcon('set', { color: 'black', fontSize: 16 })}</span>
`
Copy the code
As you can see from the image above, rendering is basically fine, one more thing we need to do is expose a selector for the rendered SVG tag and mouse over it. RenderIcon can be modified as follows:
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)
})
}
Copy the code
Add a mouseEnterOptions parameter to define the mouseenter and mouseleave parameters, then listen for mouseEnter and mouseleave events.
Options ={color:’black’,fontSize:30}/>
Font icon library
We used node.js preprocessing SVG file + rendering logic above to basically achieve an icon library that can meet most business scenarios. The more common approach in the industry is to use SVG as a font, which is what I started with. Maybe you can render an icon just by . Let’s see how this works.
We will use a very cool font manipulation library ————font-carrier, which is a 1.5K star third-party package on GitHub, to make it very easy to generate fonts using SVG. NPM I font -carrier-d, create a new font directory in the root directory, and create script.js under this directory.
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 = 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'
})
Copy the code
Eot. SVG. TTF. Woff. Woff2. Then we define an iconfonts.css file, mainly for defining fonts, which reads as follows:
@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, iOS4.2+ * /url('iconfonts.svg#uxiconfont') format('svg'); / * iOS 4.1 - * /
}
.iconfont {
font-family: "iconfont";
font-size: 16px;
font-style: normal;
}
Copy the code
Once defined, import the CSS file and you can use it as follows:
<span class="iconfont search"></span>
<span class="iconfont home"></span>
<span class="iconfont setting"></span>
Copy the code
pseudo-classes
We used the Unicode encoding of the font directly above, but we can also use the CSS pseudo-class form, which is the most commonly used form in the industry. On the basis of the above, just generate one more icon. CSS to record these pseudo-class information. The code is as follows:
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 = iconMap[key]
content += `
.icon-${name}::before {
content:'${value}'} `
})
fs.writeFileSync('./icon.css',content)
Copy the code
The generated icon.css looks like this:
.icon-home::before {
content: '\E001'
}
.icon-search::before {
content: '\E002'
}
.icon-set::before {
content: '\E003'
}
Copy the code
We can use the icon by
The last
That’s the end of this article. How do you use icon libraries in your daily projects? Welcome to comment. Leave a like if you think it’s funny or helpful