本文英文出处:http: //iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align 著作权归作者所有。
转载自https: //www.w3cplus.com/css/css-font-metrics-line-height-and-vertical-align.html

line-heightvertical-align在CSS中是两个简单的属性。如此简单,大多数人都相信自己已经完全理解它们是如何工作的以及如何使用它们。但事实上并不如此。他们其实很复杂,也是CSS中难点之一,而且也是CSS中特性之一:内联格式化上下文(inline formatting context)

比如可以设置line-height带有长度单位的值或一个无单位的值,但其默认值是normal。那么在CSS中normal是什么呢?我们常常认为它是(或者应该是)1或者1.2,甚至也可以说,CSS规范都不清楚是哪一个。我们也知道,没有单位的line-height是相对于font-size的,但问题是,font-size: 100px;在使用不同的字体(font-family)表现的行为是不一样的,所以line-height总是相同或不同的吗?真的是1还是1.2吗?另外vertical-alignline-height的影响又是什么呢?

要深入研究CSS的机制可以说没有这么简单……


首先来聊font-size

首先来看一个简单的HTML代码,一个<p>标签中包含了三个<span>标签,每个<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,但使用不同的font-family,但渲染出来的line-height是不同的:

即使我们意识到这种行为,但还是不清楚为什么font-size:100px时元素的height不是100px?我测量发现:Helvetica字体的高度是115px,Gruppo字体的高度是97px和Catamaran字体的高度是164px

起初似乎有点奇怪,但它是完全可预期的。这主要还是font-family的原因。那就要搞清楚它是如何工作的:

  • 字体定义其em-square,每个字符将会绘制出自己的容器。这个正方形使用相对单位和生成一个1000单位。但它也可以是1024,2048或者其他
  • 根据其荐对单位,字体的度量可以根据一些设置(ascender,descender,capital height,x-height等)来决定。注意,有些值是em-square之外的值
  • 在浏览器中,相对单位是用于缩放用来适应所需的font-size

让我们来看Catamaran字体,并且在FontForge中来看这个字体的度量参数:

  • em-square是1000
  • 上升(ascender)是1100和下降(descender)是540。相同的测试下,浏览器使用HHead Ascent/Descent值(Mac)和Win Ascent/Descent值(Windows),这些值可能不同。我们还需要注意,Capital高度是640和x-height的值是485

这意味着Catamaran字体在1000个单位的em-square使用了1100 + 540个单位,也就是说font-size:100px的时候,其高度是164px。这个计算高度定义了元素内容高度(在这篇文章中其它部分引用这个术语content-area)。你可能想到是内容区域相当于background属性。

我们也可以预测,大写字母是68px高度(680个单位)和小写字母(x-hegiht)是49px高度(485个单位)。因此,1ex = 49px1em = 100px,而不是164px(值得庆幸的是,em是基于font-size计算,而不是height)。

在继续深入之前,先要了解这涉及到什么?当<p>元素呈现在屏幕上,它根据它的宽度可以有很多线。每一行是由一个或多个行内元素(HTML标签元素或匿名内联元素文本内容)组成,专业术语称为行盒(line-box)。line-box的高度是基于它的子元素高度的。浏览器为每个行内元素计算的高度都是line-box(子元素的最高点到最低点)。因此line-box的总高度足以包含所有子元素(默认情况下)

每个HTML元素实际上是一个line-box的堆栈。如果你知道每个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>

它会生成三个line-box:

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

<p>元素(黑色边框)产生了一个line-box(白色边框),其包含了内联元素(实心边框)和匿名内联元素(虚线边框)。

我们清楚的看到,第二个line-box明显比其他的line-box要更高,根据子元素的内容区域(content-area)计算得来,更具体地说,是使用了Catamaran字体。

困难的是line-box创建部分是我们无法看到的,也不是用CSS控制它。即使在::first-line应用了background也无法直接在视觉上看到第个line-box的高度。


line-height问题

直到现在,我们介绍了两个概念:content-arealine-box。如果仔细阅读了前面的内容,你应该知道line-box的高度是根据子元素高度来计算,而且我并没有说是子元素的内容区域(content-area)的高度。这是有很大区别的。

尽管这听起来可能有些奇怪,内联元素有两个不同高度:内容区域(content-area)高度和虚拟区域(virtual-area)高度(这是我发明的术语virtual-area高度,你在规范中是找不到任何相关的内容)。

  • 内容区域高度是由字体来决定的(前面介绍过)
  • 虚拟区域(virtual-area)高度是line-height,它的高度用于计算line-box的高度

