5

前言

本文主要探索在安卓系统下浏览器中小字号中文居中的实现以及在混排时的对齐处理。本文是受《Deep dive CSS: font metrics, line-height and vertical-align》(以下简称为《Deep》)所启发,并以此为基础所写,建议先阅读前文,您也可以选择阅读大漠老师或方应杭老师的翻译版。大漠版 方应杭版

一直以来前端最简单的文字垂直居中方式就是line-height=height,浏览器会自动将line-height大于font-size的部分平分在文字上下,实现居中效果。但是,当网页中存在中文特别是10-12px的小字号中文时,在部分安卓机器上出现字符上飘,甚至超出容器的情况。
对于这种现象,网上流传着多种解决方案,比如tabel-cell法,flex法等。但是这类方法总是时灵时不灵,原因就在于这类方法只解决了将line-box相对外层容器居中的问题,必须要配合line-height:normal实现文字在line-box内居中才能解决问题。下文将对line-height:normal的生效原理和副作用处理进行研究。

神奇的安卓字体

根据《Deep》所述,文字中从小到大可以划出三个区域,分别是em-square,content-area和virtual-area。一般情况下,前一个区域大致居中与后一个区域,而文字本身也大致居中于这个区域。因此无论是采用哪种line-height,文字居中于line-box看起来都是一件理所当然的事。
但是,对于部分安卓系统的默认字体而言却不是这样。

下图的两个字的font-size和height都是10px,左边一个line-height为1即等于font-size,右边一个则为normal(由于DPR的原因,这里看到的像素点是实际上的三倍)。由于line-height属性不同。左边的line-box大小等于ex-square,右边的line-box大小等于virtual-area=content-area+line-gap
下图左侧红框内的淡灰色区域为em-square,高10px,深灰色区域为content-area高11px,右侧红框内的淡灰色区域为virtual-area高14px。可以看出,此时em-square位于content-area底部,字形则位于content-area顶部,所以字形完全没有居中于ex-square。
而右侧的行为则与《Deep》所述不同,virtual-area相对content-area多出来的3px大小的line-gap并不是平均分配与上下,而是全部堆在了顶部。因此恰好看起来文字居中于virtual-area。
demo1

应用

综上,line-height:normal可以使文字在那些奇怪的安卓机器上实现垂直居中。当然,这条样式会带来一个问题,即高度line-box的高度不可控,此时就需要前文所说的flex或tabel-cell将line-box
相对于外层容器居中,然后在外层容器设置高度即可。

下面是使用实例,起作用的样式是display: flex;align-items: center两条。创建一个弹性容器,然后将该容器的子元素居中,这样virtual-area多出来的部分就溢出到边框之外,而不会影响布局了。
图中红框内的浅灰色区域为高度12px的容器,深灰色为高度16px的line-box和virtual-area。最终实现了将12px大小的文字居中于12px大小的容器中的目的。

<span style="border:1px solid red;height:12px;font-size:12px;color: black;display: flex;align-items: center;">国</span>

图片描述

总结

回顾我们以前的做法。我们通常会为line-height设置一个具体的高度该高度就是line-box的高度。而浏览器会将字体的em-square居中于line-box中。对于大多数正常字体这么操作就可以实现垂直居中。
但是,部分安卓机器字体的字形不居中于em-square,却居中于virtual-area。此时通过line-height:normal样式使得virtual-area撑满line-box。从而实现文字居中于line-box。最后通过固定外层容器大小然后居中line-box的方式,消除前面的样式造成的line-box大小不可控的副作用。

PS:这种方案应用于多行文本的时,无法手动控制行间距,只能使用字体设计师决定的默认行间距。在需要手动控制行间距时,还是建议放弃此方案,反正对于多行文本,1~2px的偏移对整体视觉展现不会有太大的影响


WoodenSail
1.3k 声望18 粉丝