深入了解CSS:字体度量、line-height和vertical-align

原文地址:Deep dive CSS: font metrics, line-height and vertical-align

注:翻译中对内容进行了不少简化,力求不陷入繁缛的细节。

文章目录

  • 你真的了解它们吗
  • 首先聊聊font-size
  • line-box 和 inline-element
  • line-height相关的问题
  • vertical-align一个属性将它们串起来
  • CSS真奇妙
  • 知识点总结

你真的了解它们吗

很多人以为自己知道 line-heightvertical-align 是怎么工作的,以及怎么使用它们。但实际上,它们非常复杂,并且是在CSS特性 inline formating context 中扮演主要的角色。

测试你的理解情况,可以尝试回答下面几个问题:

问题1:line-height 可以是具体的单位值,也可以是无单位数值,但它的默认值是 normal,什么是 normal?通常我们把它当作1或1.2,又或者是1到1.2之间的数值?CSS的规范中都没有清楚地说明这一点。

问题2:我们知道line-height使用无单位数值的是相对 font-size而言,但问题是font-size:100px 对于不同的字体而言有着截然不同的表现,所以line-height的效果是相同的还是不同的呢?

问题3:vertical-align 对 line-height 有什么影响呢?

首先聊聊font-size

下面的简单HTML代码中,三个

标签包含了对应的 ,区别在于它们有不同的 font-family

Ba Ba Ba

p  { font-size: 100px }
.a { font-family: Helvetica }
.b { font-family: Gruppo    }
.c { font-family: Catamaran }
image.png

相同的 font-size 作用在不同的 font-family 上导致了不同高度,原因是:

  • 每个字体都有它的em-square(默认字体容器高度,字体就在这个容器内被绘制),通常它的基准单位被设置为1000单位,但也有字体是1024、2048单位,甚至任何单位
  • 正是基于这个em-square的单位,字体的ascend,descend,capital height,x-height等被确定

用 FontForge 打开 Catamaran 字体,可以看到它的一些数值(结合下面两张图来看):

  • em size(基准单位):1000
  • ascent(上升部分):1100
  • descent(下沉部分): 540
  • capital height(大写高度): 680
  • x height(小写高度): 485
image.png
image.png

总结:

Catamaran字体在1000单位的em-square 中使用了1640单位的高度,设置 font-size:100px 最终得到了 164px的高度,我们称这个高度为 content-area ,你可以将它等同于 background 起作用的高度。

line-box 和 inline-element

在下面的例子中,

元素受限于它的宽度,视觉上分了三行展示,每一行称作 inline-box。每一行都由匿名 或 带HTML标签的inner element组成,inline-box 的高度由它所包含的 inner element 决定。

line-box的高度肯定能容纳它的所有子元素,因此如果你知道了一个HTML元素的所有line-box的高度,那么你就知道了这个元素的高度(相加即可)。

下图中可以看到第二个line-box的高度大于另外两个,因为 Catamaran 字体的 content-area 高于其他的字体。

Good design will be better. Ba Ba Ba We get to make a consequence.

image.png

不过实际应用中,如果有多个line-box你是无法看到单个line-box的高度的。

line-height相关的问题

image.png

一个 inline element 有两个高度

  • content area,字体内容的高度(计算公式为:ascend+descend)
  • virtual area,用于计算line-box的高度,等于 line-height(作者发明了这个称呼)

virtual area 和 content area 高度的差值,分别对半被添加到了顶部和地步的 leading 中。依据 line-height的具体知道,这个差值可能为0、正数、负数。负数意味着virtual arae的高度低于content area。

还有一些类型的inline elements,他们的高度会由 height 、marign、border属性决定。如果 height 设置为 auto,那么(似乎默认就设置了,因此此处不强调它) content-area 的高度严格和line-height相等。

  • replaced inline elements:
  • inline-blockinline-* 元素
  • 在某些特殊的formating content 中(比如 flexbox中的元素)的inline元素

注:你可以尝试将

包含的 设置 display:inline-block,可以看到 content-area(背景颜色区域)变得和virtual area一样了。