行内元素有两个不同的高度。

也就是说,line-height普遍的看法是不同基线(baseline)的距离。在CSS中,它并不是这样。

计算虚拟区域(virtual-area)和内容区域(content-area)高度差称为leading。leading添加在内容区域顶部,另一半添加在内容区域底部。因此,内容区域总是在虚拟区域的中间。

根据其计算值,line-height(virtual-area)相同情况下比content-area更高或更低。对于较小的virtual-area,leading是负值和line-box要比它的子元素更小。

还有其他的内联元素:

  • 替代内联行内元素(<img><input><svg>等)
  • inline-block元素
  • 行内元素参与特定格式化上下文(如,Flexbox元素,和所有的Flex项目)

对于这些特定的行内元素,高度计算基于他们的heightmarginborder属性。如果hegiht的值是auto,然后使用line-heightcontent-area严格上等于line-height

无论如何,我们仍然面临的问题是line-heightnormal值是多小?答案是,其计算content-area高度还是依据于里面的字体来度量。

我们回到FontForge。Catamaran的em-square是1000,但我们看到ascender/descender的值:

  • 生成的Ascent/Descent: ascender是770,descender是230。用于绘制字符(OS/2)
  • 度量的Ascent/Descent: ascender是1100,descender是540。用于内容区域高度(hhea和OS/2)
  • 度量线的间距:通过Ascent/Descent度量使用line-height: normal(hhea)

在我们的示例中,Catamaran字体定义了0个单位的线间距(Line Gap),因此line-height: normal的值将等于内容区域,也就是1640个单位或1.64

作为比较,Arial字体的一个em-square是2048个单位,其ascender是1854,descender是434,线间距是67。这意味着,font-size: 100px的内容区域是112px(1117个单位)和line-height115px1150个单位或1.15)。所有这些度量都是特殊字型,由字体设计师来设置。

显而易见,设置line-height:1是一个非常糟糕的做法。我提醒你,font-size没有单位的观念是相对的,但内容区域不是相对的以及处理虚拟区域小于内容区域有很多问题存在。

但并是只有line-height:1。不论真假,我电脑上安装了1117种字体(是的,我安装了所有的Google Web字体),其中1059种字体,占全部字体的95%左右,计算的line-height大于1。它们计算line-height是从0.6183.378。你得记住,是3.378

line-box计算的小细节:

  • 对于内联元素,paddingborder增加了其background区域,但不会增加内容区域高度(甚至是line-box高度)。因此,你在屏幕上看到的不一定就是内容区域。margin-topmargin-bottom对内联元素不生效。
  • 对于行内替代元素,inline-blockblocksified行内元素,paddingmarginborder都会增加高度,所以内容区域和line-box的高度也会增加

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

前面我没有提到vertical-align属性,即使它是计算line-box高度的一个重要因素。我们甚至可以说,vertical-align属性对于行内格式化上下文中的leading有很大的作用。

vertical-align的默认值是baseline。你注意到度量字体的ascender和descender?这些值是基于baseline,具有一定的比例。那么ascender和descender之间的比例真的是50/50,它可能会产生意想不到的结果,例如所有兄弟元素。

先从这个代码开始:

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

两个<span>元素继承了<p>元素的font-familyfont-size和固定的line-height。基线将会匹配以入line-box的高度等于他们的line-height

如果第二个元素设置更小的font-size呢?

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

这听起来很奇怪,但默认基线对齐可能导致更高的line-box,如下图所示。我提醒你,line-box的高度是从它的子元素最高点和最低点计算。

有一个观点可以得到支持,那就是line-height设置不带任何单位的值,但有时你需要做一个完美的Vertical-rhythm。说实话,不管你选择什么,你总是会有困难的。

看看另一个例子。<p>元素的line-height值设置了200px,并且包含了一个<span>元素,这个<span>元素继承了<p>元素的line-height

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

line-box有多高?我们期望的是200px,但如果不是,我们得到的又是什么?这里不同的是<p>元素有自己的字体(默认是serif)。<p><span>之间的基线可能是不同的,因此line-box的高度是高于预期的。这是因为浏览器给每个line-box计算都是开始于一个任意字符。规范中称之为strut。

一个看不见的角色,但的确是会有可见的影响。

就我自己一些经历,我们将面临同样的问题,那就是兄弟元素。

基线对齐是完了,但vertical-align:middle可以拯救它们?可以阅读规范:

