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


93

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

1
ブラウザは、非同期でフォントをバックグラウンドでロードします。これは正常な動作です。paulirish.com/2009/fighting-the-font-face-fout
Thomas

回答:


71

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

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

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

3
を追加したくなるdisplay: noneかもしれませんが、ブラウザがフォントの読み込みをスキップする可能性があります。の代わりにスペースを使用することをお勧めします.
トーマス

6
スペースを使用すると、IEはdiv内にあるはずの空白ノードを破棄し、フォントにレンダリングするテキストは残りません。もちろん、IEはまだキャンバスをサポートしていないため、future-IEがこれを継続するかどうか、およびフォントの読み込み動作に影響するかどうかは不明ですが、IEのHTML解析に関する長年の問題です。
ボビンス

1
フォントをプリロードする簡単な方法はありませんか?たとえば、なんとかしてJavaScriptで強制しますか?
Joshua

@ジョシュア:ページ上に要素を作成し、その上にフォントを設定することによってのみ、つまり 上記と同じコンテンツを動的に作成します。
bobince

17
これを追加しても、JavaScriptの実行時にフォントが既にロードされているとは限りません。問題のフォントを遅延(setTimeout)してスクリプトを実行しなければなりませんでした。
ニック

23

このトリックを使用し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 /programming/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);
};

3
スマートトリック。ただし、CSSをロードしていることに注意してください。CSSには、実際のフォントファイル(.ttf、.woffなど)への参照が含まれています。私はあなたのトリックを2回使用しなければなりませんでした。
マグマ

1
このアプローチを.ttfフォントで試した-Chrome(41.0.2272.101 m)では安定して動作しません。5秒のsetTimeoutでも役に立ちません-最初のレンダリングはデフォルトのフォントで行われます。
ミハイルフィアドセンカ2015年

2
srcを設定する前に onerrorハンドラを設定する必要があります
asdjfiasd

1
また、「新しい画像」。括弧がありません。
asdjfiasd

@asdjfiasd括弧は必要ありません。src属性が設定されていても、ロードは次の実行ブロックで開始されます。
有料のオタク

16

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

GoogleのWebFont Loaderを見てください。「カスタム」プロバイダーのようでactive、ロード後のコールバックで機能します。

私はこれを使用したことがありませんが、ドキュメントのクイックスキャンからfonts/pressstart2p.css、次のように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);
  })();

13

フォントをキャンバスで使用する前に、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の仕様は、この記事の執筆時点での草案です。こちらのブラウザ互換表をご覧ください。


1
あなたがいませんdocument.fonts.add。ブルーノの答えを見てください。
パセリエ2017年

ありがとう@Pacerier!私の答えを更新しました。
Fred Bergman

このテクノロジーは実験的なものであり、ほとんどのブラウザでサポートされていません。
Michael Zelensky

11

単純な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>



1

これが役立つかどうかはわかりませんが、コードの問題を解決するために、ロードしたいすべてのフォントを実行する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);
    }

上記のコードを追加して、私の答えを説明します。別のウェブページを開発するときに同様の問題があり、サーバーエンドですべてのフォントが読み込まれるため、ウェブページに正しく表示されるため、これが解決しました。
grapien

1

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

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

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


1

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


0

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


0

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

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

それも機能しませんでした-短い(100ms)タイマー遅延も追加するまで。これまでMacでのみテストしました。Chromeは100msで正常に動作しました。Safariではページの再読み込みが必要だったので、タイマーを1000に増やしたところ、問題はありませんでした。Firefox 18.0.2および20.0では、Googleフォント(アニメーションバージョンを含む)を使用している場合、キャンバスに何も読み込まれません。

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

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


0

同じ問題に直面しています。「bobince」およびその他のコメントを読んだ後、次のJavaScriptを使用して回避策を実行します。

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

0

新しいフォントが読み込まれるたびに再描画したい場合(そしておそらくレンダリングを変更したい場合)、フォント読み込みAPIにもそのための素晴らしいイベントがあります。完全な動的環境で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

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



-4

以下のように遅延を追加します

<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>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.