CSS之文本渲染

732 阅读5分钟

注:以下内容摘自Josh Comeau的CSS for JavaScript Developers课程

每个字符的精确位置在不同的浏览器之间是不同的。比起 Safari,Chrome 的逗号更接近前面的单词。但是如果你仔细观察,你会发现大多数字母在不同浏览器之间会有轻微的变化。

TextRendering.png

字距调整

不同浏览器之间字母排列方式略有不同的原因是,浏览器实现了不同的字距调整算法。

字距调整是指单个字符之间的间距。字符被轻推向左或向右。这个过程一半是艺术,一半是科学。

浏览器不会对等宽(monospaced)字体进行字距调整,因为这些字符需要整齐地排列在列中。

使用 CSS,我们可以选择使用font-kerning: none完全禁用调整,但是浏览器没有提供给我们任何字距细粒度控制。letter-spacing这个属性让我们可以增加/减少单个字符之间的间距,它充当了一个“字距调整增幅器”ーー它会放大浏览器定好的字距调整。

如何自定义字距调整?

  1. 使用 font-kerning: none 禁用内置字距调整。
  2. span标签包装每个字母。
  3. 使用letter-spacing进一步移动字母,给每个span标签选择自定义值。

如果你对提高字距调整技巧感兴趣,一个超级整洁的字距调整小游戏可以帮助你练习。

文本栅格化

除了浏览器扮演的角色之外,浏览器的操作系统也会影响排版的渲染方式。

为了理解为什么文本在不同机器上看起来如此不同,我们需要学习光栅化rasterization和抗锯齿anti-aliasing

在早期的计算机时代,字体实质上是图像的集合。每个字符由一张图片表示。“font”是一个巨大的图库,每个字体的不同尺寸都对应一个图片。这就是所谓的“位图字体”。

位图字体在今天仍然很流行,但是很少见。对于字体来说,使用像 ttf、 otf、 svg 和 woff/woff2这样的矢量格式更为常见。在矢量字体中,我们为每个字符存储一组数学指令。

矢量字体的主要好处是它可以缩放到任何大小,而不会使字母变得像素化和模糊。

然而,为了将矢量字体转换成屏幕上的字符,浏览器必须找出每个像素的颜色,这个过程被称为光栅化。

维基百科上有一篇关于字体光栅化文章,深入挖掘了不同的文本渲染方法。

最简单的方法是填充矢量路径所经过的任何像素。在这个例子中,我们通过在相关的像素上涂色来绘制字母“A”:

letter-outline.gif

正如你所想象的,这种光栅化的方法产生了像素齿状化的文本:

https://courses.joshwcomeau.com/course-materials/wiki-no-antialias.png

为了使文本看起来更顺滑,浏览器可以应用“抗锯齿”:

https://courses.joshwcomeau.com/course-materials/wiki-no-hinting.png

Basic anti-alias algorithm.

https://courses.joshwcomeau.com/course-materials/wiki-hinting.png

Anti-aliasing using hints embedded in the font file.

那么,为什么我们的文本在不同的机器上看起来不同呢?这是因为浏览器和操作系统在光栅化和抗锯齿过程中都起到了一定的作用。不同的算法产生不同的结果。

我们能改变它在 CSS 中使用的算法吗? 可以...,但是很复杂。

平滑字体

webkit-font-smoothing 属性允许我们切换浏览器使用的抗锯齿算法。但不幸的是,它只能在 MacOS 上使用,而且只能在 Chrome/Safari/Edge (不能在 Firefox 上使用)上使用。

subpixel-antialias.png

“Subpixel antialiasing”是一种奇特的技术,它利用像素的 R/G/B 元素来产生更精确的抗锯齿效果。这张来自维基百科的放大照片展示了这种技术:

subpixel-antialias2.png

从本质上讲,我们可以通过从相邻的像素中 "借用 "一个颜色来提高感知的分辨率。这一点额外的厚度对抗锯齿算法很有用。

试着把你的头靠近/远离显示器。当足够靠近屏幕时,你应该能够看到3色带。离屏幕较远时,文本应该是白色的。这很好地演示了显示器是如何工作的!我们的眼睛可以通过结合三原色而看到白色。

十多年前,当这个属性被添加到苹果设备上时,它帮助生成了更清晰的文本。

不过,自那以后,很多事情都发生了变化。

首先,我们的像素排列变得更加复杂。如果你在显微镜下观察现代设备的屏幕,你会发现一些非常有创意的像素排列。

例如,看看 Apple Watch 和 iPhone XS Max:

https://courses.joshwcomeau.com/course-materials/pixel-closeup-apple-watch.jpg

Apple Watch

https://courses.joshwcomeau.com/course-materials/pixel-closeup-iphone.jpg

iPhone XS Max

(Sources: Apple Watch and iPhone)

另一件大事也发生了变化。大多数显示器是“高分辨率”。这意味着每个逻辑像素都映射到多个物理像素上。

现代 iPhone 有3倍的设备像素比,这意味着每个逻辑像素由9个物理像素表示。

考虑到现代像素如此微小,我们能从subpixel antialiasing中得到什么好处吗? 很显然,苹果公司不这么认为。

自2018年发布 MacOS Mojave 以来,苹果公司默认关闭了subpixel antialiasing功能。如果你使用视网膜显示器,subpixel antialiasing效果似乎弊大于利,会产生朦胧、难以辨认的文本。

然而令人困惑的是,像 Chrome 和 Safari 这样的 MacOS 浏览器并不“继承”系统默认设置。我们需要明确地使用antialiased 来启用抗锯齿:

*, *:before, *:after {
  box-sizing: border-box;
  -webkit-font-smoothing: antialiased;
}

其他浏览器

webkit-font-smoothing 只能在 MacOS Chrome/Safari/Edge 上使用。

MacOS Firefox 提供了一个替代属性-moz-osx-font-smoothing,但它只允许我们在grayscaleauto之间切换。而且,在大多数情况下,auto默认为grayscale。两者似乎没有任何差别。

而在 Windows 上,根本没有这个属性。Windows 确实有一个称为 ClearType 的subpixel-antialiasing算法,但它不是在 CSS 中公开的。