Javascript 如何知道字体(@font-face)是否已经加载?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12312323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:41:48  来源:igfitidea点击:

How to know if a font (@font-face) has already been loaded?

javascriptjquerycssfont-facefont-awesome

提问by Shankar Cabus

I'm using Font-Awesome, but while the font files are not loaded, the icons appear with ?.

我正在使用 Font-Awesome,但在未加载字体文件时,图标显示为 ?。

So, I want these icons to have display:nonewhile files are not loaded.

因此,我希望display:none在未加载文件时具有这些图标。

@font-face {
  font-family: "FontAwesome";
  src: url('../font/fontawesome-webfont.eot');
  src: url('../font/fontawesome-webfont.eot?#iefix') format('eot'), url('../font/fontawesome-webfont.woff') format('woff'), url('../font/fontawesome-webfont.ttf') format('truetype'), url('../font/fontawesome-webfont.svg#FontAwesome') format('svg');
  font-weight: normal;
  font-style: normal;
}

How do I know that these files have been loaded and I'm finally able to show the icons?

我怎么知道这些文件已经加载,我终于可以显示图标了?

Edit:I'm not talking when the page is loaded (onload), because the font could be loaded before the whole page.

编辑:我不是在页面加载(onload)时说话,因为字体可以在整个页面之前加载。

采纳答案by Patrick

Now on GitHub: https://github.com/patrickmarabeas/jQuery-FontSpy.js

现在在 GitHub 上:https: //github.com/patrickmarabeas/jQuery-FontSpy.js

Essentially the method works by comparing the width of a string in two different fonts. We are using Comic Sans as the font to test against, because it is the most different of the web safe fonts and hopefully different enough to any custom font you will be using. Additionally we are using a very large font-size so even small differences will be apparent. When the width of the Comic Sans string has been calculated, the font-family is changed to your custom font, with a fallback to Comic Sans. When checked, if the string element width is the same, the fallback font of Comic Sans is still in use. If not, your font should be operational.

本质上,该方法的工作原理是比较两种不同字体中字符串的宽度。我们使用 Comic Sans 作为测试字体,因为它是最不同的网络安全字体,希望与您将使用的任何自定义字体有足够的不同。此外,我们使用了非常大的字体大小,因此即使是很小的差异也会很明显。计算 Comic Sans 字符串的宽度后,字体系列将更改为您的自定义字体,并回退到 Comic Sans。勾选时,如果字符串元素宽度相同,则仍然使用Comic Sans的fallback字体。如果没有,您的字体应该是可操作的。

I rewrote the method of font load detection into a jQuery plugin designed to give the developer the ability to style elements based upon whether the font has been loaded or not. A fail safe timer has been added so the user isn't left without content if the custom font fails to load. That's just bad usability.

我将字体加载检测的方法重写为一个 jQuery 插件,旨在让开发人员能够根据字体是否已加载来设置元素样式。添加了故障安全计时器,因此如果自定义字体无法加载,用户不会没有内容。这只是糟糕的可用性。

I have also added greater control over what happens during font loading and on fail with the inclusion of classes addition and removal. You can now do whatever you like to the font. I would only recommend modifying the fonts size, line spacing, etc to get your fall back font as close to the custom as possible so your layout stays intact, and users get an expected experience.

我还通过包含类的添加和删除,对字体加载期间和失败时发生的事情进行了更好的控制。您现在可以对字体执行任何您喜欢的操作。我只建议修改字体大小、行距等,以使您的回退字体尽可能接近自定义字体,从而使您的布局保持完整,并且用户可以获得预期的体验。

Here's a demo: http://patrickmarabeas.github.io/jQuery-FontSpy.js

这是一个演示:http: //patrickmarabeas.github.io/jQuery-FontSpy.js

Throw the following into a .js file and reference it.

将以下内容放入 .js 文件并引用它。

