前言

一些布局上的完全加载前后的变化很容易解决:为动态元素预先分配正确的空间,在图像上使用宽度和高度属性,并优先考虑 HTML 文档中的可见元素。但是,导致布局偏移的还有一个难以解决的问题:无样式文本 (FOUT) 的闪烁。

这篇文章我们将探索令人惊讶的复杂文本渲染世界,以及一些解决无样式文本闪烁的技术。

如果这篇文章有帮助到你,️关注+点赞️鼓励一下作者,文章公众号首发,关注 前端南玖 第一时间获取最新文章~

为什么字体会导致布局变化?

意外的布局变化(页面内容在没有用户交互的情况下移动)不利于用户体验。下载网络字体时,当字体题发生变化时,会导致包含元素(例如<div>,段落或段落)的大小发生变化,从而导致布局发生变化。当 Web 字体的字体高度或段落长度与系统字体相比不同时,就会出现这种情况。布局页面时,浏览器将使用后备字体的尺寸和属性来确定包含元素的大小,即使你已声明 Web 字体以阻止系统字体font-display: block

两种不同的字体是可能会导致布局发生变化的,但不是一定,这主要取决于字体的字体高度。

如何避免

我们现阶段的网页为了满足用户的审美往往会使用一些特殊字体,但与此同时也会带来一些体验上的问题,最常见的就是页面的加载速度以及文本闪烁等。所以我们有必要对字体进行一些优化操作来满足我们“日益挑剔”的用户。

font-display

最粗暴的解决方案是只需要一行CSS代码就能够解决。

font-display: optional;

为什么说只需要这一行代码就能够解决呢,因为如果 Web 字体在呈现文本时不可用(加上 100 毫秒),它会告诉浏览器使用备用系统字体。这意味着在未缓存的页面加载时,可能会使用备用字体,但所有后续页面加载都应使用 Web 字体呈现,因为它将被下载并在缓存中可用。

它一共有以下几个属性:

  • auto: 字体显示策略由用户代理
  • block: 为字体提供一个短暂的阻塞周期和无限的交换周期,在等待网络字体时隐藏文本最多三秒钟,并在加载时始终交换网络字体
  • swap: 为字体提供一个非常小的阻塞周期和无限的交换周期,尽快显示文本,并在加载时始终交换网络字体
  • fallback: 为字体提供一个非常小的阻塞周期和短暂的交换周期,隐藏文本最多 100 毫秒,然后仅在三秒内加载时交换网络字体
  • optional: 为字体提供一个非常小的阻塞周期,并且没有交换周期,隐藏文本最多 100 毫秒,然后仅使用可用的网络字体,从不交换

上面这样解释如果还不太明白的话,可以看看下面这张图:

Optional 是唯一保证不发生布局偏移的字体显示值

不幸的是,系统字体不一定是最好的设计,并且它们在各个操作系统之间并不一致。大多数设计师一想到向用户展示一个备用系统字体就会畏缩。接下来介绍各种优化以更快地将字体文件传送到浏览器,允许使用任何字体显示选项,但显示系统字体的风险最小,或者用于optional: 以外的选项而不触发布局转换。

优化字体文件

优化网络字体有两种关键方法:子集和格式。

子集字体

许多字体将具有来自多个字母的字形(字形是单个字符,例如a&)如果你仅以拉丁字母 (a - Z) 提供并且不使用连字(如é),那么这些字形表示您的字体文件中浪费的字节。

从此字体中删除非拉丁字符会产生woff2一个大小为六分之一的文件。

字体格式

各主流设备基本都支持 woff2 字体格式,因此网站中没有必要再引入多种不同格式的字体了。一般地,建议只引入 woff2 就好了,既可以保持代码的简洁性,又可以减少上传到你服务器的文件。

加载更少的字体

虽然我们会将字体转换成woff2格式,但文件大小依然有好几百K,有时甚至是几M,字体文件的大小也会影响页面整体的渲染速度。有些时候我们只需要一些极少数的文字用于特殊字体,那我们就没必要将一整个字体文件引入了。

