我正在尝试在<canvas>
元素上显示自定义 Web 字体,但是由于尚未预加载字体,它并不总是第一次正确显示:
目前的外观:
这是它的外观:
我知道存在几种预加载网络字体的方法,但不幸的是它们不适用于这种情况,因为页面最多可以使用90种字体。
大数字的原因是因为我使用的中文字体已被分割成许多单独.woff2
的文件,以便我的 CSS 文件可以unicode-range
像这样使用描述符:
/* unicode range [1] */
@font-face {
font-family: 'ma-shan-zheng';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Ma Shan Zheng Regular'), local('MaShanZheng-Regular'), url(ma-shan-zheng.5.woff2) format('woff2');
unicode-range: U+fee3, U+fef3, U+ff03-ff04, U+ff07, U+ff0a, U+ff17-ff19, U+ff1c-ff1d, U+ff20-ff3a, U+ff3c, U+ff3e-ff5b, U+ff5d, U+ff61-ff65, U+ff67-ff6a, U+ff6c, U+ff6f-ff78, U+ff7a-ff7d, U+ff80-ff84, U+ff86, U+ff89-ff8e, U+ff92, U+ff97-ff9b, U+ff9d-ff9f, U+ffe0-ffe4, U+ffe6, U+ffe9, U+ffeb, U+ffed, U+fffc, U+1f004, U+1f170-1f171, U+1f192-1f195, U+1f198-1f19a, U+1f1e6-1f1e8;
}
/* unicode range [2] */
@font-face {
font-family: 'ma-shan-zheng';
font-style: normal;
font-weight: 400;
font-display: swap;
src: local('Ma Shan Zheng Regular'), local('MaShanZheng-Regular'), url(ma-shan-zheng.6.woff2) format('woff2');
unicode-range: U+f0a7, U+f0b2, U+f0b7, U+f0c9, U+f0d8, U+f0da, U+f0dc-f0dd, U+f0e0, U+f0e6, U+f0eb, U+f0fc, U+f101, U+f104-f105, U+f107, U+f10b, U+f11b, U+f14b, U+f18a, U+f193, U+f1d6-f1d7, U+f244, U+f27a, U+f296, U+f2ae, U+f471, U+f4b3, U+f610-f611, U+f880-f881, U+f8ec, U+f8f5, U+f8ff, U+f901, U+f90a, U+f92c-f92d, U+f934, U+f937, U+f941, U+f965, U+f967, U+f969, U+f96b, U+f96f, U+f974, U+f978-f979, U+f97e, U+f981, U+f98a, U+f98e, U+f997, U+f99c, U+f9b2, U+f9b5, U+f9ba, U+f9be, U+f9ca, U+f9d0-f9d1, U+f9dd, U+f9e0-f9e1, U+f9e4, U+f9f7, U+fa00-fa01, U+fa08, U+fa0a, U+fa11, U+fb01-fb02, U+fdfc, U+fe0e, U+fe30-fe31, U+fe33-fe44, U+fe49-fe52, U+fe54-fe57, U+fe59-fe66, U+fe68-fe6b, U+fe8e, U+fe92-fe93, U+feae, U+feb8, U+fecb-fecc, U+fee0;
}
/* etc. all the way up to [90] */
这具有仅在需要时才下载相关文件的明显好处.woff2
,但这也意味着当用户第一次下载网络字体时,他们将看到如上所示的无样式文本。
在理想情况下,我可以将回调函数附加到字体的自动下载中,但似乎无法访问浏览器行为的这一部分。
我目前的解决方法
我已经从一个关于网络字体预加载的旧 SO 问题中修改了一个解决方案——它非常 hacky,但它确实有效。
简而言之,它会创建一个<span>
元素,其中包含一些默认字体的文本,测量宽度/高度,将元素字体设置为 Web 字体,再次测量大小并比较结果。如果大小发生了变化,则假定 Web 字体已加载:
function waitFontLoaded(font, phrase, callback) {
var node = document.createElement("span");
// Set node content to the desired phrase/text
node.innerHTML = phrase;
// 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 size with no applied web font
var width = node.offsetWidth;
var height = node.offsetHeight;
node.style.fontFamily = font + ", sans-serif";
var interval;
// Compare current size with original size
function checkFont() {
if (node && (node.offsetWidth !== width || node.offsetHeight !== height)) {
node.parentNode.removeChild(node);
node = null;
clearInterval(interval);
callback();
return true;
}
return false;
}
if (!checkFont()) {
interval = setInterval(checkFont, 50);
}
}
正如我所说,这确实有效,但显然不是一个可靠的解决方案,因为两个字符在默认系统字体和 Web 字体中的大小相同并非不可能。
<canvas>
另一个非常 hacky 的解决方案是每秒简单地刷新一次元素,例如使用setInterval
.
我觉得必须有一种更清洁、更优雅的方式来做到这一点。任何人都可以提供任何建议吗?