想要字体图标设计师却给了SVG?没关系,自己转
zhezhongyun 2025-05-22 14:57 5 浏览
本文为Varlet组件库源码主题阅读系列第三篇,读完本篇,你可以了解到如何将svg图标转换成字体图标文件,以及如何设计一个简洁的Vue图标组件。
Varlet提供了一些常用的图标,图标都来自 Material Design Icon。
转换SVG为字体图标
图标原文件是svg格式的,但最后是以字体图标的方式使用,所以需要进行一个转换操作。
处理图标的是一个单独的包,目录为/packages/varlet-icons/,提供了可执行文件:
打包命令为:
接下来详细看一下lib/index.js文件都做了哪些事情。
// lib/index.js
const commander = require('commander')
commander.command('build').description('Build varlet icons from svg').action(build)
commander.parse()
使用命令行交互工具commander提供了一个build命令,处理函数是build:
// lib/index.js
const webfont = require('webfont').default
const { resolve } = require('path')
const CWD = process.cwd()
const SVG_DIR = resolve(CWD, 'svg')// svg图标目录
const config = require(resolve(CWD, 'varlet-icons.config.js'))// 配置文件
async function build() {
// 从配置文件里取出相关配置
const { base64, publicPath, namespace, fontName, fileName, fontWeight = 'normal', fontStyle = 'normal' } = config
const { ttf, woff, woff2 } = await webfont({
files: `${SVG_DIR}/*.svg`,// 要转换的svg图标
fontName,// 字体名称,也就是css的font-family
formats: ['ttf', 'woff', 'woff2'],// 要生成的字体图标类型
fontHeight: 512,// 输出的字体高度(默认为最高输入图标的高度)
descent: 64,// 修复字体的baseline
})
}
varlet-icons的配置如下:
// varlet-icons.config.js
module.exports = {
namespace: 'var-icon',// css类名的命名空间
fileName: 'varlet-icons',// 生成的文件名
fontName: 'varlet-icons',// 字体名
base64: true,
}
核心就是使用webfont包将多个svg文件转换成字体文件,webfont的工作原理可以通过其文档上的依赖描述大致看出:
使用svgicons2svgfont包将多个svg文件转换成一个svg字体文件,何为svg字体呢,就是类似下面这样的:
每个单独的svg文件都会转换成上面的一个glyph元素,所以上面这段svg定义了一个名为geniconsfont的字体,包含两个字符图形,我们可以通过glyph上定义的Unicode码来使用该字形,详细了解svg字体请阅读SVG_fonts。
同一个Unicode在前端的html、css、js中使用的格式是有所不同的,在html/svg中,格式为dddd;或hhhh;,代表后面是四位10进制数值,代表后面是四位16进制数值;在css中,格式为\hhhh,以反斜杠开头;在js中,格式为\uhhhh,以\u开头。
转换成svg字体后再使用几个字体转换库分别转换成各种类型的字体文件即可。
到这里字体文件就生成好了,不过事情并没有结束。
// lib/index.js
const { writeFile, ensureDir, removeSync, readdirSync } = require('fs-extra')
const DIST_DIR = resolve(CWD, 'dist')// 打包的输出目录
const FONTS_DIR = resolve(DIST_DIR, 'fonts')// 输出的字体文件目录
const CSS_DIR = resolve(DIST_DIR, 'css')// 输出的css文件目录
// 先删除输出目录
removeSync(DIST_DIR)
// 创建输出目录
await Promise.all([ensureDir(FONTS_DIR), ensureDir(CSS_DIR)])
清空上次的成果物,创建指定目录,继续:
// lib/index.js
const icons = readdirSync(SVG_DIR).map((svgName) => {
const i = svgName.indexOf('-')
const extIndex = svgName.lastIndexOf('.')
return {
name: svgName.slice(i + 1, extIndex),// 图标的名称
pointCode: svgName.slice(1, i),// 图标的代码
}
})
const iconNames = icons.map((iconName) => ` "${iconName.name}"`)
读取svg文件目录,遍历所有svg文件,从文件名中取出图标名称和图标代码。svg文件的名称是有固定格式的:
uFxxx是图标的Unicode代码,后面的是图标名称,名称也就是我们最终使用时候的css类名,而这个Unicode实际上映射的就是字体中的某个图形,字体其实就是一个“编码-字形(glyph)”映射表,比如最终生成的css里的这个css类名:
.var-icon-checkbox-marked-circle::before {
content: "\F000";
}
var-icon是命名空间,防止冲突,通过伪元素显示Unicode为F000的字符。
这个约定是svgicons2svgfont规定的:
如果我们不自定义图标的Unicode,那么会默认从E001开始,在Unicode中,E000-F8FF的区间没有定义字符,用于给我们自行使用private-use-area:
接下来就是生成css文件的内容了:
// lib/index.js
// commonjs格式:导出所有图标的css类名
const indexTemplate = `\
module.exports = [
${iconNames.join(',\n')}
]
`
// esm格式:导出所有图标的css类名
const indexESMTemplate = `\
export default [
${iconNames.join(',\n')}
]
`
// css文件的内容
const cssTemplate = `\
@font-face {
font-family: "${fontName}";
src: url("${
base64
? `data:application/font-woff2;charset=utf-8;base64,${Buffer.from(woff2).toString('base64')}`
: `${publicPath}${fileName}-webfont.woff2`
}") format("woff2"),
url("${
base64
? `data:application/font-woff;charset=utf-8;base64,${woff.toString('base64')}`
: `${publicPath}${fileName}-webfont.woff`
}") format("woff"),
url("${
base64
? `data:font/truetype;charset=utf-8;base64,${ttf.toString('base64')}`
: `${publicPath}${fileName}-webfont.ttf`
}") format("truetype");
font-weight: ${fontWeight};
font-style: ${fontStyle};
}
.${namespace}--set,
.${namespace}--set::before {
position: relative;
display: inline-block;
font: normal normal normal 14px/1 "${fontName}";
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
}
${icons
.map((icon) => {
return `.${namespace}-${icon.name}::before {
content: "\\${icon.pointCode}";
}`
})
.join('\n\n')}
`
很简单,拼接生成导出js文件及css文件的内容,最后写入文件即可:
// lib/index.js
await Promise.all([
writeFile(resolve(FONTS_DIR, `${fileName}-webfont.ttf`), ttf),
writeFile(resolve(FONTS_DIR, `${fileName}-webfont.woff`), woff),
writeFile(resolve(FONTS_DIR, `${fileName}-webfont.woff2`), woff2),
writeFile(resolve(CSS_DIR, `${fileName}.css`), cssTemplate),
writeFile(resolve(CSS_DIR, `${fileName}.less`), cssTemplate),
writeFile(resolve(DIST_DIR, 'index.js'), indexTemplate),
writeFile(resolve(DIST_DIR, 'index.esm.js'), indexESMTemplate),
])
我们只要引入varlet-icons.css或less文件即可使用图标。
图标组件
字体图标可以在任何元素上面直接通过对应的类名使用,不过Varlet也提供了一个图标组件Icon,支持字体图标也支持传入图片:
实现也很简单:
通过component动态组件,根据传入的name属性判断是渲染img标签还是i标签,图片的话nextName就是图片url,否则nextName就是图标类名。
n方法用来拼接BEM风格的css类名,classes方法主要是用来支持三元表达式,所以上面的:
[isURL(name), n('image'), `${namespace}-${nextName}`]
其实是个三元表达式,为什么不直接使用三元表达式呢,我也不知道,可能是更方便一点吧。
const { n, classes } = createNamespace('icon')
export function createNamespace(name: string) {
const namespace = `var-${name}`
// 返回BEM风格的类名
const createBEM = (suffix?: string): string => {
if (!suffix) return namespace
return suffix.startsWith('--') ? `${namespace}${suffix}` : `${namespace}__${suffix}`
}
// 处理css类数组
const classes = (...classes: Classes): any[] => {
return classes.map((className) => {
if (isArray(className)) {
const [condition, truthy, falsy = null] = className
return condition ? truthy : falsy
}
return className
})
}
return {
n: createBEM,
classes,
}
}
支持设置图标大小:
如果是图片则设置宽高,否则设置字号:
支持设置颜色,当然只支持字体图标:
支持图标切换动画,当设置了 transition(ms) 并通过图标的 name 切换图标时,可以触发切换动画:
具体的实现是监听name属性变化,然后添加一个改变元素属性的css类名,具体到这里是添加了一个设置缩小为0的类名--shrinking:
.var-icon {
&--shrinking {
transform: scale(0);
}
}
然后通过css的transition设置过渡属性,这样就会以动画的方式缩小为0,动画结束后再更新nextName为name属性的值,也就是变成新的图标,再把这个css类名去掉,则又会以动画的方式恢复为原来大小。
const nextName: Ref = ref('')
const shrinking: Ref = ref(false)
const handleNameChange = async (newName: string | undefined, oldName: string | undefined) => {
const { transition } = props
// 初始情况或没有传过渡时间则不没有动画
if (oldName == null || toNumber(transition) === 0) {
nextName.value = newName
return
}
// 添加缩小为0的css类名
shrinking.value = true
await nextTick()
// 缩小动画结束后去掉类名及更新icon
setTimeout(() => {
oldName != null && (nextName.value = newName)
// 恢复为原本大小
shrinking.value = false
}, toNumber(transition))
}
watch(() => props.name, handleNameChange, { immediate: true })
图标组件的实现部分还是比较简单的,到这里图标部分的详解就结束了,我们下一篇再见~
相关推荐
- 办公小技巧:杜绝意外 让字体永不丢失
-
当精心编辑的文档,在其它电脑上打开时,最常见的问题就是页数增加了,表格被撑破了,有些严重的甚至会出现乱码……为什么会这样呢?究其根源都是因为缺少相应的字体。本文介绍各类文档中(包括Word文档、PPT...
- 网站速度优化:从10秒到2秒的极致性能提升方案
-
核心内容:性能诊断工具:PageSpeedInsights与Lighthouse的评分差异解读。WebPageTest多地区、多设备性能对比。前端优化:CSS/JS压缩与合并的自动化方案(如Webp...
- 如何在WordPress中删除谷歌字体(2种简单方法)
-
许多WordPress主题都会用Google字体,然而,Google字体并不存储在网站本地,它是一个第三方资源,这会影响网站的加载速度,让网站变慢,尤其当你的网站面向的是国内用户时,更需要禁用Goog...
- 2019年免费可商用字体大全(附打包下载)
-
私信“字体”可免费获得。附送一个几十G的,有商业授权要求的字体包!不说那么复杂了,简单讲,可能你下载的大多数字体都是收费的。比如方正、文鼎、汉仪等的90%以上需要商业授权,甚至个人授权。这儿的授权,指...
- 开源免费、美观实用的后台管理系统模版,简单轻量、开箱即用!
-
项目介绍ArtDesignPro是一款开源免费(基于MITlicense开源协议)、美观实用的后台管理系统模版,专注于用户体验和快速开发的开源后台管理解决方案。基于ElementPlus设计...
- 【推荐】一款开源免费、美观实用的后台管理系统模版
-
如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!项目介绍ArtDesignPro是一款开源免费(基于MITlicense开源协议)、美观实用的后台管理系统模...
- 想要字体图标设计师却给了SVG?没关系,自己转
-
本文为Varlet组件库源码主题阅读系列第三篇,读完本篇,你可以了解到如何将svg图标转换成字体图标文件,以及如何设计一个简洁的Vue图标组件。Varlet提供了一些常用的图标,图标都来自Mater...
- 零基础教你学前端——66、CSS谷歌字体和Icon图标
-
我们学习CSS谷歌字体和CSSIcon图标。我们在应用font-family属性声明样式字体时,除了使用HTML中的标准字体外,还可以使用谷歌字体。谷歌字体是免费使用的,并且有1000多种字体...
- 必须掌握的前端开发基础知识,什么是字体图标?
-
必须掌握的前端开发基础知识,什么是字体图标?显示隐藏元素display显示隐藏none隐藏block显示隐藏后不再占有原来的位置visibility隐藏元素后,继续占有原来的位置visible元...
- 让你的网站速度提升10倍!(四):压缩~~~
-
在前面的内容中讲了如何降低页面大小(给页面瘦身),重点讲的是如何优化图片,而没有讲文本内容如何优化,这是因为与其辛苦的优化文本还不如使用我们的秘密武器:Gzip压缩!如果你还不清楚Gzip是什么,看这...
- MFC转QT - Qt界面开发 - 常用控件
-
基础控件(QPushButton,QLabel,QLineEdit等)按钮控件族Qt提供了丰富的按钮控件,比MFC的按钮控件更加细分和功能丰富。QPushButton(标准按钮)//创建按钮...
- 使用CSS实现苹果官网文字渐入效果
-
效果分析文字是从左到有慢慢呈现出来,不是整体消失和出现,那么肯定不能使用透明度。我们可以想到渐变文字,然后通过改变背景的位置来控制文字的显示与隐藏。渐变文字渐变文字该如何实现呢?这是实现这个效果的关键...
- HarmonyOS NEXT - ArkUI: Text组件
-
Text组件用于在界面上展示一段文本信息,可以包含子组件Span。文本样式包含文本元素的组件,例如Text、Span、Button、TextInput等,都可以使用文本样式。文本样式的属性如下表:.f...
- 计算机网络的 166 个核心概念,你知道吗?
-
上回我整理了一下计算机网络中所有的关键概念,很多小伙伴觉得很有帮助,但是有一个需要优化的点就是这些概念不知道出自哪里,所以理解起来像是在云里穿梭,一会儿在聊应用层的概念,一会儿又跑到网络层协议了。针对...
- 软网推荐:请个多功能纯文本管理助手
-
纯文本不单是指常用的语言文字,还包括各种类型的程序代码、编码等。作为常用的纯文本处理工具,“记事本”显得力不从心。我们可用一款免费软件AlternateTextbrowser,随心所欲地管理各种类型...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- HTML 参考手册 (28)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML常用标签 (29)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- HTML button formtarget 属性 (30)