百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

想要字体图标设计师却给了SVG?没关系,自己转

suiw9 2025-03-23 23:38 6 浏览 0 评论

本文为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;或&#xhhhh;,&#代表后面是四位10进制数值,&#x代表后面是四位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 })

图标组件的实现部分还是比较简单的,到这里图标部分的详解就结束了,我们下一篇再见~

相关推荐

nginx的反向代理(Nginx的反向代理和负载均衡)

nginxProxy代理1、代理原理反向代理服务的实现:需要有一个负载均衡设备(即反向代理服务器)来分发用户请求,将用户请求分发到后端正真提供服务的服务器上。服务器返回自己的服务到负载均衡设备。负...

Nginx UI: 更好用更现代化的Nginx 管理面板

各位铲屎官大家好,我是喵~...

性能测试之tomcat+nginx负载均衡(nginxtcp负载均衡)

nginxtomcat配置准备工作:两个tomcat执行命令cp-rapache-tomcat-8.5.56apache-tomcat-8.5.56_2修改被复制的tomcat2下con...

nginx upstream节点健康检查(nginx tcp 健康检查)

1、前提条件编译nginx时增加nginx_upstream_check_module模板git地址:https://github.com/yaoweibin/nginx_upstream_check...

Nginx 的高并发处理能力(nginx支持高并发原理)

为了实现Nginx的高并发处理能力,需要从**硬件资源**、**操作系统**、**Nginx配置**等多个方面进行优化。以下是详细的配置和示例:---...

Nginx最全详解(万字图文总结)(nginxs)

大家好,我是mikechen。Nginx是非常重要的负载均衡中间件,被广泛应用于大型网站架构,下面我就全面来详解Nginx@mikechen本篇已收于mikechen原创超30万字《阿里架构师进阶专题...

如何用 Nginx 实现前端灰度发布(nginx 灰度测试规则)

前言在前端开发中,灰度发布是一种重要的策略,它允许我们在不影响所有用户的情况下,逐步推出新功能或更新。通过灰度发布,我们可以测试新版本的稳定性和性能,同时收集用户反馈。今天,我们将探讨如何使用Ngi...

nginx配置优化场景-直接套用so happy!

前言(叠甲在先)Nginx是一款高性能的Web服务器,广泛应用于互联网领域。...

Nginx配置前后端服务(nginx前后端分离部署)

nginx安装完成后,可以通过命令查看配置文件nginx-t配置文件nginx.conf,是总的配置,有的人会把配置全部配置到这个文件中,如果服务很多,这个文件变得非常庞大,我见过一个配置很大的,在...

使用Nginx配置TCP负载均衡(nginx如何配置负载均衡)

假设Kubernetes集群已经配置好,我们将基于CentOS为Nginx创建一个虚拟机。...

Nginx服务器深度指南:安装、配置、优化指令超详解

在当今数字化时代,Web服务器是支撑互联网应用的关键基础设施。Nginx作为一款高性能的开源Web服务器,凭借卓越的性能、丰富的功能和出色的稳定性,在Web服务器领域占据了重要地位。无论是大型互联网公...

Nginx的配置详解(附代码)(nginx基本配置)

本篇文章给大家带来的内容是关于Nginx的配置详解(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。常用配置项在工作中,我们与Nginx打交道更多的是通过其配置文件来进行。...

Nginx配置文件详解(nginx配置文件详解带实例)

Nginx配置文件详解Nginx是一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。...

从 0 到 1:构建高可用 Linux 负载均衡集群(基于 Nginx + Keepalived)

在高并发业务场景下,单台服务器往往无法支撑大量请求,因此需要使用**负载均衡(LoadBalancing)**技术来提升系统的稳定性和可用性。Nginx+Keepalived是常见的开源负载均...

配置Nginx TCP转发(nginx 接口转发)

Nginx一般用在HTTP的转发,TCP的转发大都会使用HAProxy。工作中遇到一个需求,用到了Nginx服务作为TCP转发。场景是这样,数据采集设备通过公网将数据推送到后端应用服务,服务部署在业主...

取消回复欢迎 发表评论: