今天接到一个系统需求,需要将系统内的文字替换为鸿蒙OS的字体(developer.huawei.com/consumer/cn…),但是鸿蒙OS的字体单Regular的文件就有8M+。这个直接放到网页上,会极大影响使用体验。
于是乎,开始考虑怎么开始对字体文件进行压缩。
第一种方案
一般来说,字体格式体积由大到小排列(性能从差到好):
ttf,eot(IE8),woff,woff2
从官网下载的鸿蒙字体是ttf格式的,尝试用了woff2格式的(github.com/IKKI2000/ha…),但是还是有4M多。对于Web端来说,还是太大了。
第二种方案
把系统内未使用过的汉字裁掉,只保留系统内使用的汉字,这样就可以大大减少字体文件体积。但是呢这种方式只适用于固定内容的网页,像一些动态内容(服务端返回的汉字,评论等)就没法适应。
这种方式可以使用 Font-spider-Plus 组件进行压缩。具体方法可以看到组件文档。
第三种方案
上面两种方式,对于我来说都不太理想,于是只能继续寻找方案。很好奇,鸿蒙的官网也是用的鸿蒙的字体,那它是怎么兼顾性能的呢?
通过Devtool能看到,官网加载的字体文件非常多,但是每个只有几十kb。像是被切割成了N份。
于是翻看对应的css文件,发现了这一句注释
/** generated by https://github.com/voderl/font-slice */
进入github仓库一看,发现了今天的主角 font-slice。(组件Demo地址)
效果
以得意黑字体为例为例:
处理前 ttf 大小 2074KB,woff2 大小 928KB.
处理后每个类型的字体生成 95 个文件:
ttf 总大小为 2.3M (最小文件 3.4K,最大文件 55K)
woff2 总大小为 1.3M (最小文件 1.5K,最大文件 33K)
实际加载页面的体积由页面使用的字符决定,以该页面为例,只需要加载 386KB 就能覆盖全部字符。
原理
将中文字体按照 Google Fonts 的切割子集方案,生成多个较小体积的资源包。仅需加载小部分字体资源即可展示完整页面。
即它采用了机器学习等手段,将字体拆分成合适的粒度,比如把一个 4MB 的字体包分成 100 个 40KB 的字体包,这样的话,一般网页中使用到的中文也只是一部分字体,只需要加载多个资源包就能完全覆盖。同时,就算网页中有很多生僻字,需要付出的代价也只是多加载几个资源包。
使用
1. 安装
npm install --save-dev font-slice
2.创建脚本
const path = require("path");
const createFontSlice = require('font-slice');
createFontSlice({
// fontPath
fontPath: path.resolve(__dirname, 'YourPath.ttf'),
// outputDir
outputDir: path.resolve(__dirname, './output'),
fontFamily: 'HarmonyOS_SansSC',
// 是否需要在生成完成后打开预览页面,默认为 true,如果为 false 不会生成 index.html 及启动服务器
preview: false,
})
这一步所有的配置项可以查看:github.com/voderl/font…
3. 配置script
我是把这个脚本配置到了package.json的scripts里面,后续就可以通过npm run来运行了。这一步可自行决定。
4.引入CSS
运行完成后,会输出font.css和切割成多份的字体文件。将css引入页面。页面内的地方就可以正常使用font-family了。
缺点
这种方式有个缺点,就是如果新出现的文字没有加载对应的分片,会有一瞬间的文字闪动。因为需要加载新的文字分片。