(function($) {

    $.fontSpy = function( element, conf ) {
        var $element = $(element);
        var defaults = {
            font: $element.css("font-family"),
            onLoad: '',
            onFail: '',
            testFont: 'Comic Sans MS',
            testString: 'QW@HhsXJ',
            delay: 50,
            timeOut: 2500
        };
        var config = $.extend( defaults, conf );
        var tester = document.createElement('span');
            tester.style.position = 'absolute';
            tester.style.top = '-9999px';
            tester.style.left = '-9999px';
            tester.style.visibility = 'hidden';
            tester.style.fontFamily = config.testFont;
            tester.style.fontSize = '250px';
            tester.innerHTML = config.testString;
        document.body.appendChild(tester);
        var fallbackFontWidth = tester.offsetWidth;
        tester.style.fontFamily = config.font + ',' + config.testFont;
        function checkFont() {
            var loadedFontWidth = tester.offsetWidth;
            if (fallbackFontWidth === loadedFontWidth){
                if(config.timeOut < 0) {
                    $element.removeClass(config.onLoad);
                    $element.addClass(config.onFail);
                    console.log('failure');
                }
                else {
                    $element.addClass(config.onLoad);
                    setTimeout(checkFont, config.delay);
                    config.timeOut = config.timeOut - config.delay;
                }
            }
            else {
                $element.removeClass(config.onLoad);
            }
        }
        checkFont();
    };

    $.fn.fontSpy = function(config) {
        return this.each(function() {
            if (undefined == $(this).data('fontSpy')) {
                var plugin = new $.fontSpy(this, config);
                $(this).data('fontSpy', plugin);
            }
        });
    };

})(jQuery);

Apply it to your project

将其应用于您的项目

.bannerTextChecked {
        font-family: "Lobster";
        /* don't specify fallback font here, do this in onFail class */
}

$(document).ready(function() {

    $('.bannerTextChecked').fontSpy({
        onLoad: 'hideMe',
        onFail: 'fontFail anotherClass'
    });

});

Remove that FOUC!

移除那个 FOUC!

.hideMe {
    visibility: hidden !important;
}

.fontFail {
    visibility: visible !important;
    /* fall back font */
    /* necessary styling so fallback font doesn't break your layout */
}

EDIT: FontAwesome compatibility removed as it didn't work properly and ran into issues with different versions. A hacky fix can be found here: https://github.com/patrickmarabeas/jQuery-FontFaceSpy.js/issues/1

编辑:FontAwesome 兼容性已被删除,因为它无法正常工作并遇到不同版本的问题。可以在这里找到一个 hacky 修复:https: //github.com/patrickmarabeas/jQuery-FontFaceSpy.js/issues/1

回答by cassi.lup

Try WebFont Loader(github repo), developed by Google and Typekit.

试试由 Google 和 Typekit 开发的WebFont Loader( github repo)。

