Google的Web字体API提供了一种定义回调函数的方式,以便在字体加载完成、无法加载等情况下执行。是否有一种类似的方法可以使用CSS3 web字体(@font-face)实现?
Google的Web字体API提供了一种定义回调函数的方式,以便在字体加载完成、无法加载等情况下执行。是否有一种类似的方法可以使用CSS3 web字体(@font-face)实现?
Chrome 35+ 和 Firefox 41+ 实现了 CSS 字体加载 API (MDN, W3C)。调用 document.fonts
获取一个 FontFaceSet 对象,该对象具有一些有用的 API 以检测字体的加载状态:
check(fontSpec)
- 返回给定字体列表中所有字体是否已加载并可用。 fontSpec
使用 CSS 字体的简写语法。document.fonts.check('bold 16px Roboto'); // true 或 false
document.fonts.ready
- 返回一个 Promise,表示字体加载和布局操作已完成。document.fonts.ready.then(function () { /*... 所有字体都加载完成 ...*/ });
这里是一个片段,展示了这些 API,以及 document.fonts.onloadingdone
,该 API 提供了关于字体的额外信息。
alert('Roboto loaded? ' + document.fonts.check('1em Roboto')); // false
document.fonts.ready.then(function () {
alert('All fonts in use by visible text have loaded.');
alert('Roboto loaded? ' + document.fonts.check('1em Roboto')); // true
});
document.fonts.onloadingdone = function (fontFaceSetEvent) {
alert('onloadingdone we have ' + fontFaceSetEvent.fontfaces.length + ' font faces loaded');
};
<link href='https://fonts.googleapis.com/css?family=Roboto:400,700' rel='stylesheet' type='text/css'>
<p style="font-family: Roboto">
We need some text using the font, for the font to be loaded.
So far one font face was loaded.
Let's add some <strong>strong</strong> text to trigger loading the second one,
with weight: 700.
</p>
IE11不支持此API。如果需要支持IE,请查看可用的polyfills或支持库:
check
函数总是返回true
:document.fonts.check('1em NoThisReallyDoesntExist')
。根据MDN文档,这似乎是一个非常试验性和不完整的功能。但在Chromium中似乎可以正常工作。 - Martin Tournoijonloadingdone
。.ready
总是立即触发。 - Jason在Safari、Chrome、Firefox、Opera、IE7、IE8、IE9中测试通过:
function waitForWebfonts(fonts, callback) {
var loadedFonts = 0;
for(var i = 0, l = fonts.length; i < l; ++i) {
(function(font) {
var node = document.createElement('span');
// Characters that vary significantly among different fonts
node.innerHTML = 'giItT1WQy@!-/#';
// Visible - so we can measure it - but not on the screen
node.style.position = 'absolute';
node.style.left = '-10000px';
node.style.top = '-10000px';
// Large font size makes even subtle changes obvious
node.style.fontSize = '300px';
// Reset any font properties
node.style.fontFamily = 'sans-serif';
node.style.fontVariant = 'normal';
node.style.fontStyle = 'normal';
node.style.fontWeight = 'normal';
node.style.letterSpacing = '0';
document.body.appendChild(node);
// Remember width with no applied web font
var width = node.offsetWidth;
node.style.fontFamily = font;
var interval;
function checkFont() {
// Compare current width with original width
if(node && node.offsetWidth != width) {
++loadedFonts;
node.parentNode.removeChild(node);
node = null;
}
// If all fonts have been loaded
if(loadedFonts >= fonts.length) {
if(interval) {
clearInterval(interval);
}
if(loadedFonts == fonts.length) {
callback();
return true;
}
}
};
if(!checkFont()) {
interval = setInterval(checkFont, 50);
}
})(fonts[i]);
}
};
使用方法如下:
waitForWebfonts(['MyFont1', 'MyFont2'], function() {
// Will be called as soon as ALL specified fonts are available
});
waitForWebfonts(['"Vectora W01 56 Italic"'],...);
- Lukxsans-serif
,但是如果您请求一个serif
字体:"Roboto Slab",serif
,浏览器会在等待Roboto Slab加载时自然切换到默认的serif
字体,从而提前触发测量。我认为修复这个问题的最佳方法是找出所请求的字体的回退系列,并在创建节点时使用该系列,以便只有在加载最终字体后才会发生交换。 - Tony BogdanovGoogle Web Fonts API (以及 Typekit) 使用的 JS 库可以在不使用该服务的情况下使用:WebFont Loader。
这个库定义了您所请求的回调函数以及更多其他内容。
2017年更新
JS库FontFaceObserver绝对是2017年最好的、最轻量级、跨浏览器的解决方案。它还暴露了一个基于Promise的.load()
接口。
var observe_fa = new FontFaceObserver('FontAwesome'); observe_fa.load('<SOME TEST ICON SYMBOLS GO HERE>').then(...)
。 - vintproxdocument.fonts.ready
字段是不可靠的。我发现唯一可靠的跨浏览器方式(现代浏览器)是反复检查document.fonts.status === 'loaded'
。以下是一个带有指数退避的示例: const waitForFontsLoaded = document.fonts?.ready.then(() => {
if (document.fonts?.status === 'loaded') {
return null;
}
console.warn('Browser reported fonts ready but something is still loading...');
return new Promise((resolve) => {
let waitTimeMs = 5;
const checkFontsLoaded = () => {
if (document.fonts?.status === 'loaded') {
return resolve();
}
waitTimeMs *= 2;
return setTimeout(checkFontsLoaded, waitTimeMs);
};
setTimeout(checkFontsLoaded, 5);
});
});
await waitForFontsLoaded
const fontName = "Fira Sans Condensed",
maxTime = 2500 // 2.5s
// EXAMPLE 1
fontOnload(fontName).then(() => {
console.log("success")
})
// EXAMPLE 2
fontOnload(fontName, maxTime).then(() => {
console.log("success")
}).catch(() => {
console.log("timeout")
})
async function fontOnload(fontName, maxTime = Infinity, timeInterval = 10) {
const startTime = performance.now()
return new Promise((resolve, reject) => {
setInterval(() => {
const currentTime = performance.now(),
elapsedTime = currentTime - startTime
if (document.fonts.check("12px " + fontName)) {
resolve(true)
} else if (elapsedTime >= maxTime) {
reject(false)
}
}, timeInterval)
})
}
方法二
const fontName = "Fira Sans Condensed",
maxTime = 2500 // 2.5s
// EXAMPLE 1
fontOnloadDOM(fontName).then(() => {
console.log("success")
})
// EXAMPLE 2
fontOnloadDOM(fontName, maxTime).then(() => {
console.log("success")
}).catch(() => {
console.log("timeout")
})
async function fontOnloadDOM(fontName, maxTime = Infinity, timeInterval = 10) {
return new Promise((resolve, reject) => {
const startTime = performance.now(),
abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
mainStyle = "font-size:24px!important;display:inline!important;font-family:",
body = document.body,
container = document.createElement("div"),
span1 = document.createElement("span"),
span2 = document.createElement("span")
container.classList.add("font-on-load")
container.setAttribute("style", "display:block!important;position:absolute!important;top:-9999px!important;left:-9999px!important;opacity:0!important;")
span1.setAttribute("style", mainStyle + "sans-serif!important;")
span2.setAttribute("style", mainStyle + "\"" + fontName + "\",sans-serif!important;")
span1.innerText = abc.repeat(3)
span2.innerText = abc.repeat(3)
container.append(span1, span2)
body.append(container)
const interval = setInterval(() => {
const currentTime = performance.now(),
elapsedTime = currentTime - startTime,
width1 = span1.clientWidth || span1.getBoundingClientRect().width,
width2 = span1.clientWidth || span2.getBoundingClientRect().width,
diffWidths = Math.abs(width1 - width2)
if (diffWidths > 9) {
clearInterval(interval)
resolve(true)
} else if (elapsedTime >= maxTime) {
clearInterval(interval)
reject(false)
}
}, timeInterval)
})
}
window.load事件将在所有内容加载完成后触发,包括字体。因此,您可以将其用作回调。但是,如果您决定使用Web字体加载器,则不必这样做。
除了Google、Typekit、Ascender和Monotype选项之外,还有一个自定义模块,可以从任何Web字体提供商加载样式表。
WebFontConfig = { custom: { families: ['OneFont', 'AnotherFont'], urls: [ 'http://myotherwebfontprovider.com/stylesheet1.css', 'http://yetanotherwebfontprovider.com/stylesheet2.css' ] } };
无论您指定哪个提供程序,该库都会发送相同的事件。
load
事件可能在网页字体加载完成前就已经触发了。 - Tim Down