提取字体

当我们遇到上面这种情况时,千万不要将一整个字体包引入进去,这将极大地浪费网络带宽,从而影响页面的加载。这里推荐使用font-spider 字蛛来提取文字。

  • 安装font-spider
npm install font-spider -g
  • 提取

我们还是以上面那段诗句为例,那里我们用的是汉仪旗黑.woff2字体文件。这里还是经过缩小文字库之后的大概是32K

我们再在项目目录下执行以下命令

font-spider index.html

这时会生成一个.font-spider目录,并将提取后的字体文件放在该目录下。现在的字体文件大概就只有10K,比之前的体积小了好几倍。

使用系统字体

Web 字体很受欢迎,因为它们允许设计人员在浏览器中保持一致的外观和感觉。如果不需要,系统字体将是呈现文本的最快方法。如果当前的Web字体接近系统字体,您可以使用Monica的Font Style Matcher来调整字体设置,直到获得近乎完美的匹配。

使用系统字体意味着文本将尽可能早地呈现。我们现在还拥有使字体与操作系统匹配的方法,这可能比以前的备用选项(如 Arial 和 Helvetica)更具吸引力。为此,我们需要按特定顺序列出所有操作系统的系统字体:

body {
font-family: -apple-system, BlinkMacSystemFont,
"Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell",
"Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
}

快速交付字体文件

很明显,我们为了确保字体快速且正确地应用在我们网页上,我们必须让浏览器尽快下载我们的字体文件,在我们自己的CDN上托管字体将获得最佳性能。

使用CDN托管

一般来说,我们应该提供我们服务器中的字体以避免连接到第三方服务器的成本,这对于高延迟连接尤其重要。使用第三方服务意味着您的字体将被延迟。最好的情况是您直接从另一个主机名(例如fonts.gstatic.com)请求字体文件,这会产生连接成本——DNS 查找、TCP 连接和 TLS 协商。最坏的情况是多跳,例如从fonts.googleapis.com加载引用fonts.gstatic.com上的文件的 CSS 文件,会导致两次连接损失。所以我们一般会将一些字体文件等静态资源托管在我们自己的CDN服务器上,以此来加快资源的下载速度。

缓存字体

字体可以缓存在两个地方:客户端和CDN。客户端上的缓存对于会话中的导航很重要,并且应该以避免重新验证请求的方式完成。重新验证请求 (if-not-modifiedif-modified-since) 将阻止浏览器使用字体文件,直到它验证它在服务器上没有更改。字体很少改变,所以我们应该如下实现一个缓存头,并在字体改变时更新文件名来破坏缓存:

cache-control: max-age=31536000,immutable

这告诉浏览器他们可以保留字体长达一年并且不需要重新验证( Firefox 和 Safariimmutable 支持,Chrome 应该自动避免重新验证请求)。避免将 ETag 添加到这些响应中,因为它们可能会强制重新验证。

还要检查您的 Content Delivery Network 配置是否可以将字体文件存储在缓存中,较旧的配置可能不包含.woff2扩展名,从而导致原始命中并减慢响应速度。

使用预加载

一般来讲浏览器不会随便地去下载字体文件,它们会等到渲染树构建完成后才能知道需要哪些字体。这意味着仅在浏览器下载并解析 HTML 和 CSS 时,即在呈现文本之前,才请求 Web 字体。但需要注意的是,内联 CSS 不需要网络请求,这意味着我们的字体可以在页面加载的早期获取。

渲染树的构建过程会阻塞Web字体的请求。

但是如果我们确定页面文本的渲染肯定会用到一些网络字体时,我们可以使用preload让浏览器提前下载字体文件。

<link rel="preload" href="./public/fonts/汉仪旗黑.woff2" crossorigin="anonymous" type="font/woff2">

当浏览器解析这行 HTML 时,它会立即发送一个对字体文件的高优先级请求。

但是需要注意的是,预加载的请求会占用其它请求的带宽,所以我们在使用过程需要考虑清楚是否值得这么做。

将字体转为Base64

