原文出處:http://iamvdo.me/en/blog/css-font-metrics-line-height-and-vertical-align
- 無授權(quán),基本上是翻譯原文,加上自己的理解和注釋赘风,當(dāng)做一個筆記拘央。
作者說:這兩個屬性也許沒有看上去那么簡單,甚至是最難的之一。原因在于:內(nèi)聯(lián)格式化上下文(inline formating context)。
因為我們對這兩個屬性知道的太少了,比如line-height:normal审胚,設(shè)置行高為正常,但是什么是正常呢礼旅?也許是1膳叨,也許是1.2,在字體這么多設(shè)置中痘系,它們的行為總是一樣嗎菲嘴?而且它們有什么影響嗎?
之后舉了一個font-size都是一樣的三個一樣標(biāo)簽的內(nèi)聯(lián)元素汰翠,字體不同龄坪,發(fā)現(xiàn)不一樣高,而且有一個高出了64px复唤。
font是根源所在:
1.一個字體(font)定義它的em-square(UPM ,unit per em)健田,類似于容器,每個字符都會被繪制佛纫。這個方形的容器使用相對單位妓局,一般設(shè)置為1000,但是它也可以是1024或者任何其他雳旅。
2.根據(jù)相對單位跟磨,可以設(shè)置上升下降(ascender,descender,capital height,x-height等等)间聊。一些值也有可能泄露在容器外面攒盈。
3.在瀏覽器中,相對單位被縮放以適合期望的字體大小哎榴。
這里作者用了一個軟件檢測字體的詳細指標(biāo)型豁。(https://fontforge.github.io/en-US/)
上升+下降的值=字體em-square大小。
Mac OS上相對應(yīng)的是HHead Ascent / Descent值尚蝌,在Windows上使用Win Ascent / Descent值迎变,值也許會有不同。
比如一個ascent(上升)是1100飘言,descent(下降)是540衣形,em-square是1640,設(shè)置時的高度就是164px,這個計算的高度定義了一個元素的內(nèi)容區(qū)域谆吴。內(nèi)容區(qū)域視為屬性應(yīng)用的區(qū)域倒源。
- 1ex是小寫字母,也就是對應(yīng)的x-height句狼,所以它是一個會變的數(shù)值笋熬,而不是像em基于font-size,而不是計算高度腻菇。
可以看到胳螟,它可以根據(jù)其寬度由許多線組成。每行由一個或多個內(nèi)聯(lián)元素(HTML標(biāo)簽或文本內(nèi)容的匿名內(nèi)聯(lián)元素)組成筹吐,稱為行框(line-box)糖耸,一個line-box是基于其子女的高度。
因此丘薛,瀏覽器計算每個內(nèi)嵌元素的高度蔬捷,并且因此定下line-box的高度(從其子的最高點到其子的最低點)。因此榔袋,行框總是足夠高以包含其所有子項(默認情況下)周拐。
其實知道每個line-box的高度,就知道一個元素的高度凰兑。
但是實際是看不到line-box的妥粟,也不能用CSS控制它,設(shè)置個背景也沒有多大用處吏够。
<b>line-height:to the problems and beyond</b>
一個line-box的高度由孩子的height計算勾给,沒有說它孩子content-area的高度。這產(chǎn)生了很大的區(qū)別锅知。
即使它可能聽起來很奇怪播急,一個內(nèi)聯(lián)元素有兩個不同的高度:內(nèi)容區(qū)域高度(content-area)和虛擬區(qū)域(virtual-area )高度(作者發(fā)明了術(shù)語虛擬區(qū)域O__O "…)。
1.在內(nèi)容區(qū)高度(content-area)由字體規(guī)格(font metrics)定義(如前所示)
2.在虛擬區(qū)域的高度是line-height售睹,它的高度用于計算line-box的高度(it is the height used to compute the line-box’s height)
計算出的虛擬區(qū)域和內(nèi)容區(qū)域之間的高度差稱為leading桩警。一半leading被添加在內(nèi)容區(qū)域的頂部,另一半被添加在底部昌妹。因此捶枢,內(nèi)容區(qū)域總是在虛擬區(qū)域的中間。
基于其計算值飞崖,line-height(虛擬區(qū)域)可以等于烂叔,高于或小于內(nèi)容區(qū)域。在較小的虛擬區(qū)域的情況下固歪,leading是負的蒜鸡,并且line-box在視覺上小于其孩子。
還有其他種類的內(nèi)聯(lián)元素:
取代內(nèi)聯(lián)元素(<圖片>,<input>逢防,<svg>等)
inline-block和所有inline-*元素
參與特定格式化上下文的內(nèi)聯(lián)元素(例如康聂,在flexbox元素中,所有flex項目都被塊化)
對于這些特定的內(nèi)聯(lián)元素胞四,高度的計算基于他們的height恬汁,margin和border屬性。如果height是auto辜伟,則line-height使用并且內(nèi)容區(qū)域嚴格等于line-height氓侧。
之后是討論了兩種字體的line-height:1导狡,發(fā)現(xiàn)內(nèi)容區(qū)域不一樣大约巷,
<b>很明顯,設(shè)置line-height: 1是一個壞的做法旱捧。</b>
無單位的值是font-size相對的独郎,而不是內(nèi)容區(qū)域相對的,并且處理小于內(nèi)容區(qū)域的虛擬區(qū)域是我們的許多問題的起源枚赡。
并且作者發(fā)現(xiàn)在1117中字體中1059字體氓癌,大約95%,有一個line-height計算大于1.他們的計算從0.618到3.378贫橙。
<b>vertical-align:one property to rule them all</b>
奇怪的是贪婉,默認的基線對齊可能會導(dǎo)致更高的(!)行框卢肃,一個行框的高度是從它的孩子的最高點到它的孩子的最低點計算的疲迂。
繼承的font-family可能不是想象中的大小,因為每個標(biāo)簽的基準(zhǔn)(baseline)很可能是不同的莫湘,所描述的line-box高于預(yù)期尤蒿。因為瀏覽器計算每個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.)
規(guī)范是這么寫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
注意匕争,在所有情況下,它對齊虛擬區(qū)域爷耀,因此是不可見的高度甘桑。看看這個使用vertical-align: topline-height的例子∨芎迹看不見可能產(chǎn)生奇怪铆帽,但不令人驚訝的結(jié)果。
最后德谅,vertical-align還接受提高或降低關(guān)于基線的數(shù)值爹橱。最后一個選項可能派上用場。
<b>CSS is awesome</b>
我們已經(jīng)討論過如何讓line-height和vertical-align一起工作窄做,但現(xiàn)在的問題是:字體指標(biāo)可以用CSS控制嗎愧驱?
簡答:不。
字體指標(biāo)是不變的椭盏,所以我們應(yīng)該能夠做一些事情组砚。
例如,如果我們想要一個使用 Catamaran的文本掏颊,其中captical-height正好是100px高糟红?
看起來可行:讓我們做一些數(shù)學(xué)。
首先乌叶,我們將所有字體指標(biāo)設(shè)置為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”字母的頂部和底部句旱?為了實現(xiàn)這一點,我們必須vertical-align根據(jù)上升/下降比率來計算晰奖。
首先谈撒,計算和內(nèi)容區(qū)域的高度: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));
}
我們現(xiàn)在可以計算vertical-align,這是乘以計算的距離之間的差異font-size( which is the difference between the distances multiplied by the computed font-size這句啥意思匾南?啃匿?)。(我們必須將此值應(yīng)用于內(nèi)聯(lián)子元素)
p {
…
--valign: ((var(--distanceBottom) - var(--distanceTop)) * var(--computedFontSize));
}
span {
vertical-align: calc(var(--valign) * -1px);
}
最后蛆楞,我們設(shè)置所需的line-height并計算它溯乒,同時保持垂直對齊:
p {
…
/* desired line-height */
--lineheight: 3;
line-height: calc(((var(--lineheight) * var(--fontSize)) - var(--valign)) * 1px);
}
結(jié)果就是特別完美的居中。圖就是傳不上來……
注意豹爹,此測試僅用于演示目的裆悄。
你不能依賴這個。
很多原因:
除非字體指標(biāo)是不變的臂聋,瀏覽器中的計算不是光稼。
如果字體未加載或南,則后備字體可能具有不同的字體度量,并且處理多個值將很快變得非常難以管理艾君。
<b>Takeaways</b>
我們學(xué)到了什么:
內(nèi)聯(lián)格式化上下文真的很難理解
所有內(nèi)聯(lián)元素都有2個高度:
在內(nèi)容區(qū)域content-area(根據(jù)字體規(guī)格)
在虛擬區(qū)域virtical-area(line-height)
這2個高度中沒有一個可以可視化采够,毫無疑問。
(如果你是一個devtools開發(fā)人員冰垄,想要工作蹬癌,這可能是真棒)line-height: normal 是基于字體度量
line-height: n可以創(chuàng)建小于內(nèi)容區(qū)域的虛擬區(qū)域
vertical-align 不是很可靠
一個line-box的高度是根據(jù)它的孩子們的line-height和vertical-align
屬性來計算我們不能很容易地獲取/設(shè)置CSS的字體指標(biāo)
有一個相關(guān)的未來規(guī)范來幫助垂直對齊:線網(wǎng)格模塊
但我還是喜歡CSS :)
后記:沒想到花了好幾個小時,很佩服原作者這種默默的奉獻虹茶,他一定花了比我更多的時間去寫逝薪,去做圖讓讀者更好理解。