使用@ font-face将文本绘制到<canvas>在第一次不起作用

当我通过@ font-face加载的字体在canvas上绘制文本时,文本无法正确显示。 它根本不显示(在Chrome 13和Firefox 5中),或字体错误(Opera 11)。 这种意外的行为只发生在第一次使用字体的graphics上。 之后,一切正常。

这是标准的行为还是什么?

谢谢。

PS:以下是testing用例的源代码

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

当您调用fillText方法时,在canvas上绘图必须发生并立即返回。 但是,浏览器还没有从networking中加载字体,这是一个后台任务。 所以它必须回退到它可用的字体。

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

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

使用这个技巧,并将一个onerror事件绑定到一个Image元素。

此处演示:适用于最新的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); }; 

问题的症结在于你正在尝试使用字体,但浏览器还没有加载它,甚至可能还没有要求。 你需要的是加载字体并在加载后给你callback的东西; 一旦你得到callback,你知道这是可以使用字体。

看看Google的WebFont Loader ; 它看起来像一个“自定义”的提供者和一个activecallback后,负载将使其工作。

我从来没有使用过,但从文档的快速扫描,你需要做一个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); })(); 

怎么样使用简单的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> 

在canvas中使用之前,您可以使用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.c​​sswg.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'); }); 

我最近在玩这个游戏时遇到了这个问题http://people.opera.com/patrickl/experiments/canvas/scroller/

通过在CSS中直接添加字体系列来解决这个问题,所以你可以直接添加

canvas {font-family:PressStart; }

我不知道这是否会帮助你,但是为了解决我的代码问题,我只是在我的Javascript的顶部创build了一个for循环,这个循环遍历了我想要加载的所有字体。 然后我运行了一个函数来清除canvas,并在canvas上预先加载我想要的项目。 到目前为止,它已经完美运作。 这是我的逻辑我已经发布了我的代码如下:

 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); } 

一些浏览器支持 CSS字体加载规范。 它允许你注册一个callback,当所有的字体已经被加载。 您可以延迟绘制您的canvas(或至less绘制文本到您的canvas),然后一旦字体可用触发重绘。

首先使用Google的Web字体加载程序,如其他答案中的build议,并将绘图代码添加到它提供的callback中,以指示字体已被加载。 然而,这不是故事的结尾。 从这一点来说,它是非常依赖浏览器的。 大多数情况下,它可以正常工作,但有时可能需要等待几百毫秒,或者在页面的其他地方使用字体。 我尝试了不同的选项,afaik总是工作的一种方法是快速在canvas上绘制一些testing消息,并使用您要使用的字体系列和字体大小组合。 你可以用与背景相同的颜色来做,所以它们甚至不会被看见,而且会发生得非常快。 之后,字体一直为我和所有浏览器工作。

我的回答是Google Web字体而不是@ font-face。 我到处寻找解决问题的方法,而不是在canvas上显示字体。 我尝试了定时器,setInterval,字体延迟库和各种技巧。 没有工作。 (包括将font-family放在canvas的CSS或canvas元素的ID中。)

不过,我发现使用Google字体呈现的animation文本很容易工作。 有什么不同? 在canvasanimation中,我们反复绘制animation项目。 所以我想到了把文本渲染两次。

那也不pipe用 – 直到我还添加了一个短的(100ms)定时器延迟。 我只在目前的Mac上进行了testing。 Chrome在100ms时工作正常。 Safari需要一个页面重新加载,所以我增加了计时器到1000,然后就没事了。 如果我使用的是Google字体(包括animation版本),则Firefox 18.0.2和20.0不会在canvas上加载任何内容。

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

examples/canvas/scripts/canvas10.js

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

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); } 

解决方法我最终放弃了,并在第一次加载时使用了文本的图像,同时还将文本定位在canvas显示区域之外的font-faces。 所有后续的canvas显示区域中的字体显示都没有问题。 这不是一个优雅的解决方法。

解决scheme是烘烤到我的网站,但如果有人需要我会尝试创build一个jsfiddle来演示。

面临同样的问题。 阅读“bobince”等人的评论后,我使用下面的JavaScript来解决它:

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

如下所示添加延迟

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