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

我正在使用 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) ,因为字体可以在整个页面之前加载。

79185 次浏览

比如说

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

然后去做

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

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

试试由 Google 和 Typekit 开发的 WebFont Loader(Github 回购)。

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

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

本质上,该方法通过比较两种不同字体的字符串宽度来工作。我们使用 Comic Sans 作为测试字体,因为它是最不同的网络安全字体,并希望不同的足够任何自定义字体你将使用。此外,我们正在使用一个非常大的字体大小,所以即使很小的差异将是明显的。计算 Comic Sans 字符串的宽度后,font-family 将更改为自定义字体,并退回到 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 的兼容性被删除,因为它不能正常工作,并且遇到了不同版本的问题。这里可以找到一个 hacky 修复程序: https://github.com/patrickmarabeas/jQuery-FontFaceSpy.js/issues/1

这里有另外一种方法可以知道@font-face 是否已经被加载,而不需要使用计时器: 当一个精心制作的元素的大小发生变化时,利用一个“滚动”事件来接收一个即时事件。

我写了一个 博客文章关于它是如何做的,并已发表了 Github 上的图书馆

这里有一个不同于其他解决方案的方法。

我正在使用 FontAwesome 4.1.0来构建 WebGL 纹理。这给了我一个想法,使用一个小画布渲染面积,然后检查一个像素在该画布,以测试它是否已加载:

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。

这是一种替代方法,至少可以确保加载 font-Awesome,没有是 OP 的完整解决方案。原始代码发现在 wordpress 论坛在这里 https://wordpress.stackexchange.com/a/165358/40636

它是不可知的,并将与任何字体样式的资源,如字体可怕的字体家族可以检查。再仔细想想,我敢打赌这个可以应用到更多的..。

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

使用以下代码:

<!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 ");

事实上,有一个很好的方法来理解 所有字体开始下载或加载完全或没有,并陷入一些错误,但它不仅仅适用于特定的字体,注意以下代码:

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'))

或者,可以将 font-display: block添加到@font-face 声明中。

这指示浏览器在加载字体之前将备用字体呈现为不可见,不需要 display: none或任何 javascript 加载字体检测

打字稿解决方案,角度。

如果你使用的是 Angular,你可以使用这个 模组来检查字体。

// document.fonts.check extension
import type {} from 'css-font-loading-module';


ngOnInit() {
this.onFontLoad();
}


public onFontLoad() {
let myTimer = setInterval(() => {
if (document.fonts.check('14px MyFont')) {
console.log('Font is loaded!');
clearInterval(myTimer);
} else {
console.log('Font is loading');
}
}, 1);
}

此外,有些字体非常沉重。因此,可以在加载字体时添加加载屏幕,在加载字体时删除加载屏幕。我相信这是一个更好的方法,而不是改变您的 CSS 类 display: none,仅仅因为它可能需要3-4秒以上下载一些字体,如果用户有缓慢的互联网。