我试图在<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文件,但也意味着当用户第一次下载web字体时,他们将看到上面所示的未样式文本。
在理想的情况下,我可以将回调函数附加到字体的自动下载上,但似乎无法访问浏览器行为的这一部分。
我现在的解决办法
我修改了一个关于网页字体预加载的老问题的解决方案--它很麻烦,但它确实有效。
简而言之,它创建一个带有默认字体文本的<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>元素,例如使用setInterval。
我觉得一定有一种更干净、更优雅的方法来做到这一点。有人能提出建议吗?
发布于 2020-12-02 06:16:57
在理想的情况下,我可以将回调函数附加到字体的自动下载上。
我还不认为这是一个理想的世界,但你可以这样做。
当页面上呈现可见文本所需的所有字体都已加载时,document.fonts.ready承诺将解决此问题。
在离这里不远的地方,您可以迭代保存已声明的所有FontFaces的FontFaces,并检查它们是否已加载,如果需要,还可以检查它们定义的unicode-范围。
document.fonts.ready.then( () => {
const loaded_fonts = [ ...document.fonts ]
// simplify the objects for logging here
.map( ({unicodeRange, status}, index) => ({ unicodeRange, status, index }) )
.filter( ({status}) => status === "loaded" );
console.log( loaded_fonts );
});/* cyrillic-ext index:0 */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic index:1*/
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu5mxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext index:2*/
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7mxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek index:3*/
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4WxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese index:4 - should be loaded */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7WxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext index:5 - should be loaded */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin index:6 - should be loaded */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
body {
font-family: "Roboto";
}Hello thế giới
如果您需要在画布上使用特定的FontFaces之前加载它,则可以调用FontFaces,它将加载呈现the_text_to_render所需的所有FontFaces。
( async () => {
// <DEMO only>
// just to be sure the font was not loaded yet
await document.fonts.ready;
logLoadedFontsCount( "after document.fonts ready" );
// </DEMO only>
// now try to draw using that font face anyway
const canvas = document.querySelector( "canvas" );
const ctx = canvas.getContext( "2d" );
const font_shorthand = "30px Roboto";
const text = "Привет мир";
// force loading fonts
await document.fonts.load( font_shorthand, text );
// now we can use it
ctx.font = font_shorthand;
ctx.fillText( text, 30, 50 );
// <DEMO only>
logLoadedFontsCount( "after loading of customs fonts" );
// </DEMO only>
} )();
// <DEMO only>
// logs how many FontFaces are currently loaded
function logLoadedFontsCount( when = "" ) {
const loaded_fonts = [ ...document.fonts ]
.filter( ({status}) => status === "loaded" );
console.log( "%s fonts loaded %s", loaded_fonts.length, when );
}
// </DEMO only>/* cyrillic-ext index:0 */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu72xKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic index:1*/
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu5mxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext index:2*/
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7mxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek index:3*/
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4WxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0370-03FF;
}
/* vietnamese index:4 - should be loaded */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7WxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext index:5 - should be loaded */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu7GxKKTU1Kvnz.woff2) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin index:6 - should be loaded */
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(https://fonts.gstatic.com/s/roboto/v20/KFOmCnqEu92Fr1Mu4mxKKTU1Kg.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}<canvas></canvas>
https://stackoverflow.com/questions/65095157
复制相似问题