Middle “aligns the vertical midpoint of the box with the baseline of the parent box plus half the x-height of the parent”.

基线的比例不同,以及x-height比例,所以中间对齐是不可靠。最坏的情况下,在大多数的情况下,中间就从来没有真的中间过。这里面有太多的因素参与其中,使用CSS是不能设置这些因素的(x-height,ascender和descender比例等)。

它有四个值,这可能在某些情况下是有用的:

  • vertical-align: top | bottom和line-box的顶部或底部对齐
  • vertical-align: text-top | text-bottom和内容区域的顶部或底部对齐

注意了,在所有情况下它都是在虚拟区域中,所以是看不见的高度。看看这个简单的示例,使用vertical-align:top,看不见的line-height可能产生一些很奇怪的结果。

最后,vertical-align还能接受数值,提高或降低盒子的基线。最后一个选项可以派上用场。


CSS 无所不能

我们已经讨论过了line-heightvertical-align在一起是如何工作,但现在的问题是如何使用CSS来控制字度的度量指标?简短的回答:没有。即使真的如此,我也想我们应该可以做些什么?那么有关于字体度量,我们应该能够做些什么?

例如,如果我们想要给文本使用Catamaran字体,可以把其capital高度扩展到100px?通过一些数学计算,似乎可行。

首先设置度量字体的五个自定义属性,然后计算font-size,从而得到capital高度是100

p {/* font metrics */--font: Catamaran;--capitalHeight: 0.68;--descender: 0.54;--ascender: 1.1;--linegap: 0;/* desired font-size for capital height */--fontSize: 100;/* apply font-family */font-family: var(--font);/* compute font-size to get capital height equal desired font-size */--computedFontSize: (var(--fontSize) / var(--capitalHeight));font-size: calc(var(--computedFontSize) * 1px);
}

很简单,不是吗?但如果我们想要让文本在可视区居中,让剩余的空间均分在”B”字的顶部和底部,应该怎么做呢?为了达到这一目的,我们必须基于ascender和descender比例计算出vertical-align

首先,计算line-height:normal和内容区域的高度。

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

这时,我们需要:

  • 大写字每底部距离底部边缘的距离
  • 大写字母顶部距离顶部边缘的距离
    像这样:
p {…--distanceBottom: (var(--descender));--distanceTop: (var(--ascender) - var(--capitalHeight));
}

我们现在可以通过距离乘以font-size计算出vertical-align

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

最后,我们设定所需的line-height和计算它,保持一个垂直对齐:

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

添加一个图标和字母”B”垂直对齐,现在很容易就能做到:

span::before {content: '';display: inline-block;width: calc(1px * var(--fontSize));height: calc(1px * var(--fontSize));margin-right: 10px;background: url('https://cdn.pbrd.co/images/yBAKn5bbv.png');background-size: cover;
}

示例的地址可以点击这里。

注意:这个测试只是出于演示目的。你不能依赖于此。如果字体不加载,备用字全有可能具有不同的字体度量参数,它就没法正常工作了。

在一部分示例中,大家看到很有以–开头的,这是CSS的原始变量,也称之为CSS自定义属性。


总结

这篇文章我们学到了什么:

  • 行内格式化上下文真的很难理解
  • 所有行内元素都有两个高度
  • 内容区域(content-area)基于字体的度量参数
  • 虚拟区域(virtual-area)就是line-height
  • 这两个高度是无法可视的(如果你通过开发者工具,你可以看到)
  • line-height:normal是基于字体度量参数
  • line-height: n有可能创建一个虚拟区域比内容区域更小
  • vertical-align不是很可靠
  • 一个line-box的高度计算是基于它的子元素的line-heightvertical-align属性
  • 我们没有办法直接通过CSS来获取或设置字体的度量参数
  • 未来可能会有一个垂直对齐的规范来解决这些看似问题的问题:Line Grid Module

相关资源

  • 获取字体度量参数的工具:FontForge,opentype.js
  • 在浏览器中计算line-height:normal和一些比例
  • Ahem,一个特殊字体,帮助我们如何理解它怎么工作
  • 一个更深,更透彻阐述行内格式化上下文
  • 两份详细介绍line-height的PPT:CSS line-height和Deep dive line-height

博客名称:王乐平博客

CSDN博客地址:http://blog.csdn.net/lecepin

