认识字体度量、重新理解Line-Height及垂直居中
前言
上课之前,先问下大家在平常的开发中当你是否遇到过UI设计师天天来找你还原一个按钮文案或者某个元素中文案垂直居中时,文案偏上或偏下的情况,又或者你应用了line-height:1,然后自己测试时发现有的机型的文字被裁剪了的情况。如果有,恭喜您,你踩中了字体度量这个恶心的坑了。
那么这节课我们就来认识下什么是字体度量,它和line-height是什么关系,以及它影响到我们样式的原理。
首先来一个问题
Question 1
请看页面上这个文字,如果现在这个'Ba'的font-size是100px,那这个端字的宽和高分别是多少,是否都是100px?
对,没错,我们会发现是不一定的,而且不同的字体font-family,渲染出来的宽高也是各自不同的,我们这里叫拿影响最大的字体的高度来说,其渲染出来之时几乎都是比我们设置的font-size大小还要高的。在我们开发中或多或少都是知悉这种现象的,而且往往会称其为字高或者字体基线高,但正确的说法应该是字体度量和line-height所计算得出字体可视高度。要认识字体度量,让我们先抛弃杂念,先来认识一下 em-square 这个东西。
em-square
em-square,中文名叫没有,由于em代表字模高度,square指方形比例,这里我们暂译称它为字模高度方形容器,英文名也被称作“EM size”或者“UPM”
字模方形容器的大小通常是1000单位,也有1024或2048单位的情况,即字模容器的宽高比是1000 x 1000,方形嘛,也就是1:1啦,如果这里有点难懂,我举个最生动的例子。em-square就像是我们的以前小学方格簿的方格。
我们的每个字体就是定位在这样一个方格里的,当我们样式应用到时,浏览器会帮我们把字体等比例缩小到我们指定的像素,比如14px,那么方格容器就是14 x 14了,那你这时肯定会说那我的字体font-size:14px那宽高不就应该是14px吗。按理说是这样子没错,但是,em-square允许字体的笔画超出它的宽高,超出的部分我们称其additionalWidth和additionalHeight,这俩家伙就是使我们字体宽高不等于font-size大小的罪魁祸首之一。
字体度量
所以字体设计师他们在设计字体的时候,在字模方形容器中会定义几条线,而字体度量的高度就是根据这几条线来确定的,接下来我们来认识一下这几条比较关键的线
Baseline:顾名思义就是基线啦,即每个字母主体底部对齐的那条线,这里我们都是以英文来说的,因为我们默认都是用的系统的英文字体。
X-Height:小写字母x在基线上时其最高点的那条线,即小写字母(无探头字母,探头字母例如k)的高度
Cap Height:这一套字体中大写字母所能达到的最高点的那条线,也就是大写字母的高度
那超出em-square的additionalWidth和additionalHeight这俩个有没有线可以界定的,有的,这里以高度来说。
Ascender:整套字体中突出容器的最高点所在的那条线,其高度为突出的最高点到baseline的距离(通俗点说就是整套字体里最高的那个字的最高点)
Descender:整套字体中突出容器的最低点所在的那条线,其高度为突出的最低点到baseline的距离(通俗点说就是整套字体里最底的那个字的最低点)
所以我们字体的实际高度为font-size所赋值的大小,即问题1中100px,只是让她的em-square等比例缩放了而已,但超出的高度缩放后也仍是超出的,所以真正渲染到页面中的实际高度,也就是我们网页中的content-area高度,为Ascender + Descender,即图中的164px,这样就可以确保任何一个字都能正常显示。
所以,由于字体本身在容器中的位置,以及Ascender和Descender的影响,其上下的透明间距不一定是相等,因为其受字体设计师的主观设计影响,所以就会出现字体你明明尝试了各种垂直居中的方法,字体却还是偏上或偏下的情况,这种现象在安卓贼为明显,ios的默认字体设计是公认的上下比较工整对齐的,所以页面垂直居中在ios出现偏差会比较小。
Line-Height
认识完字体度量,如果没什么不懂的话让我们接下来看看line-height和他的关系,以及他们之间会产生什么无法预知的后果。
我们上面已经解释了字体的在页面中渲染出来的可视化高度是怎么得来的,然而,还有一个比他优先级更高的样式属性限制着它,它就是line-height,我们把字体的
那么
Question 2
line-height的值可以是哪些?请举例说一说
对,我们常用的line-height可以是normal、none,也可以是基于font-size比例的数字,也可以是具体的某个值 N px。
那line-height的高度是怎么作用于字体的高度上的呢,是从字体的底部向上算起,还是在顶部向下算起,不,都不是,它将会与字体的content-area进行比较计算,如果大于字体的content-area,则多出来的高度会被平均成两半加在顶部和底部,小于时则是在顶部和底部进行减除。
那么,问题来了:
Question 3
当一个字体的可视化高度例如Catamaran为164px时,如果你将line-height设置为1,且你的字体大小设置为100px,那么渲染出来的结果会是什么样的?
让我们看下屏幕,好像没问题,但你鼠标移上去一看,会发现,高度变为100px了,能显示呀,没啥问题呀,那如果line-height设为0.5呢,哦豁,字体顶部被裁掉了,你可能会说那我又不会有设置line-height为0.5的时候,但是这只是一种字体,市面上字体成千上万,如果刚好遇到一种字体设置1时就炸裂了,那等到出问题要补救岂不是凉凉。所以,line-height为数字或者具体的值要少用,除了确定好对字体显示真的无影响且设置行高需要才使用,像设为1再加padding设置居中什么这种我想现在应该是没人用了。
这里就再科普下normal这个值吧,你们认为normal的值是多少?1?1.2?1.5?
No!no!no!
normal的值其实哪个都不是,它是定义在字体里的,我们来看看Catamaran这个字体的设计属性
我们normal的值是由字体的Line Gap所决定,那它怎么影响呢?我们字体的content-area高度有一条公式,即 ** Ascent + Descent + Line Gap ** 其实就是line-height和content-area之间的差异高度,所以Catamaran这个字体设置line-height为normal时它的高度为164px,相当于line-height为1.64,font-size为100px或者line-height为164px的情况。
Question 4
现在如果Line Gap设置为80那么font-size为100px且line-height为normal时大写B的高度为多少,上下分别增加了多少px?
对,高度为224px,上下分别增加了40px。
看来你已经掌握的差不多了,其实字体度量不单单影响到line-height,vertical-align等很多跟字体基线、字体高度有关的样式属性也是一样的,所以vertical-align你设置middle是不是有时也会发现并不居中,特别是不同字体情况下,或者字体跟图标垂直对齐,因为都受字体设计影响。举个例子 middle 的意思是「用父元素 baseline 高度加上父元素中 x-height 的一半的高度来对齐当前元素的垂直方向的中点」,这里还是快速给过一下vertical-align这个属性。
看图,这是一个vertical-align为baseline的情况
middle也就如我们上面所说的定义一样,也是受字体大小影响的,下面再放一张top/bottom/text-top/text-bottom的情况
如上图所示,但你要记得,vertical-align为top或bottom时对齐的顶点是包括line-height加上去的高度的,而不是文字自身的content-area。
所以,这些属性都是字体设计师决定的,用vertical-align来实现垂直方向各种对齐其实也是很不靠谱的,这个属性大多时候还是拿来顶部或底部对齐行块元素的。
总结
介绍了这么多,有什么解决方法吗,有,但不实际,平常很少会应用上。
首先,你得知道你使用的字体的Ascent和Descent,然后在css定义好变量然后利用calc去计算,因为很繁琐,这里我就不列出来了,一是calc是比较消耗性能的一个属性,二是每次需要知道字体的设计属性,正常UI设计师自己都不知道,所以可行性不高,了解即可,这里我丢个参考链接出来
https://zhuanlan.zhihu.com/p/25808995
当然阿里有个大牛提出了一个解决方法,在安卓7.0以上的版本上设置 lang 属性:同时font-family不指定英文字体,利用浏览器的字体fallback机制,查询并应用中文字体,目前中文字体都是无超出em-square容器的情况的,所以相对偏差不大,但如果手机没中文字体那就裂开了。
所以,目前要控制字体度量造成的影响其实还没有比较好的方法出现,并非那么容易征服它的,当然你可以找个字体设计师专门定制一套字体哈,那就没问题了。那么我就其影响比较大的垂直居中和行高这方面给出我的建议:
1、针对垂直居中
现在flex布局移动端兼容性已经很好了,所以UI设计师能接受字体度量带来的微误差的话就用flex去实现垂直居中吧,其次还有上下padding和绝对定位+transform,当强烈接受不了字体度量带来的影响时建议采用连文字一起切图,而不是采用后续写死transform或margin或padding位移微调这种方式,很不靠谱的,经常换个机型就boom。
2、针对line-height
少用或不用line-height为1的情况,或许我们测试时我们的字体没问题,但用户手机可能会用自己下载的个性化字体,那往往就会出现问题了,用来定义行高间距时妥善使用1以上的值,或者具体的数值,当然你最好提前看一下你的字体的content-area是多高,防止裁切情况的发生。段落的行高能用padding或margin就用padding或margin,没必要时不要随意修改line-height。
3、帮你UI还原时更好地砍需求
结尾
以上就是本节课的内容,希望对你能有所帮助,感谢您的阅览,谢谢~