All I want is 对齐

在项目中要某些元素对齐简直直是不能再常见的需求。

文本的对齐就是对齐大军中的一支先锋部队了。说实话,我到现在都有点害怕文本对齐🤦‍♂️。

那么,这篇blog也就呼之欲出了,今天我们就来一起学习一下文本相关的对齐。

本文注重的是不同font-size的字体对齐

说在前

有的CSS属性看起来是最基本最简单的,但是其中真的蕴藏玄机。有的时候真的觉得CSS好难啊😭。

感觉自己一直没有很系统的去学习一遍CSS,总是停留在某些单个的属性上会让知识变得相当的分散。无法形成体系也就意味着对整体没有概念无法通其前因后果,方难得始终啊

所以,类似于BFC & IFC这样的核心知识当然是要被提上日程。本期就先不展开讨论了~(不,我不会鸽的,不知鸽为何物)

先说说font-size

至于IFC & ,我们当然要留到下期展开讲了

不知道大家在开发过程中有没有观察过界面里的字体。在框选它们的时候,款选的文本上下都会出现一部分的空白

接下来看看不同的字体。

相同的font-size在框选它们的时候却拥有着不一样的高度

图片源于Vincent De Oliveira

这就非常迷惑了,大家都是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

图片源于Jackie Yin - 深度剖析Css Baseline

接下来看这张图

图片源于Jackie Yin - 深度剖析Css Baseline

其实我们会发现,实际高度字母本身的高度相差的地方就是图中?所指出的地方。(ig.emo直呼内行并发了个?

其实在设计字体的时候,字体的一些属性就已经定死了。这是字体内部的属性,是无法通过css来改变的。

图片源于Jackie Yin - 深度剖析Css Baseline

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

图片源于Jackie Yin - 深度剖析Css Baseline

这里首先需要指出一点

通过测试发现,macOS 上的浏览器使用了 HHead Ascent 和 HHead Descent 值,Windows 上的浏览器使用了 Win Ascent 和 Win Descent(而且两个平台上的值不一样)。

然后我们发现,?部分其实就是

而这些值都是在设计字体的时候就定好的

我们再来计算一次,如图所示

go back

我们假设我们给定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

这个时候就要请出line-height这个属性了。

图片源于Vincent De Oliveira

可以看到如果我们设定的line-height > 计算出来的content-area那么,line-box为了能够放得下这个内容,自然应该跟随line-height的高度而非content-area

这么一来,这就打破了一个长久的谣言:line-height 表示两个 baseline 之间的距离。在 CSS 里,不是这样的。

图片源于Vincent De Oliveira

其实我说句我个人的理解:line-height === baseline之间的距离也不是全错,它只是需要有一个前置条件,那就是line-height的高度就是content-area的高度。当然line-height === line-box的高度更加通用。

还有另一类比较特殊的元素

这三种虽然都是内联元素,他们的height是取决于自己的盒模型高度的

那么它们的height: auto;这个auto的值取自谁呢??

那就是line-height且这个时候content-area的高 === line-height

有趣的事儿

line-height: normal;时,到底是多高呢?

其实这个时候就要拿出刚才的content-area,对就是那个100px -> 164pxcontent-area

但这个时候还要引出一个在设计字体时配置的字段line gap

normal时的高度是根据content-area的高度 & line gap的值算出来,也就是说,normal的高度其实也是在设计之初就规定好我们无法改变的。取决于字体类型的。


图片源于Vincent De Oliveira

line-height 的值是一个数字时,其实就是相对 font-size 的倍数,而不是相对于 content-area。所以 line-height:1 很有可能使得 virtual-area 比 content-area 矮,从而引发很多其他的问题。


一直不懂的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救不了我??????

原来绝对定位才是最方便快捷有效的让中英文不同字体对齐的最有效办法。

参考