web-dev-qa-db-ja.com

@ font-faceを使用して<canvas>にテキストを描画すると、最初は機能しません。

@ 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>
83
lemonedo

キャンバスに描画する必要があり、fillTextメソッドを呼び出すとすぐに戻る必要があります。ただし、ブラウザはネットワークからフォントをまだロードしていません。これはバックグラウンドタスクです。そのため、使用可能なフォントにフォールバックする必要がありますdoes.

フォントが使用可能であることを確認したい場合は、ページ上の他の要素をプリロードしてください。例:

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

このトリック を使用して、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);
};
20
a paid nerd

問題の核心は、フォントを使用しようとしているが、ブラウザがまだそれをロードしておらず、おそらく要求さえしていないことです。必要なのは、フォントをロードし、ロード後にコールバックを提供するものです。コールバックを取得すると、フォントを使用しても問題ないことがわかります。

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);
  })();
15
ellisbben

次のようなフォントを使用してdivを非表示にする単純なCSSを使用するのはどうですか。

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

キャンバスで使用する前に 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の仕様は、この記事の執筆時点での草案です。 ブラウザの互換性の表を参照してください。

7
Fred Bergman

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

});
4
Bruno

最近遊んでいるときに問題にぶつかりました http://people.opera.com/patrickl/experiments/canvas/scroller/

フォントファミリーをCSSのキャンバスに直接追加することでそれを回避したので、追加するだけです

canvas {font-family:PressStart; }

3

ここに提案された修正のほとんどを組み込んだ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);
}

回避策最終的に私は譲り、最初のロードでは、テキストをキャンバスの外側のフォントフェイスで配置しながらテキストの画像を使用しました表示領域。キャンバス表示領域内のフォントフェイスの以降の表示はすべて問題なく機能しました。これは、決してエレガントな回避策ではありません。

このソリューションは私のWebサイトに組み込まれていますが、必要な場合はjsfiddleを作成してデモを試みます。

1
HatHead

一部の ブラウザサポートCSSフォントの読み込み 仕様。すべてのフォントがロードされたときのコールバックを登録して登録できます。それまではキャンバスの描画を遅らせ(または少なくともテキストをキャンバスに描画する)、フォントが使用可能になったら再描画をトリガーできます。

1
MvG

これが役立つかどうかはわかりませんが、コードの問題を解決するために、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);
    }
1
grapien

FontFaceSet.loadを使用して問題を解決しようとしています: https://jsfiddle.net/wengshenshun/gr1zkvtq/

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

ブラウザの互換性は https://developer.mozilla.org/en-US/docs/Web/API/FontFaceSet/load から見つけることができます。

0
翁沈顺

私の答えは、@ font-faceではなく、Google Webフォントに関するものです。フォントがキャンバスに表示されないという問題の解決策を探しました。タイマー、setInterval、フォント遅延ライブラリ、およびあらゆる種類のトリックを試しました。何も機能しませんでした。 (canvasのCSSまたはcanvas要素のIDにfont-familyを含めることを含みます。)

しかし、Googleフォントでレンダリングされたアニメーションテキストは簡単に機能することがわかりました。違いは何ですか?キャンバスアニメーションでは、アニメーション化されたアイテムを何度も再描画します。そこで、テキストを2回レンダリングするというアイデアを得ました。

それもうまくいきませんでした-短い(100ms)タイマー遅延を追加するまで。これまでMacでしかテストしていません。 Chrome 100msで正常に動作しました。Safariはページの再読み込みを必要としたため、タイマーを1000に増やした後、問題ありませんでした。Firefox18.0.2および20.0はキャンバスに何もロードしませんGoogleフォント(アニメーションバージョンを含む)を使用していました。

完全なコード: http://www.macloo.com/examples/canvas/canvas10.html

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

0
macloo

新しいフォントがロードされるたびに再描画したい場合(そしておそらくレンダリングを変更する場合)、フォントロードAPIにはそのためのNice event もあります。完全な動的環境でPromiseに問題がありました。

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
}
0
HolgerJeromin

まず、他の回答でアドバイスされたようにGoogleのWeb Font Loaderを使用し、提供されたコールバックに描画コードを追加して、フォントがロードされたことを示します。しかし、これで話は終わりではありません。この時点から、ブラウザに大きく依存します。ほとんどの場合は問題なく動作しますが、場合によっては数百ミリ秒待機するか、ページ上のどこかでフォントを使用する必要があります。さまざまなオプションを試しましたが、afaikが常に機能する方法の1つは、使用するフォントファミリとフォントサイズの組み合わせを使用して、キャンバスにテストメッセージをすばやく描画することです。あなたは背景と同じ色でそれを行うことができますので、それらは見えさえせず、それは非常に速く起こります。その後、フォントは私とすべてのブラウザーで常に機能しました。

0
Megas

キャンバスは、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();
}
0

同じ問題に直面しています。 「bobince」などのコメントを読んだ後、次のjavascriptを使用して回避します。

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