在渲染网页之前等待字体加载

Posted

技术标签:

【中文标题】在渲染网页之前等待字体加载【英文标题】:Wait for fonts to load before rendering web page 【发布时间】:2011-06-10 09:07:22 【问题描述】:

我正在使用@font-face 在我的网站中嵌入字体。首先文本呈现为系统默认值,然后(一旦字体文件可能已加载)正确的字体会在几分之一秒后呈现。有没有办法通过将页面渲染延迟到字体加载或类似之后来最小化/消除这种延迟。

【问题讨论】:

缓存字体会起作用。 请记住,这是让您的网站看起来很慢的好方法,因为无线链接上的用户可能需要一段时间才能加载网络字体。如果文本在几秒钟内没有出现,许多用户只会点击返回按钮。 这能回答你的问题吗? How to be notified once a web font has loaded 【参考方案1】:

我在渲染到 html 画布时遇到了类似的问题,这是我的解决方案。它基于 FontFace API,类似于 Holtwicks 方法。主要区别在于这是一种通用方法,并且它可以开箱即用地用于外部字体/样式表(例如 google 字体)。

几个笔记; 如果字体未知,fonts.load( ... ) 将很乐意用一组空字体解决。据推测,如果在添加声明字体的样式表之前调用此代码,则会发生这种情况。我添加了fonts.check(...) 来解决这个问题。

这将让您等待 javascript 执行,直到字体可用,因此对于“普通”HTML 内容,它不会开箱即用。您可以将其与上面的 Holtwicks 答案结合使用。

export async function waitForFontLoad(
    font: string,
    timeout = 1000,
    interval = 10
) 
    return new Promise((resolve, reject) => 
        // repeatedly poll check
        const poller = setInterval(async () => 
            try 
                await document.fonts.load(font);
             catch (err) 
                reject(err);
            
            if (document.fonts.check(font)) 
                clearInterval(poller);
                resolve(true);
            
        , interval);
        setTimeout(() => clearInterval(poller), timeout);
    );

【讨论】:

【参考方案2】:

这段代码对我来说效果很好。它使用Font Loading API,在现代浏览器中得到了很好的支持。

<style>
  @font-face 
    font-family: 'DemoFont';
    font-style: normal;
    font-weight: 400;
    src: url("./fonts/DemoFont.eot");
    src: url("./fonts/DemoFont.woff2") format("woff2"),
    url("./fonts/DemoFont.woff") format("woff"),
    url("./fonts/DemoFont.ttf") format("truetype");
  

  .font 
    font-family: 'DemoFont';
    color: transparent;
  

  html.font-loaded .font 
    color: inherit; // Override `transparent` from .font
  

</style>
<script>
  // Check if API exists
  if (document && document.fonts)     
    // Do not block page loading
    setTimeout(function ()            
      document.fonts.load('16px "DemoFont"').then(() => 
        // Make font using elements visible
        document.documentElement.classList.add('font-loaded') 
      )
    , 0)
   else 
    // Fallback if API does not exist 
    document.documentElement.classList.add('font-loaded') 
  
</script>

诀窍是为使用该字体的元素将 CSS 颜色设置为透明。加载后,通过将 font-loaded 类添加到 &lt;html&gt; 元素来重置它。

请将DemoFont 替换为对您的项目有意义的内容以使其正常运行。

【讨论】:

这个问题的一个问题是fonts.load('10px Nothing') 很乐意用一个空的字体列表来解决。您需要结合 loadcheck【参考方案3】:

使用https://github.com/typekit/webfontloader

并检查配置中的事件 https://github.com/typekit/webfontloader#configuration

<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js"></script>
<script>
    WebFont.load(
        custom: 
            families: [ "CustomFont1", "CustomFont2" ]
        ,
        active: function() 
            //Render your page
        
    );
</script>

【讨论】:

【参考方案4】:

编辑:最好的方法可能是base64 编码你的字体。这意味着您的字体必须在您的 HTML 被解析和显示时完全加载。您可以通过单击“专家”然后单击“base64 编码”来使用字体松鼠的 webfont 生成器https://www.fontsquirrel.com/tools/webfont-generator 来完成此操作。这就是 TypeKit 等服务的工作方式。


原答案: 另一种检测字体是否已加载的方法是使用 FontLoader https://github.com/smnh/FontLoader 或复制它们的策略。

它们绑定到浏览器中的滚动事件,因为当字体加载时它会调整文本大小。它使用两个包含的 div(当高度变化时会滚动)和一个单独的 IE 后备。

另一种方法是使用setInterval 定期检查 DOM,但使用 javascript 事件更快更好。

显然,您可能会执行一些操作,例如将 body 的不透明度设置为 0,然后在字体加载后显示它。

【讨论】:

我真的很喜欢 base64 解决方案!为我工作! 我尝试通过一些 JavaScript 代码将 base64 编码字体插入&lt;style&gt; 标签。之后,我测量文本的大小。但它(至少)不适用于 Safari(ios 15),因为我将大小作为默认字体应用而不是新字体。【参考方案5】:

