最近在优化公司内部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的一些深刻理解及应用