使用类似Typekit这样的在线字体,固然可以让网页在任何地点都能获得统一的视觉效果,但缺点也很明显——在字体加载完全之前会出现多次闪屏。

这一问题目前是无法正面解决的,因为各地的网速都不一致,而且像Typekit这样的服务通常在中国的载入速度相当缓慢。

我们只能通过一些曲折的方法掩盖闪屏,但实质上,这种闪烁客观存在且不会以网页设计者的意志而发生转移。

在术语中,这种闪屏被简称为「FOUT」,即「字体格式未加载完全前的闪烁」(Flash of Unstyled Text)。事实上,Typekit在其官方博客中早已给出了解决办法——当然,这也是一种治标不治本的方法。其主要思路是,通过定义「字体事件」来区分「加载中」(wf-loading)、「激活」(wf-active)和「未激活」(wf-inactive,即在「加载超时」(timeout)后认定该字体不可用)等三种状态,继而在CSS中针对上述三种状态给出不同的渲染方式。

通常,我们可以隐藏(visibility:hidden)处于加载中状态的字体,显示(visibility:visible)已激活的字体,并为未激活的字体添加替代字体(设置font:family)

一般情况下,如果将加载中的字体隐藏起来,那么可能会在几秒钟内(视字体具体的加载时间而定)出现网页空白或者部分元素缺失的(视你针对哪些元素设置了visibility字段)。

为了避免上述窘境,我们可以对已激活字体设置一个淡入效果,这样就可以在字体完全加载之前生成一个画面慢慢浮现的效果,看上去比较优雅。

具体的实现方式很简单,只需要在Farbox的网站设置后台里,将以下代码添加到渲染面板的脚本输入框里:

<style>
    .wf-loading body{
    opacity: 0;
    visibility: hidden;
    }
 
    .ie.wf-loading body{
    visibility: hidden;
    }
 
    .ie.wf-active body{
    visibility: visible;
    }
 
    .wf-active body{
    visibility: visible;
    opacity: 1;
    -webkit-transition: opacity 0.64s ease-in-out;
    -moz-transition: opacity 0.64s ease-in-out;
    transition: opacity 0.64s ease-in-out;}
</style>

另外,Typekit其实还有多种不同的嵌入方式,其中包括「标准嵌入」(standrd embed)和「异步加载模式」(asynchronous pattern)。两者之间的区别在于,当在线字体因超时而铁定无法载入时,标准嵌入可能会导致整个网页无法显示,最终卡死在白屏上,而异步模式则不影响其他元素的渲染,并且在载入超时后自动使用「备用字体」(fallback)。说到底,异步加载模式加强了网页的「可得性」,其具体解释可以参考《异步加载非核心CSS》

针对上述两种嵌入模式,Typekit给出了详细的操作指导和优缺点分析。没时间阅读的朋友可以直接添加以下脚本,添加位置和此前的步骤相同,注意将kitId换成你自己的

<script type="text/javascript">
  (function() {
    var config = {
      kitId: 'kitId',
      scriptTimeout: 3000
    };
    var h = document.getElementsByTagName('html')[0];
    h.className += ' wf-loading';
    var t = setTimeout(function() {
      h.className = h.className.replace(/(\s|^)wf-loading(\s|$)/g, ' ');
      h.className += ' wf-inactive';
    }, config.scriptTimeout);
    var d = false;
    var tk = document.createElement('script');
    tk.src = '//use.typekit.net/kitId.js';
    tk.type = 'text/javascript';
    tk.async = 'true';
    tk.onload = tk.onreadystatechange = function() {
      var rs = this.readyState;
      if (d || rs && rs != 'complete' && rs != 'loaded') return;
      d = true;
      clearTimeout(t);
      try { Typekit.load(config); } catch (e) {}
    };
    var s = document.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(tk, s);
  })();
</script>