最近在優化公司內部UI組件時,遇到了一個問題:
咱們的字體圖標在跟文字放在一塊兒時,若是不寫專門的樣式,看起來「沒有對齊」,如圖: css
vertical-align: middle
:
vertical-align: middle
:
IFC是什麼?
在理解浮動以及清理浮動的原理時,相信不少人都接觸過BFC(Block Formatting Contexts),定義了在「普通流」(Normal Flow)中,塊級盒子(Block Box)組成上下文中的表現特性。
而IFC,即Inline Formatting Context,顧名思義,定義了「行內盒子」(Inline Box)組成上下文中的表現特性,完整定義可見CSS標準文檔。
根據IFC的描述,行內盒子(Inline Box)由「行內級元素」(Inline Level Elements)和「文本」(Contents)所組成,而行內盒子(Inline Box)水平排列所構成的一行矩形區域,叫作「行盒子」(Line Box)。
對應實際場景的話,字體圖標是一個display: inline-box
的行內級元素,而「中文」這兩個字是另一個行內級元素,把這兩個元素造成的兩個行內盒子放在一塊兒,構成了一個行盒子。
除了display: inline-box
的元素和文本是行內級元素外,display屬性爲「inline」,「inline-table」的元素以及像「img」、「input」、「video」等「替換元素」(Replaced Elements)都是行內級元素,但他們在行盒子中的高度計算方式不太相同。
替換元素的高度,等於它們自己的高度(含margin),而非替換元素的高度,則稍微有點複雜。
這裏,就要引出咱們故事的主角:字體度量(Font Metrics)。html
簡單來講,字體度量(Font Metrics)就是字體中的一系列參數,這些參數對於CSS來講是不可見的,因此咱們須要藉助一些工具來查看,好比FontForge。
拿中文字體經常使用的「微軟雅黑」爲例,能夠看到這些參數: git
上面一節講到了,字體實際渲染的高度與所設定的字體大小不一致。那麼,對應到瀏覽器中,這個高度是什麼高度呢?
想必,你已經猜到了,這個就是默認的行高,也就是當line-height: normal
時的字體行高: github
line-height: normal
大體等於
line-height: 1.32
。
line-height: normal
的值時,行間距(Leading)爲正值;當行高的值小於
line-height: normal
的值時,行間距(Leading)爲負值。
line-height
設置太小,不然行與行之間的文本可能會出現重疊。若是咱們給部分的字體設置了背景色,因爲背景色是覆蓋文本的實際高度的,這樣會形成視覺效果更糟糕:
對於vertical-align
這個css屬性,我曾經對它的實際做用苦惱了好久,好比:middle
相對什麼對齊?top
和text-top
又有什麼區別?baseline
究竟在哪裏等等。
那咱們就結合官方定義,來一個個看一下:面試
官方描述是,將行內盒子的baseline與其父級盒子的baseline對齊;若是行內盒子沒有baseline,就將它底部的margin邊界與父級盒子baseline對齊。
從前面的章節能夠看出,對於文原本說,它的baseline的高度,其實就是字體度量中Descent的高度。而對於替換元素,就如後半句的定義,以底部margin的邊界來對齊。
那麼問題來了,父級盒子的baseline在哪裏呢?
官方解釋說,每一個行盒子裏,最開始會有一個不可見的、零寬度的行內盒子,官方稱它叫「strut」。這個strut,能夠當作一個普通的文本,字體爲父級盒子所設置的font-family
屬性。因此,vertical-align
其實就是與這個隱藏的文本strut的對應參考線對齊。api
vertical-align: middle
是你們在處理垂直居中時,最經常使用到的屬性。然而,它有時候會出現不少怪異的場景,好比父級元素高度增長了。
仍是先來看下官方定義:將行內盒子垂直方向的中點與父級盒子的baseline以上的小寫字母x的一半的高度對齊。
簡單來講,就是參照小寫字母x的一半的那條線,將垂直高度的一半與之對齊。
由此能夠想象,設置了vertical-align: middle
的行內盒子的相對位置會降低,在高度不變的基礎上,父級盒子便會高出它降低的這段高度。
根據定義,能夠很簡單的算出來:(XHeight / 2 + Descent) - [(Ascent + Descent) / 2 - Descent],即 [(1106 / 2 + 536) - (2703 / 2 - 536)] * (100 / 2048) ≈ 13px。 瀏覽器
vertical-align: middle
後,未必從視覺上看,是必定對齊的。這也是文章最開始中,圖標跟字體看起來,尚未徹底對齊的緣由之一。
關於top
、text-top
、bottom
、text-bottom
等的區別,這裏就不一一展開了,具體能夠參考官方定義文檔,或者參考下圖: 框架
好了,繞了那麼大一圈,如今咱們回過來分析一下,最開始字體圖標對齊的問題。
字體圖標跟普通字體同樣,一樣能夠用FontForge來查看: ide
vertical-align: middle
以後,根據定義,字體圖標會和小寫字母x對齊:
vertical-align: middle
以後,因爲兩遍同時以本身的垂直中線與同一條線對齊,視覺上相對就對齊了。
若是眼見夠尖會發現,其實此時的字體圖標仍是略微偏上了。
仔細檢查一番,發現這個字體圖標並無撐滿其內容區域: wordpress
這樣彷佛還有問題:
其一,前面也提到過,設置vertical-align: middle
,可能帶來父級元素高度增長的反作用;
其二,每次使用時,都必須對字體圖標和文本添加vertical-align: middle
,十分繁瑣;
其三,對於多段文本中穿插字體圖標的狀況,這一解決方案就不適用了。 在理想狀態下,默認對齊時,字體圖標與文本視覺上就應該是對齊的。
根據前文的分析,若是想要視覺上儘量對齊,對字體圖標設置一個合適的Descent是關鍵。
若是針對英文環境,這個相對容易一些:由於大寫英文字母,所有在baseline之上,且高度相對一致。部分小寫字母,會下探到baseline如下,如g、y,但總的字體高度也大體相同。參考字體生成平臺icomoon,其默認的baseline高度爲6.28%em,也就是對於1024個單位的字體,baseline設置爲64個單位。
IFC相關知識一直以來都是css中的一大難點,在面試中也常常會涉及。
可能有些狀況,嘗試了幾下就解決了,好比本人一開始的作法,對字體圖標和文本同時添加vertical-align: middle
,但若是沒有完全弄清其中的原理的話,每每採用的並非最佳方式,甚至還會遇到一些「奇怪」的問題,好比高度增長。
但願經過本文,能幫助你們理解IFC,同時下面也列出了一些很好的參考文章供你們參考。本人在寫做中,也藉助這些文章,進一步加深並鞏固了相關知識。
Deep dive CSS: font metrics, line-height and vertical-align
css行高line-height的一些深刻理解及應用