第一次,用@font-face 将文本绘制到 < 画布 > 不起作用

当我用@font-face 加载的字体在画布上绘制文本时,文本不能正确显示。它根本没有显示(在 Chrome13和 Firefox5中) ,或者字体是错误的(Opera 11)。这种类型的意外行为仅在第一次使用该字体绘制时发生。之后一切都会好起来的。

这是标准行为还是什么?

谢谢你。

PS: 以下是测试用例的源代码

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>@font-face and &lt;canvas&gt;</title>
<style id="css">
@font-face {
font-family: 'Press Start 2P';
src: url('fonts/PressStart2P.ttf');
}
</style>
<style>
canvas, pre {
border: 1px solid black;
padding: 0 1em;
}
</style>
</head>
<body>
<h1>@font-face and &lt;canvas&gt;</h1>
<p>
Description: click the button several times, and you will see the problem.
The first line won't show at all, or with a wrong typeface even if it does.
<strong>If you have visited this page before, you may have to refresh (or reload) it.</strong>
</p>
<p>
<button id="draw">#draw</button>
</p>
<p>
<canvas width="250" height="250">
Your browser does not support the CANVAS element.
Try the latest Firefox, Google Chrome, Safari or Opera.
</canvas>
</p>
<h2>@font-face</h2>
<pre id="view-css"></pre>
<h2>Script</h2>
<pre id="view-script"></pre>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script id="script">
var x = 30,
y = 10;


$('#draw').click(function () {
var canvas = $('canvas')[0],
ctx = canvas.getContext('2d');
ctx.font = '12px "Press Start 2P"';
ctx.fillStyle = '#000';
ctx.fillText('Hello, world!', x, y += 20);
ctx.fillRect(x - 20, y - 10, 10, 10);
});
</script>
<script>
$('#view-css').text($('#css').text());
$('#view-script').text($('#script').text());
</script>
</body>
</html>
88556 次浏览

在画布上绘制必须发生,并在调用 fillText方法时立即返回。但是,浏览器尚未从网络加载字体,这是一个后台任务。因此,它必须回落到它的字体 是的有可用的。

如果你想确保字体是可用的,在页面上预加载一些其他元素,例如:

<div style="font-family: PressStart;">.</div>

我最近玩 http://people.opera.com/patrickl/experiments/canvas/scroller/的时候碰到了这个问题

通过直接在 CSS 中将 font-family 添加到画布来解决这个问题,因此您可以直接添加

{ font-family: PressStart; }

The nub of the problem is that you are trying to use the font but the browser has not loaded it yet and possibly has not even requested it. What you need is something that will load the font and give you a callback once it is loaded; once you get the callback, you know it is okay to use the font.

看看 Google 的 WebFont Loader,它看起来像一个“自定义”的提供程序和加载后的 active回调函数。

我以前从来没有用过它,但是通过快速扫描文档,你需要制作一个 css 文件 fonts/pressstart2p.css,像这样:

@font-face {
font-family: 'Press Start 2P';
font-style: normal;
font-weight: normal;
src: local('Press Start 2P'), url('http://lemon-factory.net/reproduce/fonts/Press Start 2P.ttf') format('ttf');
}

