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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2756575/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-29 02:56:30  来源:igfitidea点击:

Drawing text to <canvas> with @font-face does not work at the first time

htmlcanvasfont-face

提问by lemonedo

When I draw a text in a canvas with a typeface that is loaded via @font-face, the text doesn't show correctly. It doesn't show at all (in Chrome 13 and Firefox 5), or the typeface is wrong (Opera 11). This type of unexpected behavior occurs only at the first drawing with the typeface. After then everything works fine.

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

Is it the standard behavior or something?

这是标准行为还是什么?

Thank you.

谢谢你。

PS: Following is the source code of the test case

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>

回答by bobince

Drawing on canvas has to happen and return immediately when you call the fillTextmethod. However, the browser has not yet loaded the font from the network, which is a background task. So it has to fall back to the font it doeshave available.

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

If you want to make sure the font is available, have some other element on the page preload it, eg.:

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

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

回答by a paid nerd

Use this trickand bind an onerrorevent to an Imageelement.

使用此技巧并将onerror事件绑定到Image元素。

Demo here: works on the latest Chrome.

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

回答by ellisbben

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.

问题的关键在于您正在尝试使用该字体,但浏览器尚未加载它,甚至可能还没有请求它。您需要的是可以加载字体并在加载后给您回调的东西;一旦你得到回调,你就知道可以使用字体了。

Look at Google's WebFont Loader; it seems like a "custom" provider and an activecallback after the load would make it work.

看看谷歌的WebFont Loader;它似乎是一个“自定义”提供程序,active加载后的回调将使其工作。

I've never used it before, but from a quick scan of the docs you need to make a css file fonts/pressstart2p.css, like this:

我以前从未使用过它,但是通过快速扫描文档,您需要制作一个 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');
}

Then add the following JS:

然后添加以下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);
  })();

回答by Fred Bergman

You can load fonts with the FontFace APIbefore using it in the 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');
});

The font resource myfont.woff2is first downloaded. Once the download completes, the font is added to the document's FontFaceSet.

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

The specification of the FontFace API is a working draft at the time of this writing. See browser compatibility table here.

FontFace API 的规范在撰写本文时是一个工作草案。请在此处查看浏览器兼容性表。

回答by Gabriel Gonzalez

What about using simple CSS to hide a div using the font like this:

使用简单的 CSS 来隐藏 div 使用这样的字体怎么样:

CSS:

CSS:

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

HTML:

HTML:

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

回答by Bruno Andrade

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

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

});

回答by Patrick H. Lauke

i've bumped into the issue when playing with it recently http://people.opera.com/patrickl/experiments/canvas/scroller/

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

worked around it by adding the font-family to canvas directly in the CSS, so you can just add

通过直接在 CSS 中将字体系列添加到画布来解决它,因此您只需添加

canvas { font-family: PressStart; }

画布 { 字体系列:按开始;}

回答by grapien

I am not sure if this will help you, but to solve the problem with my code I simply created a for loop at the top of my Javascript which ran through all the fonts that I wanted to load. I then ran a function to clear the canvas and preload the items I wanted on the canvas. So far it has worked perfectly. That was my logic I have posted my code below:

我不确定这是否会帮助你,但为了解决我的代码问题,我只是在我的 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);
    }

回答by MvG

Some browsers supportthe CSS Font Loadingspecification. It allows you to register register a callback for when all the fonts have been loaded. You can delay drawing your canvas (or at least drawing text into your canvas) until then, and trigger a redraw once the font is available.

有些浏览器支持CSS字体加载规范。它允许您在所有字体加载后注册注册回调。您可以延迟绘制画布(或至少将文本绘制到画布中)直到那时,并在字体可用后触发重绘。

回答by HatHead

I wrote a jsfiddle incorporating most of the suggested fixes here but none resolved the issue. However, I am a novice programmer so perhaps did not code the suggested fixes correctly:

我写了一个 jsfiddle,其中包含了大部分建议的修复程序,但没有解决问题。但是,我是一名新手程序员,所以可能没有正确编码建议的修复程序:

http://jsfiddle.net/HatHead/GcxQ9/23/

http://jsfiddle.net/HatHead/GcxQ9/23/

HTML:

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:

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:

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

WorkaroundI eventually gave in and, on the first load, used an image of the text while also positioning the text with the font-faces outside of the canvas display area. All subsequent displays of the font-faces within the canvas display area worked no problem. This is not an elegant workaround by any means.

解决方法我最终放弃了,在第一次加载时,使用了文本图像,同时还将文本与画布显示区域外的字体定位在一起。画布显示区域内字体的所有后续显示都没有问题。无论如何,这都不是一个优雅的解决方法。

The solution is baked into my website but if anyone needs I will try to create a jsfiddle to demonstrate.

该解决方案已集成到我的网站中,但如果有人需要,我将尝试创建一个 jsfiddle 来演示。