on
All I want is 对齐
在项目中要某些元素对齐简直直是不能再常见的需求。
而文本的对齐就是对齐大军中的一支先锋部队了。说实话,我到现在都有点害怕文本对齐🤦♂️。
那么,这篇blog也就呼之欲出了,今天我们就来一起学习一下文本相关的对齐。
本文注重的是不同font-size的字体对齐
说在前
有的CSS属性看起来是最基本最简单的,但是其中真的蕴藏玄机。有的时候真的觉得CSS好难啊😭。

感觉自己一直没有很系统的去学习一遍CSS,总是停留在某些单个的属性上会让知识变得相当的分散。无法形成体系也就意味着对整体没有概念无法通其前因后果,方难得始终啊。
所以,类似于BFC & IFC这样的核心知识当然是要被提上日程。本期就先不展开讨论了~(不,我不会鸽的,不知鸽为何物)
先说说font-size
至于IFC & ,我们当然要留到下期展开讲了
不知道大家在开发过程中有没有观察过界面里的字体。在框选它们的时候,款选的文本上下都会出现一部分的空白

接下来看看不同的字体。
相同的font-size在框选它们的时候却拥有着不一样的高度

这就非常迷惑了,大家都是font-size: 100px,凭啥有的高有的低呢?
想要搞起出这个问题,你必须要搞清楚字体设计和"制造"的过程。
字体是如何“制造”出来的?
当需要设计一款字体的时候,我们肯定是针对每个字(汉字是一个字,英文则是每个字母)来逐一设计的。
每款字体会定义一个属于自己的em-square。可以理解为一个容器,用来盛放单个字母或者汉字,有点像我们古代的活字印刷。每个字符的高度是统一的,这样每个字模可以整齐地放进行和块中。

em-square
em-square这个名字的由来,后半部分就不用说了,因为是正方形的容器。
em则是源于大写字母M的宽度。em size是根据字模计算出的点值,所以点值为10 points的字体em也等于10 points。
em大小通常是1000 units。在TrueType字体中,UPM约定是2的幂,通常是1024或2048。
在真正的使用这些字体时,是会根据使用的情况进行缩放。
假如一个M高度是700units,那么在一个font-size: 10em的字体中M的高度将是7em。

为什么要说这个呢?
因为我们还没有解开为什么这些字体实际的高度会比我们看到的字母本身的高度上下都多出来一截这个问题。
接下来要引入一个设计字体的工具FontForge
FontForage告诉你为什么,字体实际高度比看起来要高
由于我在学习的博客中大家都提到了
FontForge这个字体设计程序,所以接下来的截图均源于FontForge

接下来看这张图