所以回到开篇问题:line-height 的 normal 的值是多少?

我们以另外一个字体 Arial 为例,它的 em-square取的基准单位是 2048,ascender的高度是1854单位,descender的高度是434单位,行间距是67单位,计算得到font-size:100px时,content-area的高度是:

(1854+434) / 2048 * 100=112px(小数点后四舍五入),line-height:normal的高度是:(1854+434+67)/2048*100=115px。因此 line-height:normal 的高度通常是由字体的设计者决定的。

所以显而易见的是:设置 line-height:1 是一种糟糕的实践,因为我们它相对的是font-size(即em-square),而不是 content-area,结果就是 virtual area 比 content area小。

我(注:原文作者)统计了电脑上安装的1117中字体,发现95%的字体 line-height的值都大于1,计算得到的line-heigt的值在 0.618到3.378之间。

一些line-box计算的知识点

  • inline元素,padding和border会导致背景区域变大,但是content-area高度不变,可知content-area的高度并不总和你看到的一致;margin-top和margin-bottom没有效果。
  • 对于replaced inline elements设置 inline-block后,padding margin border都会增加高度:包括content-area和line-box的高度。

vertial-align一个属性将它们串起来

虽然到目前位置我们还没提起过 vertical-align,但实际上它可能是 inline formating context 的最重要的概念。

vertical-align 的默认值是 baseline,如果你还记得前面条的 ascender 和 descender的话,它们的value决定了baseline的位置,以及上下leading的比率(很少是50/50)。

Ba Ba

p {
    font-family: Catamaran;
    font-size: 100px;
    line-height: 200px;
}

一个

标签包含两个相邻的 元素,它们都继承相同的 font-family , font-sizeline-height 。两个 元素的baseline对齐并且line-box的高度和line-height一样。

image.png

如果我们把第二个元素的 font-size 设置得更小一些会怎么样?

span:last-child {
    font-size: 50px;
}

可以看到,默认的baseline对齐导致了更高的line-box:line-box的高度计算方式是由子元素的最高点到最低点

image.png

让我们看看另外一个例子,一个

标签设置了 line-height: 200px,包含一个 继承了父级的 line-height

Ba

p {
    line-height: 200px;
}
span {
    font-family: Catamaran;
    font-size: 100px;
}

line-box的高度会是多少呢?我们期望它是200px,事实上

有它自己的 font-famliy,结果就是

的baseline和 的baseline不同,更高的baseline导致了更高的context-area的高度。在规范中这个规则叫strut:0宽度的字符也会参与line-box的计算当中。

image.png

直接使用 vertical-align: middle 能解决这个问题吗?如规范中所说, middle 对齐的是:垂直中点加上x-height高度的一半。考虑到上下ratio并不相同,设置为 middle 也不可靠,在大多数情况下,middle 并不表示的绝对中间位置。

顺带提一下,还有4个值可能会有用:

  • vertical-align: top / bottom 对齐line-box的头部和底部
  • vertical-align: text-top / text-bottom 对齐content-area的头部或底部
image.png

当仍旧要小心的是,多数情况下对齐的参照标准是 virtual area,看看同样使用了 vertical-align: top 的例子:line-height导致了奇怪但并不令人惊讶的结果

image.png

最后, vertical-align 可以忽略baeline的位置接受数字类型的值,它可能会派上用场。

CSS真奇妙

最后原文作者演示了如何设置一个字体的各个部位的高度,可以做到让不同字体在同一个 line-box齐整的对仗,但在实际中有若干理由并不推荐你这么做。

知识点总结

  • inline formatting context 是一个实在难以理解的概念
  • 所有的inline elements有两个高度:context area 和 virtual area,并且它们都无法被视觉查看到
  • line-height: normal 是基于每种字体的设计而定的
  • vertical-align 是不可靠的
  • 一个 line-box 高度计算是基于所有子元素的 line-heightvertical-align
  • 在CSS中查询或设置字体的度量并不容易

你可能感兴趣的:(深入了解CSS:字体度量、line-height和vertical-align)