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


80

我使用的是Font-Awesome,但未加载字体文件时,图标显示为。

因此,我希望这些图标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;
}

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

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


希望我们很快能有本地字体事件blog.typekit.com/2013/02/05/more-reliable-font-events
Pacerier 2014年


2
这个问题是重复的,我已针对原始问题发布了2015年更新的答案
Dan Dascalescu 2015年

smnh.me/web-font-loading-detection-without-timers上有一种使用滚动检测的解决方案
Pacerier

Answers:


44

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

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

我将字体加载检测的方法重写为一个jQuery插件,该插件旨在使开发人员能够根据是否已加载字体来对元素进行样式设置。添加了故障安全计时器,因此,如果自定义字体加载失败,则不会为用户留下任何内容。那只是不好的可用性。

我还增加了对字体加载过程中发生的事情的控制,并通过添加和删除类来控制失败。现在,您可以对字体进行任何操作。我只建议修改字体大小,行距等,以使后备字体尽可能接近自定义字体,以使布局保持完整,并为用户带来预期的体验。

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

将以下内容放入.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);

将其应用于您的项目

.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'
    });

});

删除那个FOUC!

.hideMe {
    visibility: hidden !important;
}

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

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


比较字体长度...这是WebFont Loader(请参阅另一个答案)在做什么吗?
Pacerier '16

1
而且在任何情况下,比较字符长度都不适用于许多字体,因为许多“复制”字体被设计为具有相同的长度。例如,Arial,Helvetica和Liberation Sans的所有字符的字符宽度都相同。另请参阅en.wikipedia.org/wiki/Arial。到目前为止,似乎使用画布进行逐像素检查可能是唯一的防呆选择……..
Pacerier '16

1
我需要使用它来解决iScroll在加载字体之前计算元素大小错误时遇到的问题。但是我没有使用jQuery,所以制作了一个普通的js版本:github.com/keithswright/vanilla-fontspy似乎对我有用
基思2016年

@Pacerier-请注意,只有您选择的测试和您选择的加载字体需要具有不同的长度。因此,除非Comic Sans那里有很多字体具有与之相同的字符宽度,否则在大多数情况下仍然可以使用。
BryanGrezeszak '16

20

尝试使用由Google和Typekit开发的WebFont Loadergithub repo)。

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


9

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

我正在使用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();
};

由于它使用画布,因此需要相当现代的浏览器,但它可能也可以在IE8上与polyfill一起使用。


我在KonvaJS中加载字体真棒时遇到了同样的问题
Mahdi Alkhatib

4

实际上,有一种很好的方法可以理解所有字体是否开始下载或完全加载或加载不正确,并且会出现一些错误,但这不仅限于特定的字体,请注意以下代码:

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
};

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

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

谢谢。完全有效!但是我需要通过在某处放置使用该字体的<span>元素来触发字体加载。
tyt2y3

IE11不支持Windows,不幸的是IE11仍与Windows Server捆绑在一起
Guy Passy

@GuyPassy,我不知道什么是IE11!
AmerllicA

4
@AmerllicA我希望有一天能如此幸运
Guy Passy

3

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

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


0

尝试类似的东西

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

然后做

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

加载字体后,将触发load事件。


这与$(function(){...})加载整个页面时运行的相同。
Shankar Cabus 2012年

1
这是不一样的。hayk.mart的示例将在页面中的DOM(HTML)和资产(CSS,JS,图像,框架)加载完成时触发。仅当DOM完成加载时的示例。
布莱斯

好奇为什么这个答案不正确,快速搜索表明这是正确的方法,例如eager.io/blog/how-to-decide-when-you-code-should-run

-1

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

它是不可知论的,并且可以与任何字体样式资源(例如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="https://stackoverflow.com/css/font-awesome.min.css" rel="stylesheet">');
        }
        faSpan.remove();
    })(jQuery);
</script>

如果Font-Awesome无法加载(或者加载速度太慢!),但是在字体加载完成不会通知则这是一个后备方法。
Dan Dascalescu 2015年

@DanDascalescu我已经更新了答案,以更清楚地表明这是另一种方法,该方法只能确保加载真棒字体库,而不是完整的解决方案。希望这可以使它有所清除,因为我从上一次迭代中获得了一些支持。
oucil

-3

使用以下代码:

<!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>

可以检查许多字体:

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

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

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

里夫特在一年前发表了同样的“渲染到画布”的想法。
Dan Dascalescu 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.