深度了解CSS里面字体规则、行高和垂直对齐方式

小蜗牛🐌🐌🐌
• 阅读 928

大家熟知的Line-heightvertical-align是简单的CSS属性。如此简单,以至于我们大多数人都确信完全理解它们的工作原理和使用方法。真的是这样么,我觉得事实并非如此。它们确实很复杂,也是CSS中难点之一,因为它们在创建 CSS 鲜为人知的各种功能方面发挥着重要作用:内联格式化上下文

例如,line-height可以设置为长度或无单位值,但默认为normal. 好的,但什么是正常?我们经常读到它是(或应该是)1,或者可能是 1.2,甚至CSS 规范在这一点上也不清楚。我们知道无单位line-heightfont-size相对的,但问题是它在不同字体系列中表现不同,所以总是相同还是不同?真的在1到1.2之间吗?并且,它的含义是什么?font-size: 100px``line-height``vertical-align``line-height

深入研究一个不那么简单的 CSS 机制……

我们font-size先来说说

看看这个简单的 HTML 代码,一个<p>包含 3 的标签<span>,每个都有不同的font-family

<p>
    <span class="a">Ba</span>
    <span class="b">Ba</span>
    <span class="c">Ba</span>
</p>
p  { font-size: 100px }
.a { font-family: Helvetica }
.b { font-family: Gruppo    }
.c { font-family: Catamaran }

将相同font-size的字体与不同的字体系列一起使用会产生具有不同高度的元素:

深度了解CSS里面字体规则、行高和垂直对齐方式

*

不同的字体系列,相同的字体大小,给出不同的高度
*


即使我们知道这种行为,为什么不创建 100px 高度的元素呢?我测量并发现了这些值:Helvetica:115px,Gruppo:97px 和 Catamaran:164pxfont-size: 100px

深度了解CSS里面字体规则、行高和垂直对齐方式

*

100px 的元素高度在 97px 到 164px 之间变化
*


虽然一开始看起来有点奇怪,但这是完全可以预料的。**原因在于字体本身**。下面是它的工作原理:
  • 一种字体定义了它的em-square(或 UPM,units per em),一种将在其中绘制每个字符的容器。这个平方使用相对单位,一般设置为1000个单位。但它也可以是 1024、2048 或其他任何值。
  • 根据其相对单位,设置字体规格(升序、降序、大写高度、x 高度等)。请注意,某些值可能会溢出到 em-square 之外。
  • 在浏览器中,相对单位被缩放以适合所需的字体大小。

让我们使用 Catamaran 字体并在FontForge中打开它以获取指标:

  • em-square 是 1000
  • ascender 是 1100,descender 是 540。运行一些测试后,浏览器似乎在 Mac OS 上使用HHead Ascent / Descent值,在 Windows 上使用 Win Ascent / Descent值(这些值可能不同!)。我们还注意到资本高度为 680,X 高度为 485。
    ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/38f8e5245f2d42b0b0cf5824dc0dc331~tplv-k3u1fbpfcp-zoom-1.image)

*

font-size: 使用 FontForge 的字体规格值
*