还有一种常用的方法是将字体作为 Base64 字符串嵌入到 CSS 中,从而无需额外的字体请求并确保在呈现文本时字体可用。

但这个方法也不是绝对的好方法,它只适合一些小型字体文件,例如上面提到的使用font-spider提取后的字体文件,并且该字体文件足够小,因为将字体文件转化为Base64字符串往往会增加体积。

一般来讲它有以下缺点:

  1. 字体文件是压缩的二进制对象,编码为 Base64 字符串会显着增加大小。CSS 包的 gzip 或 brotli 压缩并不能完全弥补这种膨胀。
  2. 字体将被发送到每个浏览器,即使有的浏览器不能使用
  3. 字体很少更改,但 CSS 经常更改,这将降低字体的缓存效率,因为每次 CSS 更改都会使整个包无效
  4. 膨胀 CSS 大小几乎肯定会延迟页面渲染

使用f-mods减少布局偏移

F-mods 是对字体描述符规范的提议更新,其中包括四个新的描述符:

  • ascent-override (%) : 覆盖分配给上升器的大小
  • descent-override (%) : 覆盖分配给下降者的行高
  • line-gap-override (%) : 覆盖行间距
  • advance-override (#) : 为每个字符设置一个额外的提前量,以帮助匹配行宽并防止单词溢出

前三个都影响线的高度:线框高度 = 上升 + 下降 + 线间隙。基线位置 = 线框顶部 + 线间隙 / 2 + 上升。

这四个描述符的组合允许我们通过告诉浏览器在下载 Web 字体之前字符将占用多少空间来覆盖备用字体的布局以匹配 Web 字体。

f-mods 只真正修改垂直间距和定位。这意味着仍然需要处理字符间距和字母间距,否则可能会在不同的点出现断行的单词,从而导致元素高度发生变化,从而导致布局发生变化。但是@font-face 声明中没有letter-spacingandword-spacing属性,因此我们必须在主体或元素上声明。

@font-face {
font-family: custom-font;
src: url("./public/fonts/汉仪旗黑.woff2");
}
@font-face {
font-family: fallback-font;
src: local(Arial);
ascent-override: 100%;
descent-override: 20%;
line-gap-override: normal;
advance-override: 10;
}
/* 这些具体数值因字体而已,需要按照自己的字体进行计算调整*/
body {
font-family: custom-font, fallback-font;
}
.content {
letter-spacing: -1.1px;
word-spacing: -0.2px;
}

总结

总之,如果浏览器没有及时获取网络字体,并且可以应用font-display: optional到网络字体,让浏览器以备用系统字体呈现来防止布局偏移,否则的话就只能优化我们的字体以尝试在浏览器需要它们之前将它们获取到浏览器:

  • 使用woff2最小化文件大小
  • 优化字体文件
  • 加载更少的字体
  • 预加载关键字体
  • 在CDN上托管字体文件
  • 使用 f-mods 减少字体交换的影响

喜欢的同学欢迎点个赞呀~ 原文首发地址点这里,欢迎大家关注公众号 「前端南玖」,如果你想进前端交流群一起学习,请点这里

我是南玖,我们下期见!!!

如何避免由 Web 字体引起的布局偏移的更多相关文章

  1. WEB字体,多列布局和伸缩盒

    WEB字体 语法 @font-face{ font-family:""; src:url() format() ... } 兼容性写法 @font-face { font-fami ...

  2. CSS3 04. 伸缩布局、设置主轴,侧轴方向、主/侧轴对齐方式、 伸缩比例、元素换行、换行控制、覆盖父元素的align-items;控制子元素顺序、web字体、突变字体

    CSS3 在布局方面做了非常大的改进,对块级元素的布局排列变得十分灵活,适应性非常强,其强大的伸缩性,在响应式开发中可以发挥极大的作用.(兼容性不好) 必要元素: 指定一个盒子为伸缩盒子 displa ...

  3. 如何把你的图标转换成web字体

    在这篇教程中,我们将使用一个免费的Web应用程序IcoMoon将矢量图转换成Web字体,然后将生成的字体通过css应用到Web页面中. 通常我们在网站中必不可少的会使用到一些小图标.在正常尺寸下,布局 ...

  4. web app 自适应 弹性布局之rem

    关于rem,主要参考文档 1.腾讯ISUX (http://isux.tencent.com/web-app-rem.html) 2.http://www.w3cplus.com/css3/defin ...

  5. web前端响应式布局,自适应全部分辨率

    写phpd的我. 近期公司要弄个app关键是没有web开发,而我有比較闲,那就扛枪上阵吧. 响应式布局,web端的?php我一直都是用tp框架,对于web首先想到的是bootstrap框架.仅仅是简单 ...

  6. 【转载】Web移动端Fixed布局的解决方案

    特别声明:本文转载于EFE的<Web移动端Fixed布局的解决方案>.如需转载,烦请注明原文出处:http://efe.baidu.com/blog/mobile-fixed-layout ...

  7. Monotype推出基于HTML5的Web字体平台

    著名字体公司Monotype近日宣布推出基于HTML5的Web字体平台,设计者可以访问近10万字体的目录. Monotype推出基于HTML5的Web字体平台 Monotype推出基于HTML5的We ...

  8. 制作web字体:CSS3 @font-face

    @font-face是CSS3中的一个模块,他主要是把自己定义的Web字体嵌入到你的网页中,随着@font-face模块的出现,我们在Web的开发中使用字体不怕只能使用Web安全字体,另外@font- ...

  9. 几种web字体格式

    目前,文字信息仍是网站最主要的内容,随着CSS3技术的不断成熟,Web字体逐渐成为话题,这项让未来Web更加丰富多彩的技术拥有多种实现方案,其中之一是通过@font-face属性在网页中嵌入自定义字体 ...

随机推荐

  1. 安装docker-compose--翻译

    Install Docker Compose 译文 安装 Docker Compose 你可以在macOS.Windows.64-bit Linux上运行 Compose 前提条件 Docker Co ...

  2. 【读书笔记】C#高级编程 第九章 字符串和正则表达式

    (一)System.String类 System.String是一个类,专门用于存储字符串,允许对字符串进行许多操作.C#提供了关键字string和相关的语法,以便使用这个类更轻松. 例子: 使用运算 ...

  3. Python 第五次实验

    [1] (程序设计)编写程序,将二维列表数据写入 CSV文件(命名为"out.csv"),用逗号隔开.二维列表如下:[['Name','Age','Gender'], ['Bob' ...

  4. PLM产品生命周期管理,包含哪些阶段?

    PLM:Product Lifecycle Management=产品生命周期管理.产品的整个生命周期包括:投入期.成长期.成熟期.衰退期.结束期.PLM系统使企业可以把多年积累的所有产品相关数据放到 ...

  5. 配置联想IMM使用AD账户登录

    IMM是联想(IBM)服务器的管理卡Integrated Management Module的缩写,现在是第二个版本.通过它可以远程管理服务器,就像你在服务器面前操作一样.可以修改BIOS设置,可以重 ...

  6. 《Java基础——继承》

    Java基础--继承     一.类的继承: 规则: 1.一个子类只能有一个父类,但一个父类可以有多个子类,每个子类都可以作为父类使用. 2.若一个类没有声明父类,则默认其父类为Object. 3.父 ...

  7. Django django-admin.py 命令详解

    一.Django 基本命令 下载 Django pip3 install django     # 默认下载最新版 pip3 install django==4.1  # 手动选择版本 创建Djang ...

  8. .NET Core Web APi类库如何内嵌运行?

    话题 我们知道在.NET Framework中可以嵌入运行Web APi,那么在.NET Core(.NET 6+称之为.NET)中如何内嵌运行Web Api呢,在实际项目中这种场景非常常见,那么我们 ...

  9. Grafana Loki 架构

    转载自:https://mp.weixin.qq.com/s?__biz=MzU4MjQ0MTU4Ng==&mid=2247492186&idx=2&sn=a06954384a ...

  10. Logstash:使用Logstash将电子邮件导入到Elasticsearch