This examplefirst displays the text in the default serif font; then after the fonts have loaded it displays the text in the specified font. (This code reproduces Firefox's default behavior in all other modern browsers.)

本示例首先以默认衬线字体显示文本;然后在字体加载后,它以指定的字体显示文本。(此代码在所有其他现代浏览器中重现了 Firefox 的默认行为。)

回答by Leeft

Here is a different approach to the solutions from others.

这是解决其他人的不同方法。

I'm using FontAwesome 4.1.0 to build WebGL textures. That gave me the idea to use a tiny canvas to render a fa-square to, then check a pixel in that canvas to test whether it has loaded:

我正在使用 FontAwesome 4.1.0 来构建 WebGL 纹理。这让我想到使用一个小画布来渲染一个 fa-square,然后检查该画布中的一个像素以测试它是否已加载:

function waitForFontAwesome( callback ) {
   var retries = 5;

   var checkReady = function() {
      var canvas, context;
      retries -= 1;
      canvas = document.createElement('canvas');
      canvas.width = 20;
      canvas.height = 20;
      context = canvas.getContext('2d');
      context.fillStyle = 'rgba(0,0,0,1.0)';
      context.fillRect( 0, 0, 20, 20 );
      context.font = '16pt FontAwesome';
      context.textAlign = 'center';
      context.fillStyle = 'rgba(255,255,255,1.0)';
      context.fillText( '\uf0c8', 10, 18 );
      var data = context.getImageData( 2, 10, 1, 1 ).data;
      if ( data[0] !== 255 && data[1] !== 255 && data[2] !== 255 ) {
         console.log( "FontAwesome is not yet available, retrying ..." );
         if ( retries > 0 ) {
            setTimeout( checkReady, 200 );
         }
      } else {
         console.log( "FontAwesome is loaded" );
         if ( typeof callback === 'function' ) {
            callback();
         }
      }
   }

   checkReady();
};

As it uses a canvas it requires a fairly modern browser, but it might work on IE8 as well with the polyfill.

因为它使用画布,所以它需要一个相当现代的浏览器,但它可能也适用于 IE8 以及 polyfill。

回答by smnh

Here's another way of knowing if a @font-face has already been loaded without having to use timers at all: utilize a "scroll" event to receive an instantaneous event when the size of a carefully crafted element is changed.

这是了解@font-face 是否已经加载而根本无需使用计时器的另一种方法:当精心设计的元素的大小发生变化时,利用“滚动”事件接收瞬时事件。

I wrote a blog postabout how it's done and have published the library on Github.

我写了一篇关于它是如何完成的博客文章,并在 Github 上发布了该

回答by AmerllicA

Actually, there is a good way to understand all fontsbegin to download or loaded completely or not and fall into some errors, but it is not just for a specific font, pay attention to the following code:

实际上,有一个很好的方法可以了解所有字体是否开始下载或加载完成并陷入某些错误,但不仅仅是针对特定字体,请注意以下代码:

document.fonts.onloading = () => {
  // do someting when fonts begin to download
};
document.fonts.onloadingdone = () => {
  // do someting when fonts are loaded completely
};
document.fonts.onloading = () => {
  // do someting when fonts fall into some error
};

And also there is an option that returns Promiseand it could handle with .thenfunction:

还有一个返回的选项,Promise它可以处理.then函数:

document.fonts.ready
 .then(() => console.log('do someting at the final with each status'))

回答by hayk.mart

Try something like

尝试类似的东西

$(window).bind("load", function() {
       $('#text').addClass('shown');
});

and then do

然后做

#text {visibility: hidden;}
#text.shown {visibility: visible;}

The load event should fire after the fonts are loaded.

load 事件应该在字体加载后触发。

回答by oucil

This is an alternate approach that will at least ensure that font-awesome is loaded, NOTa complete solution to the OP. Original code found in the wordpress forums here https://wordpress.stackexchange.com/a/165358/40636.

这是一种替代方法,至少可以确保加载 font-awesome,而不是OP 的完整解决方案。在此处https://wordpress.stackexchange.com/a/165358/40636的 wordpress 论坛中找到的原始代码。

It's agnostic and will work with any font style resource like font-awesome where a font-family can be checked. With a little more thought I bet this could be applied to much more...

它是不可知的,可以与任何字体样式资源一起使用,例如 font-awesome 可以检查字体系列。再多考虑一下,我敢打赌这可以应用于更多……

<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<script>
    (function($){
        var faSpan = $('<span class="fa" style="display:none"></span>').appendTo('body');
        if (faSpan .css('fontFamily') !== 'FontAwesome' ) {
            // Fallback Link
            $('head').append('<link href="/css/font-awesome.min.css" rel="stylesheet">');
        }
        faSpan.remove();
    })(jQuery);
</script>

回答by ali bagheri

Use the below code:

使用以下代码:

<!DOCTYPE HTML>
<html>
    <head>
    </head>

<body>
<canvas id="canvasFont" width="40px" height="40px" style="position: absolute; display: none;"></canvas>

<script>
function IsLoadedFonts()
    {
        var Args = arguments;
        var obj = document.getElementById('canvasFont');
        var ctx = obj.getContext("2d");
        var baseFont = (/chrome/i.test(navigator.userAgent))?'tims new roman':'arial';
         //................
          function getImg(fon)
          { 
            ctx.clearRect(0, 0, (obj).width, (obj).height);
            ctx.fillStyle = 'rgba(0,0,0,1.0)';
            ctx.fillRect( 0, 0, 40, 40 );
            ctx.font = '20px '+ fon;
            ctx.textBaseline = "top";
            ctx.fillStyle = 'rgba(255,255,255,1.0)';
            ctx.fillText( '\u0630', 18, 5 );
            return ctx.getImageData( 0, 0, 40, 40 );
          };
        //..............
          for(var i1=0; i1<Args.length; i1++)
          {
            data1 = getImg(Args[i1]);
            data2 = getImg(baseFont);
            var isLoaded = false;
            //...........
            for (var i=0; i<data1.data.length; i++)
            {
                if(data1.data[i] != data2.data[i])
                    {isLoaded = true; break;}
            }
            //..........
            if(!isLoaded)
                    return false;
         }
         return true;
    };

     setTimeout(function(){alert(IsLoadedFonts('myfont'));},100);
   </script>
   </body>

Can check many fonts:

可以检查多种字体:

setTimeout(function(){alert(IsLoadedFonts('font1','font2','font3'));},100);


The below code works in opera only but is easy:

以下代码仅适用于歌剧,但很简单:

if(!document.defaultView.getComputedStyle(document.getElementById('mydiv'))['fontFamily'].match(/myfont/i))
          alert("font do not loaded ");