深入研究CSS字体度量及CSS 盒子

本文首发于[Godfery的博客]。

感谢Godfery为大家贡献的优秀文章,大家可以通过点击本文下方的阅读原文来访问Godfery的博客

这张图展示的是8种不同的字体,其中第一、第二个分别为 font-awesome图标、自定义的字体图标,其余字体依次为Avenir、Trebuchet MS、Arial、Helvetica、Hiragino Sans GB、STXihei。(源码地址:https://codepen.io/hiyangguo/pen/VXqQMB

这些字符的font-size:100px,但是占的高度却不一样。有的是 100px,有的大于 100px。另外可以看出,垂直方向并没有居中对齐。 这篇文章主要研究:

  • font 的工作原理及度量参数
  • CSS box models 的类型、定义

字体度量

要弄明白上面问题的答案,需要先从字体说起:

我们拿出其中Avenir、Helvetica、Hiragino Sans GB三种字体进行分析

由上图可知,在我们设置 font-size:100px 时,文字所占的高度分别为 137px、 115px 和 100px。

感觉有点懵啊。怎么 font-size:100px ,可是高度却由于字体不同,而不一样了呢? 在字体设计中一个字符所在的空间容器称为EM Square(也被称作“EM size”或者“UPM”)。

在传统的金属字模中,这个容器就是每个字符的实际金属块。每个字符的高度是统一的,这样每个字模可以整齐地放进行和块中(如下)。

字体的定义规则

  • 字母的高度被称为“em”,在数字化字体中 em 是空间的数字化定义总量。em的大小(以下均写为: EM size)通常是 1000 单位,在 TrueType 字体中,EM size 约定是2的幂,通常是1024或2048。
  • 根据其实际使用的单位,字体的度量可以根据一些设置来决定。注意,有些值是em-square之外的值。
  • 在浏览器中,相对单位是用于缩放用来适应所需的 font-size

字体的设置

这是一张详解字体设置的图例,图中各个属性的意义:

  • baseline (基线): 分隔 ascent 和 descent ,默认字符底端沿 baseline 排列,如图中的P,x,Ё(为俄文字符)
  • ascent (上升): 基线的上部分,字符最高处与 ascent 顶端可能有空白,由 font-family决定
  • descent (下降): 基线的下部分,字符最低处与 descent 底端可能有空白,由 font-family 决定
  • xHeight (X 字高): 小写字符 x 的高度,由 font-family 决定
  • capHeight (顶面高度): 大些字符 P 的高度,由 font-family 决定
  • lineSpacing (行间距): 在浏览器中一般 lineSpacing = ascent + descent
  • lineHeight (行高): 默认等于 lineSpacing,受 line-height 设置影响,如果设置 line-height,lineHeight 等于 line-height。
  • half-leading (半行距): 如果lineHeight > lineSpacing,则lineHeight 与 lineSpacing 之间会产生上下相等的空隙 (lineHeight - lineSpacing)/2 称为半行距(half-leading或 half lead strips)。

字符所占高度的计算

所以在了解了上面的概念以后,就可以解答为什么在 font-size:100px 的时候行高却不一样的问题。

首先,先下载一个专业的字体软件FontForge,这个软件运行在xquartz上,所以要两个都要装。

百度云通道( https://pan.baidu.com/s/1GqI-n39GkFQWsNZ8vWKMGg#list/path=%2F )

安装后我们以 Avenir 字体为例进行分析。

  • EM size 为 1000
  • ascent 为 1000 ,descent 为 366
  • capHeight 为 708
  • xHeight 为 468

注:浏览器使用HHead Ascent/Descent值(Mac)和Win Ascent/Descent值(Windows),并且这些值可能不同。

这意味着 Avenir 字体在 1000 单位的 EM size 中使用了 1000 + 366 个单位,也就是说 font-size:100px,其高度为 100px * (1000 + 366 ) ≈ 137px。 这个计算高度定义了 元素内容(Content area)高度 也就相当于 background 属性。

CSS box models

接下来我们深入的研究一下,CSS box models。你可能不知道什么CSS box models,不过说出来你可能不信,在实际工作当中恐怕你最常见的就是CSS box models。

Block Box/Containing Box (块盒子/包裹盒子)

比如有一段简单的文字,就有可能会有一些列的 box 。那么这个段落被称为 Containing Box ,之所以这么命名,可能是因为他包含了很多 box 吧。(呵…)当然你也可以称之为 Block Box,因为他就是一个块。简单来说Containing Box和Block Box其实是一个东西。

Inline Box (内联盒子)

在段落内部,有很多的 Inline Box。 这些 Box 不会像 Block Box 那样形成新的一⾏。

在上面的例子中, <em/> 标签包裹的 斜体元素 就是一个典型的 Inline Box。

Anonymous Inline Box (匿名内联盒子)

在段落内部,那些没有标记的Inline Box 则成为 Anonymous inline Box。

Line Box (行盒子)

所有Inline Box在Containing Box紧挨着排列,则会形成 Line Box。需要注意的是,Line Box是没办法直观看到的。

Content area

Content area 是围绕⽂文本的隐形框。 而且在字体度量这一小节我们也证明过了,它的高度由font-size决定。

更详细的定义及说明可以访问 CSS 规范2.1 中关于 视觉格式化模型(Visual formatting model)一节进行阅读。

地址:

视觉格式化模型

http://www.ayqy.net/doc/css2-1/visuren.html

9 Visual formatting model

https://www.w3.org/TR/CSS2/visuren.html

Inline Box 与 Line Box

Inline Box 如何影响 Line Box

Line box 的高度由 Line Box 中最⾼的 Inline Box(或Replaced Element)确定。 最⾼的 Inline Box 可以是⼀个 Anonymous Inline Box。

也可能是一个增加了line-height的 Inline Box。 由于增加了 line-height ,所以这个它会比其它的 box 更高。

可能是⼀个更⼤的 font-size 的 Inline Box,这使得这个 Inline Box ⽐其他 Inline Box更高。

由于浏览器器的不同,它也可能受到上标或下标的影响。 因为有些浏览器以影响Line box的方式渲染上标元素。

我们可以通过设置 <sup/>、<sub/> 的 line-height 为 0 来解决这个问题。

Inline Box 可能受到 Replaced Element(如:<img/>、<input/>、<svg/>) 的影响。

Inline Box 撑破 Line Box

正如我们所看到的, Line Box 将增加所有行内 Inline Box 的⾼度。

但是,有时候 Inline Box 的一部分会撑破 Line Box 的顶部或底部。例如一个拥有padding、margin、border 的 Inline Box 。由于 Inline Box 不能设定高度(设了也白设)。因此会在元素的上方和下方显示padding、margin、border,但并不会影响 Line Box。 注意:对于Replaced Element、inline-block行内元素padding、margin、border都会增加高度,所以Line Box 的高度也会受到影响。

浏览器将按照文档的先后顺序呈现 Line Box。 所以, 后续行上的 border 可能会覆盖上⼀行的 border和文本。

参考文章 行内元素垂直方向的layout 深入了解CSS字体度量,行高和vertical-align FontForge 与字体设计 - EM Square Deep dive line-height

原文发布于微信公众号 - 较真的前端(gh_7af41a2be77e)

原文发表时间:2018-04-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葬爱家族

Android高德之旅(8)绘制线废话简单的api总结

绘制线会比绘制点稍微复杂点,抛开一些复杂的属性不谈,主要分为三类:实线、虚线、纹理。绘制线在自定义地图中是非常重要的一个环节。

3455
来自专栏walterlv - 吕毅的博客

Grid 布局算法!自己动手实现一个 Grid

2018-05-20 07:11

922
来自专栏wym

Bomb Catcher 游戏 (Pygame)

991
来自专栏Spring相关

Css权重解析

关于CSS权重,我们需要一套计算公式来去计算,这个就是 CSS Specificity,我们称为CSS 特性或称非凡性,它是一个衡量CSS值优先级的一个标准 具...

1362
来自专栏编程

你不知道的前端算法之热力图的实现

本文作者:TalkingData 可视化工程师李凤禄 编辑:Aresn inMap 是一款基于 canvas 的大数据可视化库,专注于大数据方向点线面的可视化效...

2619
来自专栏Python爬虫与算法进阶

Kaggle入门之预测房价

先给出本次参赛的地址House Prices: Advanced Regression Techniques

1583
来自专栏编程

你不知道的前端算法之热力图的实现

作者:TalkingData 李凤禄 本文为TalkingData原创,未经授权禁止转载。申请授权请在评论中留言联系! inMap 是一款基于 canvas 的...

7768
来自专栏jojo的技术小屋

原 CSS3 filter

作者:汪娇娇 日期:2016.10.9 其实之前几乎都没用过filter属性,就算知道也只是在脑中留了点浅浅的印象,直到最近因为项目的原因,才对filter进行...

2043
来自专栏极客猴

Python 绘图,我只用 Matplotlib(二)

上篇文章,我们了解到 Matplotlib 是一个风格类似 Matlab 的基于 Python 的绘图库。它提供了一整套和matlab相似的命令API,十分适合...

1341
来自专栏前端说吧

CSS3的transition动画功能

3246

扫码关注云+社区

领取腾讯云代金券