其实我们会发现,实际高度和字母本身的高度相差的地方就是图中?所指出的地方。(ig.emo直呼内行并发了个?
其实在设计字体的时候,字体的一些属性就已经定死了。这是字体内部的属性,是无法通过css来改变的。

这是一张字体相关的设置图,就是设置一些字体相关的数值,每个字段所代表的含义可以看下面这张图更直观。

这里首先需要指出一点
通过测试发现,macOS 上的浏览器使用了 HHead Ascent 和 HHead Descent 值,Windows 上的浏览器使用了 Win Ascent 和 Win Descent(而且两个平台上的值不一样)。
然后我们发现,?部分其实就是
- ?上 = Win/HHead Ascent - Type Ascent
- ?下 = Win/HHead Descent - Type Descent
而这些值都是在设计字体的时候就定好的。
我们再来计算一次,如图所示

我们假设我们给定font-size: 100px;
这里要注意font-size是相对于em-square的,而不是字母本身

所以当我们设定了font-size: 100px;时,实际的高度竟然有164px(特定的某种font-family)

所以这里也解释了为什么不同的字体高度都不一样。因为设计时就已经配置了不同的Win/HHead Ascent & Win/HHead Descent & other setting...
插曲baseline
emmmm,没有错,baseline其实就是指字母本身的最下侧,在上图标出来了。
那么line-height呢?
说到line-height就要引出两个概念,分别是content-area&line-box。
content-area就是我们刚才算了半天的100px -> 164px的那个164px的实际高度,对就是它。它(164px)决定了content-area的高。line-box则是由它所有子元素的高度计算得出的,换句话说就是每个line-box一定能放得下自己所有的子元素(尤其是高度)。
这个时候就要请出line-height这个属性了。

可以看到如果我们设定的line-height > 计算出来的content-area那么,line-box为了能够放得下这个内容,自然应该跟随line-height的高度而非content-area。
这么一来,这就打破了一个长久的谣言:line-height 表示两个 baseline 之间的距离。在 CSS 里,不是这样的。

其实我说句我个人的理解:line-height === baseline之间的距离也不是全错,它只是需要有一个前置条件,那就是line-height的高度就是content-area的高度。当然line-height === line-box的高度更加通用。
还有另一类比较特殊的元素
- 可替换的内联元素,如 img / input / svg 等
- inline-block 元素,以及所有 display 值以 inline- 开头的元素,如 inline-table / inline-flex
- 处于某种特殊格式化上下文的内联元素,例如 flexBox 元素中的子元素都处于 flex formatting context(弹性格式化上下文)中,这些子元素的 display 值都是「blockfied」
这三种虽然都是内联元素,他们的height是取决于自己的盒模型高度的。
那么它们的height: auto;这个auto的值取自谁呢??
那就是line-height且这个时候content-area的高 === line-height。
有趣的事儿
line-height: normal;时,到底是多高呢?
其实这个时候就要拿出刚才的content-area,对就是那个100px -> 164px的content-area。
但这个时候还要引出一个在设计字体时配置的字段line gap。
normal时的高度是根据content-area的高度 & line gap的值算出来,也就是说,normal的高度其实也是在设计之初就规定好我们无法改变的。取决于字体类型的。

当 line-height 的值是一个数字时,其实就是相对 font-size 的倍数,而不是相对于 content-area。所以 line-height:1 很有可能使得 virtual-area 比 content-area 矮,从而引发很多其他的问题。
- 对于内联元素,padding 和 border 会增大 background 区域,但是不会增大 content-area(不是 line-box 的高度)。一般来说你无法再屏幕上看到 content-area。margin-top 和 margin-bottom 对两者都没有影响。
- 对于可替换内联元素(replaced inline elements)、inline-block 元素和 blockified 内联元素,padding、margin 和 border 会增大 height
一直不懂的vertical-align
首先要记住,vertical-align的默认值是baseline,只作用于inline,inline-block, table-cell box。
当不同font-size的文本以baseline的形式对齐时,就会出现这种情况。

line-box为了能盛下所有的自元素,它变高了。
这种情况还可能发生在一些本身就内置了font-family的标签中。
vertical-align: middle;是什么意思?
vertical-align: middle: Aligns the middle of the element with the baseline plus half the x-height of the parent.
这里的x-height是什么呢?
记得上面的图中有提到Capital height是大写字母的高度,这里的x-height也是设计师一开始就设定好的这种字体小写字母的高度。
baseline 所处的高度跟字体有关,x-height 的高度也跟字体有关,所以 middle 对齐也不靠谱。
所以我想要的对齐该怎么做
英文数字很简单~
import './App.css';
function App() {
return (
<div className="container">
<div>
<span className="span1">AAAAA</span>
<span className="span2">aaaaa</span>
<span className="span3">00000</span>
<span className="span4">GgGgGg</span>
</div>
</div>
);
}
export default App;
.span1 {
font-size: 30px;
}
.span2 {
font-size: 10px;
}
.span3 {
font-size: 50px;
}
.span4 {
font-size: 20px;
}

当改成中文的时候,就不行了

这是因为中文的基线不是汉字的下端。
可以分别设置不同的line-height
function App() {
return (
<div className="container">
<div class="a inline">
<div class="b">哈哈</div>
<span class="b">你好啊</span>
<b class="b">??</b>
<div class="b e1">haha</div>
<span class="b e2">小丑竟是我自己</span>
</div>
</div>
);
}
.a {
width: 100%;
border-bottom: 1px solid lightblue;
margin: 40px auto;
}
.inline .b {
display: inline;
vertical-align: bottom;
}
.a div {
font-size: 20px;
line-height: 20px;
}
.a span {
font-size: 40px;
line-height: 38px;
}
.a b {
line-height: 15px;
}
.b.e1 {
line-height: 16px;
}
.b.e2 {
line-height: 37px;
}

但这样也太傻了。。。。。
所以最终的最终,我才意识到,我学了半天对齐,竟然vertical-align救不了我??????
原来绝对定位才是最方便快捷有效的让中英文不同字体对齐的最有效办法。
