line-height vertical-align

原文出处:http://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align

  • 无授权,基本上是翻译原文,加上自己的理解和注释,当做一个笔记。
    作者说:这两个属性也许没有看上去那么简单,甚至是最难的之一。原因在于:内联格式化上下文(inline formating context)。
    因为我们对这两个属性知道的太少了,比如line-height:normal,设置行高为正常,但是什么是正常呢?也许是1,也许是1.2,在字体这么多设置中,它们的行为总是一样吗?而且它们有什么影响吗?

之后举了一个font-size都是一样的三个一样标签的内联元素,字体不同,发现不一样高,而且有一个高出了64px。
font是根源所在:
1.一个字体(font)定义它的em-square(UPM ,unit per em),类似于容器,每个字符都会被绘制。这个方形的容器使用相对单位,一般设置为1000,但是它也可以是1024或者任何其他。
2.根据相对单位,可以设置上升下降(ascender,descender,capital height,x-height等等)。一些值也有可能泄露在容器外面。
3.在浏览器中,相对单位被缩放以适合期望的字体大小。

这里作者用了一个软件检测字体的详细指标。(https://fontforge.github.io/en-US/
上升+下降的值=字体em-square大小。
Mac OS上相对应的是HHead Ascent / Descent值,在Windows上使用Win Ascent / Descent值,值也许会有不同。
比如一个ascent(上升)是1100,descent(下降)是540,em-square是1640,设置时的高度就是164px,这个计算的高度定义了一个元素的内容区域。内容区域视为属性应用的区域。

  • 1ex是小写字母,也就是对应的x-height,所以它是一个会变的数值,而不是像em基于font-size,而不是计算高度。
在原图基础上加了属性方便查看

可以看到,它可以根据其宽度由许多线组成。每行由一个或多个内联元素(HTML标签或文本内容的匿名内联元素)组成,称为行框(line-box),一个line-box是基于其子女的高度。
因此,浏览器计算每个内嵌元素的高度,并且因此定下line-box的高度(从其子的最高点到其子的最低点)。因此,行框总是足够高以包含其所有子项(默认情况下)。
其实知道每个line-box的高度,就知道一个元素的高度。

但是实际是看不到line-box的,也不能用CSS控制它,设置个背景也没有多大用处。

<b>line-height:to the problems and beyond</b>

一个line-box的高度由孩子的height计算,没有说它孩子content-area的高度。这产生了很大的区别。

即使它可能听起来很奇怪,一个内联元素有两个不同的高度:内容区域高度(content-area)和虚拟区域(virtual-area )高度(作者发明了术语虚拟区域O__O "…)。
1.在内容区高度(content-area)由字体规格(font metrics)定义(如前所示)
2.在虚拟区域的高度是line-height,它的高度用于计算line-box的高度(it is the height used to compute the line-box’s height)

计算出的虚拟区域和内容区域之间的高度差称为leading。一半leading被添加在内容区域的顶部,另一半被添加在底部。因此,内容区域总是在虚拟区域的中间。
基于其计算值,line-height(虚拟区域)可以等于,高于或小于内容区域。在较小的虚拟区域的情况下,leading是负的,并且line-box在视觉上小于其孩子。
还有其他种类的内联元素:

取代内联元素(<图片>,<input>,<svg>等)
inline-block和所有inline-*元素
参与特定格式化上下文的内联元素(例如,在flexbox元素中,所有flex项目都被块化)
对于这些特定的内联元素,高度的计算基于他们的height,margin和border属性。如果height是auto,则line-height使用并且内容区域严格等于line-height。

上图!同样是原作者的图 ![Uploading line-height-yes-no_400235.png . . .]

之后是讨论了两种字体的line-height:1,发现内容区域不一样大,
<b>很明显,设置line-height: 1是一个坏的做法。</b>
无单位的值是font-size相对的,而不是内容区域相对的,并且处理小于内容区域的虚拟区域是我们的许多问题的起源。
并且作者发现在1117中字体中1059字体,大约95%,有一个line-height计算大于1.他们的计算从0.618到3.378。

<b>vertical-align:one property to rule them all</b>

奇怪的是,默认的基线对齐可能会导致更高的(!)行框,一个行框的高度是从它的孩子的最高点到它的孩子的最低点计算的。

继承的font-family可能不是想象中的大小,因为每个标签的基准(baseline)很可能是不同的,所描述的line-box高于预期。因为浏览器计算每个line-box都是以0宽度开始的,称为strut。(This happens because browsers do their computation as if each line-box starts with a zero-width character , that the spec called a strut.)

规范是这么写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比也不同,使用align也不可靠。middle不是真的在中间。

vertical-align: top / bottom align to the top or the bottom of the line-box

vertical-align: text-top / text-bottom align to the top or the bottom of the content-area

依旧是原博客图

注意,在所有情况下,它对齐虚拟区域,因此是不可见的高度。看看这个使用vertical-align: topline-height的例子。看不见可能产生奇怪,但不令人惊讶的结果。

我说怎么对不齐呢

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

<b>CSS is awesome</b>

我们已经讨论过如何让line-height和vertical-align一起工作,但现在的问题是:字体指标可以用CSS控制吗?
简答:不。
字体指标是不变的,所以我们应该能够做一些事情。

例如,如果我们想要一个使用 Catamaran的文本,其中captical-height正好是100px高?
看起来可行:让我们做一些数学。

首先,我们将所有字体指标设置为CSS自定义属性,然后计算font-size获得100px的captical-height。

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”字母的顶部和底部?为了实现这一点,我们必须vertical-align根据上升/下降比率来计算。
首先,计算和内容区域的高度:line-height: normal

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

然后需要:

the distance from the bottom of the capital letter to the bottom edge
the distance from the top of the capital letter to the top edge

像这样:

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

我们现在可以计算vertical-align,这是乘以计算的距离之间的差异font-size( which is the difference between the distances multiplied by the computed font-size这句啥意思??)。(我们必须将此值应用于内联子元素)

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>Takeaways</b>

我们学到了什么:

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

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

  • 内容区域content-area(根据字体规格)

  • 虚拟区域virtical-area(line-height)

  • 这2个高度中没有一个可以可视化,毫无疑问。
    (如果你是一个devtools开发人员,想要工作,这可能是真棒)

  • line-height: normal 是基于字体度量

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

  • vertical-align 不是很可靠

  • 一个line-box的高度是根据它的孩子们的line-height和vertical-align
    属性来计算

  • 我们不能很容易地获取/设置CSS的字体指标

  • 有一个相关的未来规范来帮助垂直对齐:线网格模块

但我还是喜欢CSS :)

后记:没想到花了好几个小时,很佩服原作者这种默默的奉献,他一定花了比我更多的时间去写,去做图让读者更好理解。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,727评论 1 92
  • 有些东西我们经常用,但是我们却并不了解它的原理,所以一旦换了场景,好多东西就不知道该怎么用了。最近一直很纠结ver...
    朱小维阅读 4,909评论 8 34
  • 参考文章:深入了解CSS的line-height属性Vertical-Align: 你需要知道的所有事【译】Ver...
    若邪Y阅读 3,422评论 1 6
  • 前言 总括: 本文通过实例讲解CSS中最大的难点之一,行内元素的布局,主要是挖掘line-height和verti...
    秦至阅读 1,904评论 0 1
  • 人生若只如初见 何事秋风悲画扇 等闲变却故人心 却道故人心易变 骊山语罢清霄半 泪雨零铃终不怨 如何薄幸锦衣郎 比...
    CXY_CXY阅读 90评论 0 1