开发日记(04) - iconfont 挂了,项目内的图标怎么办?
iconfont 应该是挂了有 1-2 两个月了,不管是因为什么原因,肯定会有一大堆的项目有一阵子图标出不来。
📌 0x1 问题
iconfont 应该是挂了有 1-2 两个月了,不管是因为什么原因,肯定会有一大堆的项目有一阵子图标出不来。
作为开发者以来就一直有个感觉,那就是在线服务都不可靠。项目中第三方服务越多,更多不可控的因素就越多。如果都是在自己的服务器上,至少我可以控制服务启动,关闭。都是在同一时间,而不会出现这种能用,但是又没有完全能用的状态,这比服务挂了还恶心。
Node 创始人新搞的 deno 也支持引入在线的脚本,我看到这段代码第一时间不是竟然还能这样写,给我的第一感觉就是这代码某天可能就挂了,导致整个项目起不来。
import { VERSION } from 'https://deno.land/std/version.ts'
console.log(VERSION)
📌 0x2 需要达到的效果
因为是 uni-app 开发的项目,且需要兼容 app 和 nvue。因此图标只能使用单色字体图标的形式,无法直接使用 svg 的形式。
- 本地导入,不依赖任何的第三方
- 支持 weex 的 nvue 界面
- 兼容 h5 和 app 的显示
- 组件形式使用
- (web 也是差不多的思路,这里以 uni-app 为例)
📌 0x3 解决思路
既然在线服务不可靠,那么肯定是使用本地导入方式了。现在的 iconfont 是无法使用,无法上传和下载。看看还有什么办法将 svg 转换到字体图标了。github 上有不少将 svg 转换到 webfont 的库。
我尝试下载了几个测试,发现都存在一个问题,就是我从 MasterGo 导出的 svg 图标使用这些工具进行转换的话,在项目内使用始终是一坨黑色的。猜测是 svg 需要进行一些处理。因为我从 iconfont 下载一个 svg 下来进行转换是 ok。这里不展开说,也不是自己擅长的方向。如果你想了解 iconfont 做了什么可以看看这个
里面讲的比较详细,字体图标处理其实有很大的学问,图标很小,做的事情可不少。
📌 0x4 icomoon svg 处理
这是一个类似于 iconfont 的平台,缺点就是只能本地维护,所有的数据都存在缓存,如果清理了浏览器缓存数据就被清除了。不过这个支持我从设计图直接导出的 svg 进行处理。这里以 MasterGo 进行演示实际上就是获取 svg 图标的过程。
4x1 文件改名
首先让 ui 整理好项目内用到的图标,命名好。可以让我直接导出成为 svg。
4x2 上传到 icomoon
拿到导出的 svg 图标,上传到 icomoon。点击 icomoon app 进入 icomoon
![](https://static.yoouu.cn/imgs/2022/blog/07/202207241740032.webp)
导入 svg 图标
![](https://static.yoouu.cn/imgs/2022/blog/07/202207241740750.webp)
选择从 MasterGo 导出的图标,上传
![](https://static.yoouu.cn/imgs/2022/blog/07/202207241740215.webp)
选中所有的图标点击生成字体
![](https://static.yoouu.cn/imgs/2022/blog/07/202207241741880.webp)
点击预设设置为 class 选择器,取消支持 ie 8,点击 download 下载字体包
![](https://static.yoouu.cn/imgs/2022/blog/07/202207241830974.webp)
4x3 拿到导出的文件
看起来和 iconfont 导出的没啥区别。
📌 0x5 图标组件
c-icon-moon.vue
普通的 vue 组件,里面的 #ifdef
写法是 uni-app 的条件编译。为了兼容多个平台需要使用到条件编译。
<template>
<!-- #ifdef APP-NVUE -->
<text @click="onClick" class="icomoon" :style="iconStyle">{{ icons[name] }}</text>
<!-- #endif -->
<!-- #ifndef APP-NVUE -->
<text @click="onClick" class="icomoon" :class="name" :style="iconStyle" />
<!-- #endif -->
</template>
<script>
// #ifdef APP-NVUE
import icons from './icons'
// #endif
import { addUnit } from '@/utils'
export default {
name: 'CustomIconMoon',
props: {
size: {
type: [Number, String],
default: 'inherit',
},
width: {
type: [Number, String],
default: 'auto',
},
weight: {
type: String,
default: 'normal',
},
height: {
type: [Number, String],
default: 'auto',
},
color: {
type: String,
default: '#909399',
},
name: {
type: String,
required: true,
},
rpx: {
type: Boolean,
required: false,
default: false,
},
bubble: {
type: Boolean,
required: false,
default: true,
},
},
computed: {
iconStyle() {
return {
fontSize: this.size === 'inherit' ? 'inherit' : addUnit(this.size, this.rpx),
width: addUnit(this.width, this.rpx),
height: addUnit(this.height, this.rpx),
color: this.color,
'line-height': addUnit(this.height, this.rpx),
'font-weight': this.weight,
}
},
},
methods: {
onClick(e) {
this.$emit('click')
if (!this.bubble && e) {
e.stopPropagation()
}
},
},
// #ifdef APP-NVUE
data() {
return {
icons: icons,
}
},
// #endif
}
</script>
<style scoped>
/* #ifndef APP-NVUE */
@import './style.css';
/* #endif */
.icomoon {
font-family: icomoon;
}
</style>
addUnit 方法
// 添加单位,如果有rpx,%,px等单位结尾或者值为auto,直接返回,否则加上rpx单位结尾
export function addUnit(value = 'auto', rpx = false) {
value = String(value)
return isNumber(value) ? `${value}${rpx ? 'rpx' : 'px'}` : value
}
/**
* 验证十进制数字
*/
export function isNumber(value) {
return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
}
icomoon-ttf-base64
为了兼容 nvue 需要将 fonts/icomoon.ttf
文件转换为 base64 写入到该文件
转换网址:https://www.giftofspeed.com/base64-encoder/
export default 'AAEAAAALAIAAAwAw...`
icomoon.woff
icons.js
这里配置 nvue 需要显示的图标名字。
\ue912
类似的字符可以打开 style.css
找到你需要在 nvue 显示的图标名字复制。
/**
* 这里配置需要在 nvue 显示的图标
*/
export default {
// 指纹
'icomoon-fingerprint': '\ue912',
}
nvue-helper
nvue 需要的初始化配置。
import icomoonTtfBase64 from './icomoon-ttf-base64'
/**
* iconfont 需要用ttf转base64
* 转换网址:https://www.giftofspeed.com/base64-encoder/
*/
export function onInitNvueFontIcon() {
const dom = uni.requireNativePlugin('dom')
dom.addRule('fontFace', {
fontFamily: 'icomoon',
src: `url('data:font/truetype;charset=utf-8;base64,${icomoonTtfBase64}')`,
})
}
style.css
删除不需要的格式化,只留下 src: url('icomoon.woff') format('woff');
这一段就行。
@font-face {
font-family: icomoon;
font-style: normal;
font-weight: normal;
font-display: block;
src: url('icomoon.woff') format('woff');
}
.icomoon {
font-family: icomoon !important;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-style: normal;
font-variant: normal;
font-weight: normal;
line-height: 1;
text-transform: none;
}
.icomoon-add::before {
content: '\e900';
}
/* ... */
📌 0x6 使用
vue 界面
直接参考 组件使用示例 就可以。
Nvue 界面
nvue 界面使用需要导入字体文件,全局只需要导入一次就行了
App.vue
<script>
// #ifdef APP-NVUE
import { onInitNvueFontIcon } from '@/components/c-icon-moon/nvue-helper'
// #endif
export default {
onLaunch() {
// eslint-disable-next-line no-console
console.log('App Launch')
/**
* Nvue 启动处理
*/
// #ifdef APP-NVUE
onInitNvueFontIcon()
// #endif
},
onShow() {
// eslint-disable-next-line no-console
console.log('App Show')
},
onHide() {
// eslint-disable-next-line no-console
console.log('App Hide')
},
}
</script>
组件使用示例
其实就是 vue 组件的使用了。这里贴一个示例。
<c-icon-moon size="60" name="icomoon-fingerprint" color="red" />
显示效果
📌 iconfont 正名
这里为 iconfont 正名一下,先看下网友的情绪 https://github.com/thx/iconfont-plus/issues
阿里是大,但不是什么部门都大,内部是有预算分配的,人家是企业,不是慈善机构。从使用 iconfont 这个平台以来一直都是白嫖,准确来说好像没有付费项目。
再来看看今天用到的 icomoon 平台订阅费用,当然是举个例子,有多少公司愿意在 icon 上有这个预算的呢?
评论区留给大家。
📔 开发日记系列
只记录些平时开发觉得有用的东西,有问题请务必斧正,拜托了 🙏🙏🙏
- 开发日记(01) - uni-app 使用等宽字体对其数字显示
- 开发日记(02) - js 异步任务队列
- 开发日记(03) - uni-app 打包为 app
- 开发日记(04) - iconfont 挂了,项目内的图标怎么办?
关于我
SunSeekerX,
全栈开发、区块链开发、移动端开发、前后端开发、NodeJS 开发、小程序、uni-app
开发、等
喜欢探讨技术实现方案和细节,完美主义者,见不得 bug
。
Github:https://github.com/SunSeekerX
个人博客:https://yoouu.cn/
个人在线笔记:https://doc.yoouu.cn/