深入了解CSS字体度量,行高和vertical-align相关推荐

  1. 【CSS】字体、行高、文本对齐

    一.字体 字体相关的样式 color font-size font-size相关单位 em相对于当前元素的font-size rem相对于根元素的font-size font-face可以直接将服务器 ...

  2. 字体、图标字体、行高、文本样式(css)

    字体.图标字体.行高.文本样式(css) 字体相关的样式 color 用来设置字体颜色 font-size (字体大小) 相关单位 em 相当于当前元素的一个font-size rem 相当于根元素的 ...

  3. 关于css中line-height(行高)设置无效的问题

    关于css中line-height(行高)设置无效的问题 我们先写下这一串代码: <!DOCTYPE html> <html lang="en"> < ...

  4. CSS字体、行高等其他样式

    CSS字体.行高等其他样式 一.长度单位与颜色单位 1. 长度单位 <!DOCTYPE html> <html lang="en"><head> ...

  5. css设置1.5倍行高,CSS怎么控制行高?

    CSS怎么控制行高? css中,调整每行文字字体间距(行距)是使用line-height属性. ● line-height 属性设置行间的距离(行高). 注:不允许使用负值. 要实现上下换行文字行间距 ...

  6. html行高设置失效,科技常识:关于css中line-height(行高)设置无效的问题的解决方法...

    今天小编跟大家讲解下有关关于css中line-height(行高)设置无效的问题的解决方法 ,相信小伙伴们对这个话题应该有所关注吧,小编也收集到了有关关于css中line-height(行高)设置无效 ...

  7. html行高设置失效,关于css中line-height(行高)设置无效的问题的解决方法

    关于css中line-height(行高)设置无效的问题 我们先写下这一串代码: Document .head{ height: 100px; text-align: center; line-hei ...

  8. HTML5自动换行的间距设置,div css p段落行高行距怎么设置篇

    如何使用div+css样式设置p段落之间的上下间距,需要设置哪些样式来控制 之间行距距离呢?css行高怎么写等问题,让大家彻底掌握div css p行距行高样式. DIVCSS5为大家介绍如何通过CS ...

  9. POI导出excel--设置字体,颜色,行高自适应,列宽自适应,锁住单元格,合并单元格

    1. 前言 poi框架可以支持我们在java代码中, 将数据导出成excel , 在上一篇 如何使用POI导出excel表格,以及处理浏览器无法识别下载文件的问题 中已经详细的进行了介绍 , 但是实际 ...

  10. 7. 前端基础--CSS背景和行高

    文章目录 CSS背景和行高 1. CSS 背景(background) 1.1 背景颜色(color) 1.2 背景图片(image) 1.3 背景平铺(repeat) 1.4 背景位置(positi ...

最新文章

  1. linux 为开发板添加板级文件config.h
  2. css兼容ie9 ie11,用webpack打包的css在ie9些有些css文件没有引进去是为什么 ie11可以...
  3. 定义定价用途的条件类型(Condition Types)
  4. Windows新终端中玩转ASCII和Emoji游戏的正确姿势
  5. C++工作笔记-stl中map基础用法(插入,遍历,删除)
  6. iOS7应用开发1、菜鸟那点儿事儿
  7. Django之中间件,csrf跨站伪造请求,auth认证模块
  8. webstorm下载微信小程序插件_微信电脑版可以打开小程序喽 前提你得下载测试版...
  9. python复制函数_Python numpy.copy函数方法的使用
  10. java中spring的注解_Java代码中spring注解浅析
  11. python学习笔记11 百度识图
  12. java实现图片压缩
  13. 在QQ远程协助为什么键盘无法输入?一起来学习!
  14. 距离度量与相似性度量
  15. 06蚂蚁-高性能Nginx服务器——1.反向代理
  16. 《紫川》之帝都风云 第二卷
  17. 华为浏览器html页样式没了,返回键没有了怎么办 华为手机没有返回键了怎么处理...
  18. LeetCode 力扣 56. 合并区间
  19. 英国金融监管机构加大力度审查违规加密货币公司
  20. 腾讯,互联网创业者的噩梦!

热门文章

  1. MySQL数据库和ACID模型
  2. mysql重置root密码方法
  3. 新浪微博iOS版SDK“宝玉XP”框架学习笔记
  4. IT宅男们,有没有什么技术让你觉得相见恨晚?
  5. 百安俱乐部关于“BotNet专题讨论”资料
  6. 一个封装了的选项卡效果js
  7. 数据中台是下一代大数据_全栈数据科学:下一代数据科学家群体
  8. 测试无服务器应用程序的最佳方法
  9. vs2005 新建项目一片空白
  10. Oracle中exists与in的效率探讨