-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
47. 可变字体探索与 require 扫盲记 #47
Comments
这是是可变字体的实例化,现在 fonttools 基本都支持了。 对于字体子集化(裁剪)基本上 JS 的库都不够成熟。我选择了另外的路线,可以看看 iconfont 的效果,支持可变字体 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
国庆后一场秋雨一场寒,属于东南季风的台风带来了明显的降温,又到了一个尴尬的温度,长袖短裤都有人穿。这个温度,感觉很舒服,尤其是在海边骑单车的时候,沿着沙河路的时候,城市灯光的点缀,观景台边的海涛声、阵阵袭人的秋意就来了。
本篇是介绍两个琐事,都是工作中遇到的,一个是可变字体探索,一个是
require
扫盲记。Variable Fonts
好像从前几个月开始,就接触了可变字体,以前设计推荐的是使用 5 种字体,你没有看错,在项目里面用到了 5 、种字体,不同的粗细,不同的高瘦,每个字体都基本在 8M 左右,通过不同的字体来展示设计的风格,真是。。。挺好的。今年开始有新的字体,没有以前的 5 种,只用一个可变字体了,通过一个字体来展示之前 5 个的字体,可以说很是优秀,当然对开发而言,统一的字体最是简单,而且一个字体意味着只要加载一种就好了,之前的要加载 5 种字体,虽然一个可变字体的体积是 20 M。
可以通过这个网站 玩一下可变字体。
字体,对于开发者而言,默认基本都是采用系统的字体,比如系统差别、中西文差别,还有最后的衬线字体,比如我们公司就喜欢用
android
的Roboto
默认字体来显示数字。如果采用自己的字体的话,会把其放在最前面,所以最前面是OPPOSANS, Roboto, Noto Sans CJK SC, Source Han Sans CN
后面两个是思源字体,毕竟OPPOSANS
是和思源字体结合的。。。。通过设置
font-variation-settings: "wght" 550
可以调整字体的粗细,比如OPPOSANS
字重可以调整到1000-1000
区间,实现无极调整,不像以前的字体,只有一百倍数的font-weight
,而且要一个字体文件就够了。还有其他的比如wdth
ital
这些都可以设置。比如下图
还能在这个基础上用上
font-weight
,当然这个就不规范了。目前font-variation-settings
的兼容性还是比较好的,除了 ie 和部分比较老的浏览器不支持外,其他都没有问题的。字体的普通处理
如果是采用系统的字体那一切都挺好的,但是作为设计,作为一家最求美感的公司,就是要有自己的字体,于是普通字体的 10M 的体积,加载速度就可以劝退大部分人了。为了平滑顺利过渡字体,一般采用的是如下几个方案:
font-face
定义的时,采用swap
来显示,系统会优先采用已有的字体,避免字体加载导致的阻塞,使得文字无法显示;**当然这种方案会导致字体加载成功时,页面切换会从当前字体切换到自定义字体,导致用户体验稍差。如果自定字体体积小,可以不采用swap
方式。link
标签里面采用preload
的方式,字体资源在浏览器里,属于优先级较低的资源,通过link
的预加载可以显著的提高优先级,避免字体加载时间过长,导致切换时候带来的不好体验。溜
这个字,那就提取字体包里的溜
,这样字体文件就可以压缩的非常小了字体的提取历程
这里要介绍是可变字体的提取问题,先看看普通字体提取,之前用的是
font-spider
,使用下来可以满足字体的压缩,提取需要的子集,用法很方便,如下:再通过指令
font-spider ./test.html
就可以从source.eot
字体包里面压缩出仅仅包含溜
一个字的字体,当然会有一点小问题,比如垂直方向的行间距变小了,但是总体问题不大,10M 的字体包,最后只剩下几 kb。这个时候如果用软件 FontForge 查看的话,可以看到溜
保留下来了,其他被移除了。上面左边是正常的字形,右边则是压缩之后的效果,可以看到压缩后周围的小伙伴都被吓跑了。
只是到了可变字体,压缩就不是这样了,简简单单的
font-spider
打包出来的字体就不能用,会出现字体镂空的情况,而且关键是不能调整可变字体的wght
,设置了也不起作用,简直就是和普通字体差不多,不再是可变字体了。于是翻箱倒柜的,在
font-spider
里面转了一圈,结果发现里面处理字体的内容不多,更多的是对输入文件和样式处理,通过模拟的浏览器环境,自研的browser-x
(大佬自己写的Node.js
实现的虚拟浏览器) 来获取样式,保证不同的的font-family
打包出不同的字体,分析输入的参数,文字最后输出四种格式的字体,woff woff2 svg ttf
这些,当然在WebFont
里面看到了不少冗余的代码,一度让我误解了,比如weight stretch
这些属性,就不能使用。。。。可能也是大佬弃坑了吧,最后落实到压缩的还是fontmin
这个库,也有三方压缩的工具都是基于fontmin
的。fontmin
是一款中间件机制的字体处理工具,比如glyph
可以用来压缩字体,比如ttf2woff
可以转换字体。比如下面的官方例子:在
fontmin
代码里面的glyph
插件代码中可以看到如下形式:敢情
fontmin
也是套娃的。。。最后核心的字体处理还是要跑到fonteditor-core
里面,怎么说呢,fontmin
是一个优秀的集成商,有字体压缩,还有字体格式转换这些功能,虽然大部分是基于第三方的。而且不管是fontmin
还是font-spider
也有四五年没有更新主要内容了,作者也都弃坑了。那对于 16 年底才发布的可变字体,好像不支持也是可以理解的。table
介绍到
fonteditor-core
就要提一下table
的概念,这个是布局信息表,其包含了字形的位置、对齐、基线等等信息,字体文件则是由这一系列的表构成的,其中有部分表是可选的。字体目录是字体文件的指南,提供访问其他表所需的信息,包含两部分:偏移子表(offset subtable)和表目录(table directory)。偏移子表记录了字体文件中
table
的数量,并提供了快速访问表目录的方法。偏移子表后面就是表目录,表目录主要包含了表的tag
、校验、偏移、长度等信息,字体文件中的所有表都在表目录里面有入口。看了不少文档,每个文档对必选的
table
都有自己的解释,综合一下,下面是其中有几个是非常必要的table
:Unicode
,获得索引也就可以根据索引从字体中加载这个字形。上面几个表的介绍可能理解不太到位地方,因该差不多大致如此吧。另外,还有一些比如
OS/2
: 用于 windows 系统的配置,所以对跨平台的字体就非常需要了,但是若是针对 Mac 这些就不必了。具体的字形什么的,用
FontForge
软件打开任意一个字体就可以看到了,比如下面的:你甚至都可以修改字形。。。。
至于字体从加载到渲染出来的流程可以参考一下知乎上的介绍
上面介绍的是
cmap
根据字符代码拿到字形索引,再从loca
拿到索引对应的字形偏移,最后到glyf
加载字形的过程。loca
表可以参考以下图:每个字形都有自己长度,从而形成相对于
0
位置的偏移,而loca
表则是记录字形索引到字形偏移的映射表。当然这里面还有很多的表的内容没有谈到,比如和
TrueType
、CCF
、SVG
以及BitMap
相关的表,还有一个是高级表,比如GSUB
是glyf
的替换表,之前提到的一个字符代码最后可以映射到字形,但是如果是连字的时候,就不一定是简单的字形叠加,例如下面的:可以看到单独一个字的时候都是好好的,但是一旦结合在一起,就是不是
f + i = fi
了,而是有新的字形。这一点在阿拉伯语中也是的,字形在不同的位置有不同的显示。。。。。(原来阿拉伯语这么神奇,简直就是蝌蚪文)。除了高级表,还有色彩相关的,其他比较杂的,最后还有一个是
OpenType Font Variations
可变字体,也是OpenType
规范中,里面有avar
、cvar
、fvar
、gvar
、HVAR
、MVAR
、STAT
和VVAR
这几个。可变字体的 table
可变字体,如前面提到的,可以让设计者将多个字体合并为一个字体,下面的示意图很好的介绍了字重和字宽度变化导致字形的变化:
这里面看到的
width
和weight
都是fvar
表所描述的,用来存储轴的信息,以及命名实例,其中命名实例是可选的字段。轴的信息,比如wght(100-1000)
、width(10-200)
,包含了轴名称、最小最大值和默认值等,命名实例则是由轴与轴之间定下的命名的特定坐标,如下面几个:可以看到轴
wght = 400
以及wdth = 100
形成的坐标Regular
,也就是命名实例。Regular
是给特定坐标提供的预设名称,也是该子字体的名称,可以让使用者直接使用。使用可变字体的时候,如果没有指定子字体,其采用默认轴值。(css 里面修改font-variation-settings
,也就是实例了)。对于可变字体,有两个表是必须的:
fvar
和STAT(style attributes)
,后者是样式属性,每个在fvar
里面的每一条轴和子字体都需要在STAT
里面有对应的信息。STAT
用来区分字体族下面的不同的字体,支持动态属性,比如fvar
里面的wght(无极)
,也支持静态属性,比如italic
是否为斜体这些,展示Variable Font
下的样式名称,比如Medium
这样的字体。其他的表则是描述
fvar
里面字体轴变化时字形的变化情况,例如avar
,是非线性的轴变化数据,例如字体的width
轴,若变化区间是100-200
,线性的时候,则150
表示字体的字形宽度是两个极值的正中间,但是非线性变化,就使得值不是均匀的变化的,150
可能不是字形宽度上的正中间状态。这种非线性变化也符合用户习惯。还有gvar
,存储字形在轴上的变化信息,描述glyp
中各个点的变化情况,可以说是非常重要的。字体提取工具
看了上面的
table
介绍,字体的处理,其实就是对table
的处理,fonteditor-core
对可变字体的处理,看了一下源码的结构,well,根本就没有可变字体的表处理,连fvar
的踪迹都没有。于是开启大海捞针的方式,在
github
里面找,最后发现一个opentypejs/opentype.js
仓库,卧槽,难道是官方的嫡系部队?只是打开到结构目录还是很失望,都是三四年前的代码了,和fonteditor-core
差不多,虽然有fvar
表,但是其他可变字体的表一个都没有。抱着试一试的想法,用一下,最后的打包出来的字体,虽然比fonteditor-core
好不少,但是压根就不可变。。。。毕竟连gvar
也没有。最后看到了这个roadMap,里面介绍到:本来决定要放弃了,毕竟官方也不支持系列,但是总觉得有问题,难道可变字体没有工具?都好几年历史了,没有人造轮子吗。。。。
最后找到了字体处理的重量级库
fonttools
,一个python
库,打开一看密密麻麻的的table
处理,有 50 个以上的处理,对比一下fonteditor-core
的 18 个表处理,简直是。。。。。。在fonttools
里面也找到了各种各样的可变字体处理表,比如gvar
,只是对python
不是很熟悉,而且一上来就看源码,有点吃力,所以就放弃了(想起了看 esbuild 源码的经历)。fonttools
里面有很多工具,提取字体用的是pyftsubset
,通过指定文件字符来确定要输出的字形,基本上一顿操作下来,从 22M 的字体包,压缩到 300kb。正常来做这就可以了,但是 原本字体包还包含了斜、高度轴,这些轴,项目用不上,而且wght
也就用到了550-1000
的范围,能不能去掉剩下的部分呢? 这样不就可以完美压缩字体了,甚至wght
就用了550
和1000
两个值,其他的能不能抛弃掉呢?可能这个就要用专业的设计工具了(比如 Adobe Illustrator?),目前在fonttools
没有看到更多可操作空间,如果有大佬晓得一定要告知。一般可变字体的体积是要大于单个字体的(字体族里面的单个字体),只有当需要用到同一字体族的多个字体的时候,可变字体收益才很大。当然如果需要艺术字那就另当别论了。另外
font-variation-settings
是属于比较基础的 API 了,如果要设置字重的话,可以使用font-weight: 550
是不是很熟悉?这个和以前的 CSS 是一致的,只是CSS Fonts Level 4
做了扩展,当然还有其他几个轴的,比如font-stretch
。require
相比于字体,
require
可以说是以前的一个知识盲区。在Vue
里面,如果需要加载资源可以采用require
方式引入,但是时不时的总会遇到无法加载资源的问题。直到一次想要把资源路径作为props
传入组件,再通过require
来获取,结果是获取到图片了,但是还引发了另外一个严重的问题,页面的样式错乱?通过审查打包出现的代码,发现原本完全没有引入的
scss
文件都被打包到样式文件里面,如果去掉require(urlProp)
则一切正常,这个就很神奇了,而且前者的打出的包还很大。有种奇奇怪怪的感觉。后面耐心的看
webpack
文档才晓得:如果采用
require('./template/' + name + '.ejs')
的方式,那template
文件下面的所有ejs
文件都会被引用,形成一个上下文的map
对象,导致该目录下原本不会被使用的文件,也被打包使用上了,这也就是为什么使用了require(urlProp)
会加载上错误的资源,可想而知,若require
里面完全采用传参的方式,会使其无法分析正确的Directory
, 于是从根文件src
开始查询文件。。。。。至于要如何破局呢,
urlProp
为了可扩展性,是要从外部传入的,而里面要读取资源只能用require
了,直到看到了下面的require.context
的方式,表达式如下:通过在外部指定目录,和正则就能获得正确的资源路径,再传给
urlProp
就完美了。上面的
mode
配置呢,其实是和webpackMode
类似的,有sync
、eager
、lazy
、lazy-once
、weak
、async-weak
一共六种。其中sync
是默认的,会直接打包到文件里面,而lazy
则会生成可延迟加载单独的chunk
。这里我用到的是
lazy-once
。为什么呢,因为我需要从require.context
里面引入的资源非常多,肯定是要拆包的,而lazy
虽然是懒加载了,但是所有文件都单独形成chunk
,导致增加了很多文件,lazy-once
就很舒服,将所有文件合成一个chunk
,只需要通过promise
的方式获取正确的路径就可以了,比如urlProp(oneFileName).then(src => list.push(src))
这样的方式。总结
require
部分算是一个小知识点,至于深入的理解,比如Directory
目录的获取和分析,感觉有点类似,可能是@babel/parser
的形式,通过ast
分析表达式来获取目录?后面的理解就没有去研究了,倒是解决了一直以来使用require
的困惑(指不定以前有好多写的不太正常的 bug,采用require
多加载了多余文件。。。。。呵呵呵)。字体部分,更像是一个新领域的探索,想要不断的优化页面,而新版本的字体就是重中之重了,从开始的通过
node.js
来debug
,到最后定位到fonteditor-core
再到fonttools
,可以看到前端的字体轮子还是少了(比如参照fonttools
代码,更新fontedior-core
?)。更多的是学习字体的结构,看各个table
的作用,对字体的展示也有初步的理解,但是没有去研究代码层面的实现,没有深入去,更多的是浅尝辄止,可能兴趣就到这里吧,没有更多的想法了,想要深入探索更多的东西,更有价值的吧。写完的时候又一个台风飞过,今年的台风真是奇怪。
参考
技术文档,当然微软的是 Opentype,苹果的是 TrueType 与 AAT。
The text was updated successfully, but these errors were encountered: