w3ctech

Web字体载入利器-->Web Font Loader

Web字体载入利器-->Web Font Loader

很久以前社区中就存在关于字体载入问题的争论,也就是说随着网站被载入,所有的字体都应该被隐藏,直到正确的资源已经下载完成时字体才出现。许多设计师以及开发人员(却提出相反的看法),解释说(虽然)字体载入的默认方法被称为Flash of Unstyled Text,或者FOUT,但是(在一些情景下)对用户来说却是件苦差事。例如备用的Georgia字体首先出现在屏幕上,然后在常见字体被下载完成后,Georgia字体就被替换掉。他们解释道,(上面的情况)会有助于形成凝聚力强的浏览体验(仅仅限于用户只是纯粹想等待所有的资源被下载完而不是想体验flash从一种字体到另一种字体的情况)。
但是现如今的情况不适合上面的例子。
事实上,让浏览器隐藏所有的文本(被普通字体修饰)的做法现如今被叫做Flash of Invisible Text或者FOIT,通常认为这种方法是所有可行选择中最差的一种。Scott Jehl已经表示过这种想法在性能和易用性方面的表现是非常糟糕的

The FOIT tends to be most problematic in browsers like iOS Safari, which hides text for up to 30 seconds before giving up and rendering it with a default font, but it can also be seen in browsers with shorter hiding durations like Chrome, Firefox, and Opera as well.

同样地,Typekit help page on the matter文中写到FOUT方法有助于(设计师)设计出响应速度快且能够跑起来的页面,尤其是在很慢的网络链接。所以作为一个设计师或者开发人员,我们必须要在FOUT以及FOIT两者之中做出选择。
Flash of Invisible Text
1.字体开启下载模式
2.只要web字体处于请求状态,文本就是不可见
3.传递速度会变得很慢
4.web字体载入
5.文本随着webfont出现而变得可见
Flash of Unstyled Text
1.字体开启下载模式
2.通过备用的font-family来快速显示文本
3.web字体载入
4.使用备用字体的文本将会被web字体所替换

这两种方法差异程度让人吃惊,Scott指出FOIT方法可以使得Filament Group网站的文本在处于3G网络情况下2.7s后才可见,然而FOUT方法可以让文本0.6s后可见。如果想让网站内容尽可能快的展示在人们面前,那么我们有必要使用FOUT方法,用户体验以及网络性能是FOUT方法中考虑较多的因素。

FOUT方法存在的问题

这FOUT方法肯定是存在一些不足的。例如在备用字体以及好看字体之间的切换会出现许多可怕的不稳定因素以及由于字重以及x-height不同导致jankiness。因此一个公司可能仅仅只是因为商标的原因来使用一种特定的字体的情况会随着FOUT方法将会变得不复存在。
(使用一些手段)可以减轻由于FOUT缺点所带来的影响,来看一下Bram Stein的网站(其实这个网站就是个很好的例子),浏览这个网站你可以发现,由纯文本组成的轻量型flash在载入的时候几乎是瞬时的,在页面加载完成后,你几乎看不到页面发生了哪些改变,就和开始的页面一模一样。当然,如果我们想使用一种字体,但是发现这种字体没有合适的备用字体,那么我们就可以使用svg代替上述字体来展示文本

FOUT技巧的初体验

我最近正在尝试Web字体载入利器,(Web字体载入利器)可以让开发人员在如何使用FOUT技巧处理字体方面有更多的选择权。首先我们需要在html中嵌入Web字体载入利器代码


(function(d){  
  var wf = d.createElement("script"),  
      s = d.scripts[0];  
  wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js';  
  s.parentNode.insertBefore(wf,s);
})(document);

在页面中异步载入上述脚本,所以你也可以在body结束标签之前加上上述代码,或者在head标签中加入上述代码,(因为上述代码属于异步载入,是不是因为该js代码是动态生成的还是只要是通过js的src特性引入js代码就是异步载入)就不会阻塞其它资源的下载,这种技巧对IE9也是起作用的,但是这种技巧对于我们项目来说不是最重要的,所以我们可以使用下面的方法代替

<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js" async></script>

一旦我们页面载入该脚本,那么我们就可以添加我们所需的字体。在项目中,通过@font-face属性引用字体的方式进行上手体验。上述所说的字体来自Typonine,于是我们可以在html的head标签中添加link标签来进行预处理Typonine

<link rel="preconnect" href="https://fonts.typonine.com/" crossorigin>

preconnect在这里是非常有用的,因为preconnect可以自动的为我们发起TCP握手,所以在我们脚本请求字体资源之前,我们已经知道所有关于字体的消息,浏览器需要抓取这些配置信息,然后可以使进程变得更快一点。多亏了Ilya Grigorik的建议,其实我们也可以使用crossorigin属性
现在我们可以通过使用WebFontConfig对象的方式来检查这些字体是否被载入

WebFontConfig = {
  custom: {
    families: [
      'Nocturno Display Medium 3',
      'Nocturno Book 2',
      'Nocturno Regular Italic 24',
      'Nocturno Regular 26',
      'Nocturno Regular 25'
    ],
    urls: [
      'https://fonts.typonine.com/WF-000000-001254.css',
      'https://fonts.typonine.com/WF-000000-001255.css'
    ]
  },
  timeout: 2000
};

custom对象可以让Web Font Loader知道我们想通过外部的样式载入字体。但是由于这个Web Font Loader,我们可以使用来自Typekit Google Facedeck或者Font.com的字体。通过families数组我们可以辨别所有字体的名字(我们可以在CSS中直接使用这些字体名字)
你可能注意到我设置了2s的timeout属性,这只是主观的配置项,但是我认为发起这样的一次网络请求是需要很多时间的,甚至是比用户处于很差的网络环境中情况所花费的时间更长,因为Web Font Loader想要的是(字体)内容
一旦loader完成所有字体载入工作,然后就会html标签上加入相应的class特性

<html class="wf-nocturnoregularitalic24-n4-active wf-nocturnoregular26-n4-active wf-nocturnoregular25-n4-active wf-nocturnodisplaymedium3-n4-active wf-nocturnobook2-n4-active wf-active">
   <!-- markup for the website goes here -->
</html>

这些样式其实相当于钩子(我们可以通过判断字体是否被载入来给页面添加相应的样式)。这里有很多可以让我们添加的样式:

  • loading:当所有的字体被请求时触发
  • active:当字体被渲染时触发
  • inactive:当浏览器不支持link引入字体或者没有字体载入触发
  • fontloading:一旦有字体被载入时触发
  • fontactive:一旦有字体被渲染时触发
  • fontinactive:当字体不能被载入时触发

在我们样式表中,我们可以通过.wf-active样式特性来设置合适的字体名:

$fallback: Georgia, serif;

h1, .h1 {
  font-family: $fallback;

  .wf-active & {
    font-family: "Nocturno Display Medium 3";
  }
}

现在我们可以发现字体名字赋给了一个变量$fallback,但是一旦样式.wf-active被Web Font Loader添加到html标签上,那么当前字体将会切换到喜欢的字体。在字体载入方面,我们正在努力寻找更好的方法。

避免jank

我注意到一个问题:一旦这些字体被载入,(就会发现)隶属于一行文字的数目发生改变以及一些文字要么太宽要么太小,这是因为这些元素添加到这个页面中。
为了解决这个问题,我们通常设置不同的font-size以及line-height来获取更平滑的体验:


h1, .h1 {
  font-size: 30px;
  line-height: 35px;
  font-family: Georgia, serif;

  .wf-active & {
    font-size: 26px;
    line-height: 30px;
    font-family: "Nocturno Display Medium 3";
  }
}

压缩纯文本的flash

在第一个页面载入后,Filament Group推荐设置cookie以便我们在没有纯文本的flash资源同时.wf-active样式都能应用到html中。但是在我的上手体验中,我只是仅仅使用静态的html,因此不能获取cookie选项。(代替的方法)我使用Web Font Loader中能够起作用的事件来进行更新sessionStorage,类似这样:

WebFontConfig = {
  // other options and settings
  active: function() {
    sessionStorage.fonts = true;
  }
}

不要忘记来看一下可使用的事件列表。但是有了这个特别的事件,我们就可以快速核实在文档的头部是否有键名存在于sessionStorage,如果真的存在的话,那么我们可以尽可能快的向html标签添加样式

<head>
  <script>
    (function() {
      if (sessionStorage.fonts) {
        console.log("Fonts installed.");
        document.documentElement.classList.add('wf-active');
      } else {
        console.log("No fonts installed.");
      }
    })();
  </script>
</head>

这个不会完全阻塞纯文本的flash资源的下载,这将会是个问题,但是确实可以改善FOIT存在的一些问题.

字体载入(的好坏)对于排版是至关重要的

在把玩了这个技巧后,我发现这种经历完全刷新了我对在web中什么是好的排版技巧的三观。

前端圈微信

扫码关注前端圈微信公众号

共收到0条回复