由于没有人提到这一点,我相信这个问题需要更新。我设法解决问题的方法是使用现代浏览器支持的“预加载”选项。

如果有人不需要支持旧浏览器。

<link rel="preload" href="assets/fonts/xxx.woff" as="font" type="font/woff" crossorigin>

一些包含更多细节的有用链接:

https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content http://www.bramstein.com/writing/preload-hints-for-web-fonts.html

【讨论】:

这是最好的答案,自定义字体在未使用时不会加载。这个会立即加载,因此第一次使用字体时不会出现框字体 在 Chrome 91、Safari 14、Firefox 89 和 Opera 77 上完美运行! 我有一个脚本,它依赖于实际文本的宽度和高度,但它从应用自定义字体之前获取它们。这解决了它! 这将预加载字体并使其在渲染 DOM 时可能可用。但是,由于它不是阻塞的,它仍然不能保证在页面渲染之前加载自定义字体。【参考方案6】:

您可以在 @font-face 中使用 CSS font-display。 所有可用值的关键字是:

自动 阻止 交换 后备 可选

Giulio Mainardi 写了一篇关于它们的好文章,你应该在 sitepoint 上使用它。

您可以在这里阅读:https://www.sitepoint.com/css-font-display-future-font-rendering-web/?utm_source=frontendfocus&utm_medium=email

【讨论】:

这是 2020 年正确且首选的方法。不要因为用户连接不稳定而惩罚用户,但如果您真的必须使用font-display: block。绝对不要做 what Ryan Taylor suggested 和 base64-encode 任何事情;没有用户愿意下载 30% 以上的数据,这样您就可以阻止渲染直到下载完成,即使(不,尤其是,在移动连接上时)如果它“只是”一个问题例如300kB。【参考方案7】:

这取决于浏览器的行为方式。

首先你的@font 声明在哪里?它是嵌入到您的 HTML 中,在页面上的 CSS 表中声明,还是(希望)在外部 CSS 表中声明?

如果它不在外部工作表中,请尝试将其移至一张(这通常是更好的做法)。

如果这没有帮助,您需要问问自己,秒差的一小部分是否真的对用户体验有很大的不利影响?如果是,那么请考虑 JavaScript,您可能可以做一些事情,重定向、暂停等,但这些实际上可能比原来的问题更糟糕。对用户来说更糟,维护也更糟。

此链接可能会有所帮助:

http://paulirish.com/2009/fighting-the-font-face-fout/

【讨论】:

您能做的最好的事情是听从 Tom 的建议,另外,确保您的客户端在后续页面加载时不会访问 Web 服务器,因为这是一个真正的字体渲染阻止程序——设置一个过期时间CSS 文件的标头和实际字体资源的标头,一切顺利。 好文章。让我们希望 Firefox 在不远的版本中采用 safari 方法来加载字体。 FOUT 过去对我来说不是问题,但是对于我正在处理的当前站点,自定义字体与任何系统字体都非常不同,所以第一次闪光非常明显。跨度> 大字体(即 200 Kb)和慢速连接(即 3G)不是一秒钟的时间。对于那些使用带有 SIM 芯片的平板电脑的人来说,设计人员应该考虑这种情况。【参考方案8】:
(function() 
        document.getElementsByTagName("html")[0].setAttribute("class","wf-loading")
        document.getElementsByTagName("html")[0].setAttribute("className","wf-loading")
    )();

使用此方法.. 与 Webfont.js 一起使用

【讨论】:

【参考方案9】:

Joni Korpi 有一篇关于在页面其余部分之前加载字体的好文章。

http://jonikorpi.com/a-smoother-page-load/

他还使用 loading.gif 来缓解延迟,这样用户就不会感到沮丧。

【讨论】:

【参考方案10】:

只有 IE 会先加载字体,然后再加载页面的其余部分。 其他浏览器出于某种原因同时加载内容。想象一下,托管字体的服务器或字体下载存在问题。 在加载字体之前,您将挂起整个站点。在我看来,一闪而过的无样式文本总比根本看不到网站要好

【讨论】:

好吧,除非您在同一个网站上托管字体。 还有,除非字体是引导字形图标 还有,除非您需要知道字形的像素大小。 flash of unstyled text 使您的用户认为您(创建者)在您的工作中不专业,并且应用程序存在一些错误。这是 UX 人。【参考方案11】:

可能是这样的:

$("body").html("<img src='ajax-loader.gif' />");

然后当页面加载时,将正文的内容替换为实际内容,并希望完全呈现字体,但您可能不得不玩这个...

【讨论】:

以上是关于在渲染网页之前等待字体加载的主要内容,如果未能解决你的问题,请参考以下文章

由网页渲染提出的优化建议

浏览器加载渲染网页过程解析

页面加载缓慢的原因

图片利用 new Image()预加载原理

网页不会呈现指定的字体

自从用了这个属性,老板都夸我网页加载的更快