然后添加以下 JS:

  WebFontConfig = {
custom: { families: ['Press Start 2P'],
urls: [ 'http://lemon-factory.net/reproduce/fonts/pressstart2p.css']},
active: function() {
/* code to execute once all font families are loaded */
console.log(" I sure hope my font is loaded now. ");
}
};
(function() {
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();

首先使用 Google 的 Web Font 加载程序,正如在另一个答案中建议的那样,并将绘图代码添加到它提供的回调中,以指示字体已经加载。然而,这并不是故事的结局。从这一点来看,它非常依赖于浏览器。大多数情况下,它都能正常工作,但有时可能需要等待几百毫秒,或者在页面的其他地方使用字体。我尝试了不同的选项,其中一个方法,afaik 总是工作的是快速绘制一些测试消息在画布与字体家族和字体大小组合,你要使用。你可以使用和背景相同的颜色,所以它们甚至不会被看到,而且会发生得很快。在那之后,字体对我和所有浏览器都适用。

Use 这个把戏 and bind an onerror event to an Image element.

Demo 给你: works on the latest Chrome.

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');


var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = 'http://fonts.googleapis.com/css?family=Vast+Shadow';
document.getElementsByTagName('head')[0].appendChild(link);


// Trick from https://stackoverflow.com/questions/2635814/
var image = new Image();
image.src = link.href;
image.onerror = function() {
ctx.font = '50px "Vast Shadow"';
ctx.textBaseline = 'top';
ctx.fillText('Hello!', 20, 10);
};

面临着同样的问题。在阅读了“ bobince”和其他评论之后,我使用了以下 javascript 来解决它:

$('body').append("<div id='loadfont' style='font-family: myfont;'>.</div>");
$('#loadfont').remove();

我不确定这是否对你有帮助,但是为了解决我的代码的问题,我只是在 Javascript 的顶部创建了一个 for 循环,它遍历了我想加载的所有字体。然后,我运行一个函数来清除画布并预加载我想在画布上的项目。到目前为止,一切都很顺利。这就是我的逻辑,我把我的代码发布在下面:

var fontLibrary = ["Acme","Aladin","Amarante","Belgrano","CantoraOne","Capriola","CevicheOne","Chango","ChelaOne","CherryCreamSoda",
"ConcertOne","Condiment","Damion","Devonshire","FugazOne","GermaniaOne","GorditasBold","GorditasRegular",
"KaushanScript","LeckerliOne","Lemon","LilitaOne","LuckiestGuy","Molle","MrDafoe","MrsSheppards",
"Norican","OriginalSurfer","OswaldBold","OswaldLight","OswaldRegular","Pacifico","Paprika","Playball",
"Quando","Ranchers","SansitaOne","SpicyRice","TitanOne","Yellowtail","Yesteryear"];


for (var i=0; i < fontLibrary.length; i++) {
context.fillText("Sample",250,50);
context.font="34px " + fontLibrary[i];
}


changefontType();


function changefontType() {
selfonttype = $("#selfontype").val();
inputtextgo1();
}


function inputtextgo1() {
var y = 50;
var lineHeight = 36;
area1text = document.getElementById("bag1areatext").value;
context.clearRect(0, 0, 500, 95)
context.drawImage(section1backgroundimage, 0, 0);
context.font="34px " + selfonttype;
context.fillStyle = seltextcolor;
context.fillText(area1text, 250, y);
}

我的答案是 Google Web 字体,而不是@font-face。我到处寻找字体在画布中不显示的问题的解决方案。我尝试了计时器、 setInterval、字体延迟库和各种技巧。什么都没用。(包括将 font-family 放入用于画布的 CSS 或者画布元素的 ID 中。)

然而,我发现用谷歌字体呈现的 充满活力文本工作起来很容易。有什么区别吗?在画布动画,我们重新绘制动画项目一次又一次。所以我想把文本渲染两次。

这也不起作用——直到我还添加了一个短的(100ms)计时器延迟。目前为止我只在 Mac 上测试过。Chrome 在100毫秒时运行良好。Safari 需要重新加载页面,所以我把计时器提高到1000,然后就没问题了。如果我使用 Google 字体(包括动画版本) ,Firefox 18.0.2和20.0不会在画布上加载任何东西。

完整代码: http://www.macloo.com/examples/canvas/canvas10.html

Http://www.macloo.com/examples/canvas/scripts/canvas10.js

我在这里编写了一个 jsfiddle,其中包含了大多数建议的修复,但是没有一个解决了这个问题。然而,我是一个新手程序员,所以可能没有正确地编写建议的修复程序:

Http://jsfiddle.net/hathead/gcxq9/23/

HTML:

<!-- you need to empty your browser cache and do a hard reload EVERYTIME to test this otherwise it will appear to working when, in fact, it isn't -->


<h1>Title Font</h1>


<p>Paragraph font...</p>
<canvas id="myCanvas" width="740" height="400"></canvas>

CSS:

@import url(http://fonts.googleapis.com/css?family=Architects+Daughter);
@import url(http://fonts.googleapis.com/css?family=Rock+Salt);
canvas {
font-family:'Rock Salt', 'Architects Daughter'
}
.wf-loading p {
font-family: serif
}
.wf-inactive p {
font-family: serif
}
.wf-active p {
font-family:'Architects Daughter', serif;
font-size: 24px;
font-weight: bold;
}
.wf-loading h1 {
font-family: serif;
font-weight: 400;
font-size: 42px
}
.wf-inactive h1 {
font-family: serif;
font-weight: 400;
font-size: 42px
}
.wf-active h1 {
font-family:'Rock Salt', serif;
font-weight: 400;
font-size: 42px;
}

JS:

// do the Google Font Loader stuff....
WebFontConfig = {
google: {
families: ['Architects Daughter', 'Rock Salt']
}
};
(function () {
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();


//play with the milliseconds delay to find the threshold - don't forget to empty your browser cache and do a hard reload!
setTimeout(WriteCanvasText, 0);


function WriteCanvasText() {
// write some text to the canvas
var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
context.font = "normal" + " " + "normal" + " " + "bold" + " " + "42px" + " " + "Rock Salt";
context.fillStyle = "#d50";
context.fillText("Canvas Title", 5, 100);
context.font = "normal" + " " + "normal" + " " + "bold" + " " + "24px" + " " + "Architects Daughter";
context.fillText("Here is some text on the canvas...", 5, 180);
}

Workaround 我最终放弃了,在第一次加载时,使用了文本的图像,同时还将文本与字体面板放置在画布显示区域之外。所有随后在画布显示区域中显示的字体面孔都没有问题。无论如何,这都不是一个优雅的解决办法。

这个解决方案已经嵌入到我的网站中,但是如果有人需要的话,我会尝试创建一个 jsfiddle 来演示。

如何使用简单的 CSS 隐藏一个 div 使用的字体如下:

CSS:

#preloadfont {
font-family: YourFont;
opacity:0;
height:0;
width:0;
display:inline-block;
}

HTML:

<body>
<div id="preloadfont">.</div>
<canvas id="yourcanvas"></canvas>
...
</body>

一些 浏览器支持CSS 字体加载规范。它允许您注册一个回调函数,以便在所有字体都已加载时使用。在此之前,您可以延迟绘制画布(或者至少将文本绘制到画布中) ,并在字体可用时触发重绘。

添加如下所示的延迟

<script>
var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
setTimeout(function() {
ctx.font = "24px 'Proxy6'"; // uninstalled @fontface font style
ctx.textBaseline = 'top';
ctx.fillText('What!', 20, 10);
}, 100);
</script>

在画布中使用 FontFace API之前,可以先加载字体:

const myFont = new FontFace('My Font', 'url(https://myfont.woff2)');


myFont.load().then((font) => {
document.fonts.add(font);


console.log('Font loaded');
});

首先下载字体资源 myfont.woff2。下载完成后,字体将添加到文档的 FontFaceSet中。

FontFace API 的规范在撰写本文时只是一个工作草案

Https://drafts.csswg.org/css-font-loading/

var myFont = new FontFace('My Font', 'url(https://myfont.woff2)');


myFont.load().then(function(font){


// with canvas, if this is ommited won't work
document.fonts.add(font);


console.log('Font loaded');


});

如果您希望每次加载新字体时都重新绘制(并且可能更改呈现方式) ,那么字体加载 api 也有一个很好的 事件。在一个完全动态的环境里,我和“无极”之间出现了问题。

var fontFaceSet = document.fonts;
if (fontFaceSet && fontFaceSet.addEventListener) {
fontFaceSet.addEventListener('loadingdone', function () {
// Redraw something
});
} else {
// no fallback is possible without this API as a font files download can be triggered
// at any time when a new glyph is rendered on screen
}

画布的绘制与 DOM 加载无关。只有在预加载之后绘制画布时,预加载技术才会起作用。

我的解决方案,即使不是最好的:

CSS:

.preloadFont {
font-family: 'Audiowide', Impact, Charcoal, sans-serif, cursive;
font-size: 0;
position: absolute;
visibility: hidden;
}

HTML:

<body onload="init()">
<div class="preloadFont">.</div>
<canvas id="yourCanvas"></canvas>
</body>

JavaScript:

function init() {
myCanvas.draw();
}

我尝试使用 FontFaceSet.load 来解决这个问题: Https://jsfiddle.net/wengshenshun/gr1zkvtq/30

const prepareFontLoad = (fontList) => Promise.all(fontList.map(font => document.fonts.load(font)))

您可以从 https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/load中找到浏览器兼容性

本文解决了懒惰加载字体不显示的问题。

How to load web fonts to avoid performance issues and speed up page loading

This helped me ...

    <link rel="preload" as="font" href="assets/fonts/Maki2/fontmaki2.css" rel="stylesheet" crossorigin="anonymous">

由于我没有找到任何实际使用 FontFace 的例子,这里是@Fred 弗雷德 · 伯格曼的回答略作修改

const fontUrl = 'https://fonts.gstatic.com/s/robotomono/v22/L0xuDF4xlVMF-BfR8bXMIhJHg45mwgGEFl0_Of2_ROW-AJi8SJQt.woff'
const robotoFont = new FontFace('Roboto Mono', `url(${fontUrl})`);


// Canvas variables
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')


robotoFont.load().then((font: FontFace) => {
// This is required
document.fonts.add(font);


// Usage example
ctx.font = `30px '${font.family}'`;
ctx.fillText("Hello World!", 10, 50);
});

这是兼容性表