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

lem*_*edo 85 html5 canvas font-face

当我在带有通过@ font-face加载的字体的画布上绘制文本时,文本无法正确显示.它根本不显示(在Chrome 13和Firefox 5中),或者字体错误(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>
Run Code Online (Sandbox Code Playgroud)

bob*_*nce 64

必须在画布上绘图并在调用fillText方法时立即返回.但是,浏览器尚未从网络加载字体,这是一项后台任务.因此,它必须回退到字体它确实有可用.

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

<div style="font-family: PressStart;">.</div>
Run Code Online (Sandbox Code Playgroud)

  • 添加这个并不能保证在执行JavaScript时已经加载了字体.我不得不使用延迟的问题字体(setTimeout)来执行我的脚本,这当然是不好的. (17认同)
  • 使用空格将导致IE丢弃应该在div中的空白节点,不留下要在字体中呈现的文本.当然IE还不支持canvas,所以不知道future-IE是否会继续这样做,以及这是否会对字体加载行为产生影响,但这是一个长期存在的IE HTML解析问题. (6认同)
  • 您可能想添加`display:none`,但这可能会导致浏览器跳过加载字体.最好使用空格而不是`.`. (2认同)
  • 有没有更简单的方法来预加载字体?例如以某种方式通过javascript强制它? (2认同)

a p*_*erd 20

使用此技巧并将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);
};
Run Code Online (Sandbox Code Playgroud)

  • 聪明的把戏.请注意,虽然您正在加载css,而css又包含对真实字体文件的引用(例如,.ttf,.woff等).我必须使用你的技巧两次,一次用于css文件,一次用于引用的字体文件(.woff)以确保所有内容都已加载. (3认同)
  • 你应该在设置src之前设置onerror handler** (2认同)

ell*_*ben 16

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

看看谷歌的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');
}
Run Code Online (Sandbox Code Playgroud)

然后添加以下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);
  })();
Run Code Online (Sandbox Code Playgroud)


小智 10

如何使用简单的CSS来隐藏div使用这样的字体:

CSS:

#preloadfont {
  font-family: YourFont;
  opacity:0;
  height:0;
  width:0;
  display:inline-block;
}
Run Code Online (Sandbox Code Playgroud)

HTML:

<body>
   <div id="preloadfont">.</div>
   <canvas id="yourcanvas"></canvas>
   ...
</body>
Run Code Online (Sandbox Code Playgroud)


Fre*_*man 9

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

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

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

  console.log('Font loaded');
});
Run Code Online (Sandbox Code Playgroud)

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

在撰写本文时,FontFace API的规范是一份工作草案.请参阅此处的浏览器兼容性

  • 您缺少“document.fonts.add”。请参阅布鲁诺的回答。 (3认同)

Bru*_*ade 7

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

});
Run Code Online (Sandbox Code Playgroud)