这意味着 Catamaran 字体在 1000 个单位的 em-square 中使用 1100 + 540 个单位,这在设置时给出 164px 的高度。**这个计算出的高度定义了** **元素的** ***内容区域***,我将在本文的其余部分引用这个术语。您可以将*内容区域*视为属性适用的区域[2](https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align#fn-2 "这不是严格意义上的")。`font-size: 100px`****** **`background`[](https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align#fn-2 "这不是严格意义上的")

我们还可以预测大写字母高 68px(680 个单位),小写字母(x 高度)高 49px(485 个单位)。因此,1ex= 49px 和1em= 100px,而不是 164px(谢天谢地,em是基于font-size,而不是计算的高度)

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 双体船字体:UPM —Units Per Em—和使用字体大小等效的像素:100px
*


在深入探讨之前,请简要说明它涉及的内容。当一个<p>元素在屏幕上呈现时,它可以根据其宽度由许多行组成。每行由一个或多个内联元素(HTML 标签或文本内容的匿名内联元素)组成,称为line-boxline-box 的高度基于它的孩子的高度**。因此,浏览器会计算每个内联元素的高度,从而计算行框的高度(从其子项的最高点到其子项的最低点)。因此,line-box总是足够高以包含其所有子项(默认情况下)。

每个 HTML 元素实际上是一堆line-boxes。如果你知道每个line-box的高度,你就知道元素的高度。

如果我们像这样更新之前的 HTML 代码:

<p>
    Good design will be better.
    <span class="a">Ba</span>
    <span class="b">Ba</span>
    <span class="c">Ba</span>
    We get to make a consequence.
</p>

它将生成 3 个行框

  • 第一个和最后一个都包含一个匿名内联元素(文本内容)
  • 第二个包含两个匿名内联元素,第三个<span>

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: font-size: A <p>(黑色边框)由包含内联元素(实线边框)和匿名内联元素(虚线边框)的行框(白色边框)组成
*


我们清楚地看到第二个*线框*比其他线框高,这是因为计算了它的子项的*内容区域*,更具体地说,是使用 Catamaran 字体的那个。

line-box创建的难点在于我们不能真正看到,也不能用 CSS 控制它。即使应用背景也不会给我们任何关于第一个line-box高度的视觉线索。::first-line**

line-height: 问题及其他

到现在为止,我介绍了两个概念:content-arealine-box。如果你没看错的话,我说过line-box的高度是根据它的children的高度来计算的,我并没有说它的children content-area的高度。这有很大的不同。

尽管这听起来很奇怪,但内联元素有两个不同的高度:内容区域高度和虚拟区域高度(我发明了术语虚拟区域,因为高度对我们来说是不可见的,但你不会发现任何出现在规范中)。

  • 内容区域高度由字体规格定义(如前所述)
  • virtual-area height 是,line-height它是*用于计算line-box*高度的高度

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: font-size: 行内元素有两种不同的高度
*


话虽如此,它打破了`line-height`基线之间距离的普遍看法。在 CSS 中,它不是[3](https://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align#fn-3 "在其他编辑软件中,它可能是基线之间的距离。 在 Word 或 Photoshop 中,就是这种情况。 主要区别在于第一行在 CSS 中也受到影响")。

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: font-size:font-size: 在 CSS 中,行高不是基线之间的距离
*


*计算出的虚拟区域*和*内容区域*之间的高度差称为行距。*前导的一半添加到内容区域*的顶部,另一半添加到底部。**因此*content-area*总是在*virtual-area***的中间。

根据其计算值,line-height( virtual-area ) 可以等于、高于或小于content-area。在较小的virtual-area的情况下,leading 是负的并且line-box在视觉上比它的孩子小。

还有其他类型的内联元素:

  • 替换内联元素(<img>, <input>,<svg>等)
  • inline-block和所有inline-*元素
  • 参与特定格式化上下文的内联元素(例如,在 flexbox 元素中,所有 flex 项目都是块化的)

对于这些特定的内联元素,高度是根据它们的height,marginborder属性计算的。如果heightauto,则line-height使用 并且内容区域严格等于line-height.

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size:内联替换元素、inline-block/inline-* 和块化内联元素的内容区域等于它们的高度或行高


无论如何,我们仍然面临的问题是`line-height`的`normal`价值是多少?至于*内容区域*高度的计算,答案可以在字体指标中找到。

那么让我们回到 FontForge。双体船的 em-square 是 1000,但我们看到许多上升/下降值:

  • generals Ascent/Descent : ascender 为 770,descender 为 230。用于人物绘图。(表 “OS/2”
  • metrics Ascent/Descent:ascender 为 1100,descender 为 540。用于content-area*的高度。(表 *“hhea” 和表 “OS/2”
  • 公制线间隙。用于, 通过将此值添加到上升/下降*指标。(表 *“hhea”line-height: normal****

在我们的例子中,Catamaran 字体定义了一个 0 单位的行间距,因此等于 content-area ,即 1640 个单位,或 1.64line-height: normal**

作为比较,Arial 字体描述了一个 2048 个单位的 em-square,一个 1854 个单位的 ascender,一个 434 个单位的下行和一个 67 的行间距。这意味着给出了112px(1117 个单位)的内容区域和115px的内容区域(1150 个单位或 1.15)。所有这些指标都是特定于字体的,由字体设计师设置。font-size: 100px**line-height: normal

很明显,设置是一种不好的做法line-height: 1。我提醒您,无单位值是font-size相对的,而不是内容区域的相对值,处理小于内容区域的 虚拟区域是我们许多问题的根源。

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 使用 line-height: 1 可以创建一个小于 content-area 的 line-box
*


但不仅如此。对于它的价值,在我的计算机上安装的 1117 种字体(是的,[我安装了所有来自 Google Web Fonts 的字体](https://github.com/qrpike/Web-Font-Load))中,1059 种字体(大约 95%)的计算值大于 1。它们的计算值从 0.618 到 3.378。你读得很好,3.378!`line-height: 1`[](https://github.com/qrpike/Web-Font-Load)`line-height``line-height`

line-box计算的小细节:

  • 对于内联元素,padding增加border背景区域,但不增加content-area的高度(也不增加line-box的高度)。因此,内容区域并不总是您在屏幕上看到的。margin-top并且margin-bottom没有效果。
  • 对于替换的内联元素inline-block块化的内联元素:paddingmarginborder增加height,因此内容区域行框的高度

vertical-align: 一个属性来控制一切

我还没有提到这个vertical-align属性,尽管它是计算line-box高度的一个重要因素。我们甚至可以说,vertical-align内联格式化上下文可能具有主导作用

默认值为baseline。您是否提醒字体指标升序器和降序器?这些值决定了基线的位置,以及比率。由于上升器和下降器之间的比例很少是 50/50,它可能会产生意想不到的结果,例如兄弟元素。

从该代码开始:

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

具有 2 个兄弟姐妹的<p>标签<span>inheritingfont-familyfont-sizefixed line-height。基线将匹配并且line-box的高度等于它们的line-height

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 相同的字体值,相同的基线,一切似乎都OK
*


如果第二个元素有一个更小的`font-size`怎么办?
span:last-child {
    font-size: 50px;
}

听起来很奇怪,默认基线对齐可能会导致更高(!)的 line-box,如下图所示。我提醒你,一个line-box的高度是从它的孩子的最高点到它的孩子的最低点计算的。

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 较小的子元素可能会导致较高的行框高度
*


这可能是支持使用[无单位值](http://allthingssmitty.com/2017/01/30/nope-nope-nope-line-height-is-unitless/)[的论据`line-height`](http://allthingssmitty.com/2017/01/30/nope-nope-nope-line-height-is-unitless/),但有时您需要固定的值来[创建完美的垂直节奏](https://scotch.io/tutorials/aesthetic-sass-3-typography-and-vertical-rhythm#baseline-grids-and-vertical-rhythm)。**老实说,无论你选择什么,你总是会遇到内联对齐的问题**。

看看这个另一个例子。带有的<p>标签,包含单个继承line-height: 200px``<span>``line-height

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

line-box有多高?我们应该期望 200px,但这不是我们得到的。这里的问题是<p>有自己的不同font-family(默认为serif)。<p>标签和标签之间的基线<span>可能不同,因此行框的高度比预期的要高。发生这种情况是因为浏览器在进行计算时就好像每个行框都以零宽度字符开头,该规范称为 strut。

一个看不见的角色,但一个看得见的影响。

继续,我们面临着与兄弟元素相同的先前问题。

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 每个子元素都对齐,就好像它的行框以一个不可见的零宽度字符开头一样
*

基线对齐被拧紧了,但是如何进行救援呢?正如您在规范中看到的那样,“将盒子的垂直中点与父盒子的基线加上父盒子 x 高度的一半对齐”。基线比率不同,x 高度比率也不同,因此 对齐也不可靠。最糟糕的是,在大多数情况下,永远不会真正“处于中间”。涉及的因素太多,无法通过 CSS 设置(x-height、ascender/descender ratio 等)vertical-align: middle``middlemiddlemiddle

作为旁注,还有 4 个其他值,在某些情况下可能有用:

  • vertical-align: top/bottom对齐到行框的顶部或底部
  • vertical-align: text-top/text-bottom对齐到内容区域的顶部或底部

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size:垂直对齐:顶部、底部、文本顶部和文本底部
*


但要小心,在所有情况下,它都会对齐*virtual-area*,因此是不可见的高度。看看这个使用. **Invisible** **可能会产生奇怪但不足为奇的结果**。`vertical-align: top`**`line-height`

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: vertical-align 一开始可能会产生一些错乱,但在可视化 line-height 时是常常见到的
*


最后,`vertical-align`还接受相对于基线升高或降低框的数值。最后一个选项可能会派上用场。

CSS无所不能

我们已经讨论了如何协同工作,但现在的问题是:字体规格是否可以通过 CSS 控制line-heightvertical-align简短的回答:没有。即使我真的希望如此。无论如何,我认为我们必须玩一下。字体指标是恒定的,所以我们应该能够做一些事情。

例如,如果我们想要一个使用 Catamaran 字体的文本,其中大写高度正好是 100px 高怎么办?似乎可行:让我们做一些数学运算。

首先我们将所有字体指标设置为 CSS 自定义属性4,然后计算font-size得到 100px 的大写高度。

p {
    /* font metrics */
    --font: Catamaran;
    --fm-capitalHeight: 0.68;
    --fm-descender: 0.54;
    --fm-ascender: 1.1;
    --fm-linegap: 0;

    /* desired font-size for capital height */
    --capital-height: 100;

    /* apply font-family */
    font-family: var(--font);

    /* compute font-size to get capital height equal desired font-size */
    --computedFontSize: (var(--capital-height) / var(--fm-capitalHeight));
    font-size: calc(var(--computedFontSize) * 1px);
}

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size:文本高度现在是 100px 高
*


很简单,不是吗?但是,如果我们希望文本在视觉上位于中间,以便剩余空间均匀分布在“B”字母的顶部和底部怎么办?为此,我们必须`vertical-align`根据上升/下降比率进行计算。

首先,计算content -area的高度:line-height: normal**

p {
    …
    --lineheightNormal: (var(--fm-ascender) + var(--fm-descender) + var(--fm-linegap));
    --contentArea: (var(--lineheightNormal) * var(--computedFontSize));
}

那么,我们需要:

  • 大写字母底部到底部边缘的距离
  • 大写字母顶部到上边缘的距离

像这样:

p {
    …
    --distanceBottom: (var(--fm-descender));
    --distanceTop: (var(--fm-ascender) - var(--fm-capitalHeight));
}

我们现在可以计算vertical-align,这是距离乘以计算所得的差值font-size。(我们必须将此值应用于内联子元素)

p {
    …
    --valign: ((var(--distanceBottom) - var(--distanceTop)) * var(--computedFontSize));
}
span {
    vertical-align: calc(var(--valign) * -1px);
}

最后,我们设置 desiredline-height并在保持垂直对齐的同时计算它:

p {
    …
    /* desired line-height */
    --line-height: 3;
    line-height: calc(((var(--line-height) * var(--capital-height)) - var(--valign)) * 1px);
}

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 文本居中时候不同行高的显示情况
*


添加一个高度与字母“B”匹配的图标现在很容易:
span::before {
    content: '';
    display: inline-block;
    width: calc(1px * var(--capital-height));
    height: calc(1px * var(--capital-height));
    margin-right: 10px;
    background: url('https://cdn.pbrd.co/images/yBAKn5bbv.png');
    background-size: cover;
}

深度了解CSS里面字体规则、行高和垂直对齐方式

*

font-size: 图标和B字母等高
*


[在 JSBin 中查看结果](http://jsbin.com/tufatir/edit?css,output)

请注意,此测试仅用于演示目的。你不能依赖这个。很多原因:

  • 除非字体规格是常量,否则浏览器中的计算不是¯⁠\ ⁠(ツ)⁠ /⁠¯
  • 如果未加载字体,后备字体可能具有不同的字体规格,并且处理多个值将很快变得非常难以管理

总结

我们学到了什么:

  • 内联格式化上下文真的很难理解

  • 所有内联元素都有 2 个高度:

    • 内容区域(基于字体规格)
    • 虚拟区域( line-height)
    • 毫无疑问,这两个高度都无法想象。(如果你是一名 devtools 开发者并且想从事这方面的工作,那可能会很棒)
  • line-height: normal基于字体规格

  • line-height: n可以创建一个小于内容区域的 虚拟区域**

  • vertical-align不是很可靠

  • line-box的高度是根据其子项line-heightvertical-align属性计算的

  • 我们无法使用 CSS 轻松获取/设置字体指标

但我仍然喜欢 CSS :)

点赞
收藏
评论区
推荐文章
Python进阶者 Python进阶者
1年前
一篇文章带你了解CSS定位知识
大家好,我是IT共享者,人称皮皮。这篇我们来讲讲CSS定位。一、Position(定位)CSS定位属性允许你为一个元素定位。它也可以将一个元素放在另一个元素后面,并指定一个元素的内容太大时,应该发生什么。元素可以使用的顶部,底部,左侧和右侧属性定位。然而,这些属性无法工作,除非是先设定position属性。他们也有不同的工作方式,这取决于定位方法。二、属
徐小夕 徐小夕
2年前
用css3实现惊艳面试官的背景即背景动画(高级附源码
我们传统的前端更多的是用javascript实现各种复杂动画,自从有了Css3transition和animation以来,前端开发在动画这一块有了更高的自由度和格局,对动画的开发也越来越容易。这篇文章就让我们汇总一下使用Css3实现的各种特效。这篇文章参考《css揭秘》这本书,并作出了自己的总结,希望能让大家更有收获,也强烈推荐大家看看这本书,你值得拥有
徐小夕 徐小夕
2年前
css3实战汇总(附源码)
本文是继上一篇文章用css3实现惊艳面试官的背景即背景动画(高级附源码)(https://juejin.im/post/6844903950123188237)的续篇也是本人最后一篇介绍css3技巧的文章,因为css这块知识难点不是很多,更多的在于去熟悉css3的新特性和基础理论知识。所以写这篇文章的目的一方面是对自己工作中一些css高级技巧的总结,另一
Python进阶者 Python进阶者
1年前
一篇文章带你了解CSS单位相关知识
大家好,我是皮皮,今天给大家分享一些前端的知识。一、了解CSS单位测量长度的单位可以是绝对的,例如像素,点等,也可以是相对的,例如百分比(%)和em单位。指定CSS单位对于非零值是必须的,因为没有默认单位。丢失或忽略单位将被视为错误。但是,如果该值为0,则可以省略该单位(毕竟,零像素与零英寸是一样的)。注意:长度是指距离测量。长度包括数字值
Stella981 Stella981
1年前
CSS兼容性(IE和Firefox)技巧大全
CSS对浏览器的兼容性有时让人很头疼,或许当你了解当中的技巧跟原理,就会觉得也不是难事,从网上收集了IE7,6与Fireofx的兼容性处理技巧并整理了一下。对于web2.0的过度,请尽量用xhtml格式写代码,而且DOCTYPE影响CSS处理,作为W3C的标准,一定要加DOCTYPE声明。CSS技巧1.div的垂直居中问题verti
Wesley13 Wesley13
1年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
1年前
CSS3的基础知识学习(二)
前言:CSS主要是用来修饰标签的样式、使得网页更加美观,其实css的引用有三种方式,但主要是使用链接引用,意思就是我们写一个.css的文件,将样式修饰的控制代码写在这里面,这样控制起来方便快捷。对于CSS的学习,知识点中最重要的部分就是属性和选择器,其次还有页面布局和盒子模型,下面进行学习以及代码效果演示。一、CSS的三种引入
Wesley13 Wesley13
1年前
CSS基础知识整理
1什么是CSS?CSS通常称为CSS样式表或层叠样式表(级联样式表),主要用于设置HTML页面中的文本内容(字体、大小、对齐方式等)、图片的外形(宽高、边框样式、边距等)以及版面的布局等外观显示样式。CSS以HTML为基础,提供了丰富的功能,如字体、颜色、背景的控制及整体排版等,而且还可以针对不同的浏览器设置不同的样式。
徐小夕 徐小夕
10个月前
前端: 如何更高效的学习Css? 有哪些库值得推荐?
之前有很多朋友问我如何快速学习css以及有哪些好用的css库,最近也抽出时间思考整理了一下,今天就和大家分享一下我的经验.如何高效学习Css之前在工作中也使用css做过很多有意思的事情,比如用css画图标,写轮播图,写动效,做布局等等,但是这些应用的实现都依赖于html和css基础知识.根据我自己的学习经验,我们需要掌
京东云开发者 京东云开发者
1个月前
如何优雅的写 css 代码
CSS(全称CascadingStyleSheets,层叠样式表)为开发人员提供声明式的样式语言,是前端必备的技能之一,基于互联网上全面的资料和简单易懂的语法,CSS非常易于学习,但其知识点广泛且分散,很难做到精通,在我们日常开发中,常常忽视了CSS代码的质量,很容易写出杂乱无章的CSS文件。
小蜗牛🐌🐌🐌
小蜗牛🐌🐌🐌
Lv1
情报组
不要怪别人让你失望,怪你自己期望过高。
3
文章
0
粉丝
1
获赞