@ Font-face ile <canvas> üzerine metin çizmek ilk seferde çalışmıyor


97

@ Font-face aracılığıyla yüklenen bir yazı tipine sahip bir tuvale metin çizdiğimde, metin doğru görünmüyor. Hiç gösterilmiyor (Chrome 13 ve Firefox 5'te) veya yazı tipi yanlış (Opera 11). Bu tür beklenmedik davranışlar yalnızca yazı tipi ile ilk çizimde ortaya çıkar. Ondan sonra her şey yolunda gidiyor.

Standart davranış mı yoksa başka bir şey mi?

Teşekkür ederim.

Not: Test senaryosunun kaynak kodu aşağıdadır

<!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
Tarayıcılar, yazı tipini arka planda eşzamansız olarak yükler. Bu normal bir davranıştır. Ayrıca bakınız paulirish.com/2009/fighting-the-font-face-fout
Thomas

Yanıtlar:


74

Kanvas üzerine çizim, fillTextyöntemi çağırdığınızda hemen gerçekleşmeli ve geri dönmelidir . Ancak, tarayıcı yazı tipini henüz bir arka plan görevi olan ağdan yüklememiştir. O yazı tipine geri düşmek zorunda Yani gelmez mevcuttur.

Yazı tipinin mevcut olduğundan emin olmak istiyorsanız, sayfada başka bir öğenin önceden yüklemesini sağlayın, örneğin:

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

3
Eklemek display: noneisteyebilirsiniz, ancak bu, tarayıcıların yazı tipini yüklemeyi atlamasına neden olabilir. A yerine boşluk kullanmak daha iyidir ..
Thomas

6
Boşluk kullanmak, IE'nin div'de olması gereken boşluk düğümünü atmasına ve yazı tipinde işlenecek metin kalmamasına neden olur. Elbette IE henüz tuvali desteklemiyor, bu yüzden Future-IE'nin bunu yapmaya devam edip etmeyeceği ve bunun yazı tipi yükleme davranışı üzerinde bir etkisi olup olmayacağı bilinmiyor, ancak bu uzun süredir devam eden bir IE HTML ayrıştırma problemi.
bobince

1
Yazı tipini önceden yüklemenin daha kolay bir yolu yok mu? Örneğin, bir şekilde javascript üzerinden zorlamak mı?
Joshua

@Joshua: sadece sayfada bir eleman oluşturarak ve üzerine yazı tipini ayarlayarak, yani. yukarıdakiyle aynı içeriği ancak dinamik olarak oluşturmak.
bobince

17
Bunu eklemek, JavaScript çalıştırıldığında yazı tipinin zaten yükleneceğini garanti etmez. Betiğimi söz konusu fontu (setTimeout) kullanarak yürütmek zorunda kaldım ki bu tabii ki kötü.
Nick

23

Bu numarayı kullanın ve bir onerrorolayı bir Imageöğeye bağlayın.

Demo burada : en son Chrome'da çalışır.

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
akıllı numara. sırayla gerçek yazı tipi dosyasına (örneğin .ttf, .woff, vb.) bir referans içeren css'yi yüklediğinizi unutmayın. Her şeyin yüklendiğinden emin olmak için numaranızı iki kez, bir kez css dosyası için ve bir kez de referans yazı tipi dosyası (.woff) için kullanmak zorunda kaldım.
magma

1
Bu yaklaşımı .ttf yazı tipiyle denedim - Chrome'da kararlı bir şekilde çalışmıyor (41.0.2272.101 m). 5 saniyedeki setTimeout bile yardımcı olmuyor - ilk oluşturma varsayılan yazı tipiyle gider.
Mikhail Fiadosenka

2
Src'yi

1
ayrıca "yeni Resim;" eksik parantez var.
asdjfiasd

@asdjfiasd Parenlere gerek yoktur ve srcöznitelik ayarlanmış olsa bile yükleme sonraki yürütme bloğunda başlayacaktır.
ücretli inek

16

Sorunun özü, yazı tipini kullanmaya çalışmanız, ancak tarayıcının henüz yüklememiş olması ve hatta muhtemelen istememiş olmasıdır. İhtiyacınız olan şey, yazı tipini yükleyecek ve yüklendikten sonra size geri arama yapacak bir şeydir; Geri aramayı aldığınızda, yazı tipini kullanmanın uygun olduğunu bilirsiniz.

Google'ın WebFont Yükleyicisine bakın ; "özel" bir sağlayıcı gibi görünüyor activeve yükten sonra bir geri arama onu çalıştıracak.

Daha önce hiç kullanmadım, ancak dokümanları hızlı bir şekilde taradığınızda, bunun fonts/pressstart2p.cssgibi bir css dosyası oluşturmanız gerekir :

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

Ardından aşağıdaki JS'yi ekleyin:

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

14

Yazı tiplerini tuvalde kullanmadan önce FontFace API ile yükleyebilirsiniz :

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

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

  console.log('Font loaded');
});

Yazı tipi kaynağı myfont.woff2ilk olarak indirilir. İndirme işlemi tamamlandığında, yazı tipi belgenin FontFaceSet'ine eklenir .

FontFace API'nin spesifikasyonu, bu yazının yazıldığı sırada çalışan bir taslaktır. Tarayıcı uyumluluk tablosuna buradan bakın.


1
Eksiksin document.fonts.add. Bruno'nun cevabını görün.
Pacerier

Teşekkürler @Pacerier! Cevabım güncellendi.
Fred Bergman

Bu teknoloji deneyseldir ve çoğu tarayıcı tarafından desteklenmemektedir.
Michael Zelensky

11

Böyle bir yazı tipini kullanarak bir div'i gizlemek için basit CSS kullanmaya ne dersiniz:

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

Bunun size yardımcı olup olmayacağından emin değilim, ancak problemi kodumla çözmek için Javascript'imin en üstünde yüklemek istediğim tüm fontların üzerinden geçen bir for döngüsü oluşturdum. Daha sonra tuvali temizlemek ve istediğim öğeleri tuvale önceden yüklemek için bir işlev çalıştırdım. Şimdiye kadar mükemmel çalıştı. Bu benim mantığımdı, kodumu aşağıya yazdım:

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

Cevabımı göstermek için yukarıya bazı kodlar ekledim. Başka bir web sayfası geliştirirken benzer bir sorun yaşadım ve bu, sunucuda olduğu gibi çözüldü, tüm yazı tiplerini yükleyerek web sayfasında doğru şekilde görüntülenmelerini sağlıyor.
grapien

1

Burada önerilen düzeltmelerin çoğunu içeren bir jsfiddle yazdım ancak hiçbiri sorunu çözmedi. Ancak, acemi bir programcıyım, bu yüzden belki de önerilen düzeltmeleri doğru şekilde kodlamamışımdır:

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

Geçici çözüm Sonunda teslim oldum ve ilk yüklemede metni tuval görüntüleme alanının dışında yazı tipi yüzleriyle konumlandırırken metnin bir görüntüsünü kullandım. Tuval görüntüleme alanındaki yazı tipi yüzlerinin sonraki tüm görünümleri sorun yaratmadı. Bu hiçbir şekilde zarif bir çözüm değildir.

Çözüm web sitemde yer alıyor ancak ihtiyacı olan olursa bunu göstermek için bir jsfiddle oluşturmaya çalışacağım.


1

Bazı tarayıcıların desteklediği CSS Yazı yükleniyor şartname. Tüm yazı tipleri yüklendiğinde bir geri arama kaydetmenize olanak tanır. Kanvasınızı çizmeyi (veya en azından tuvalinize metin çizmeyi) o zamana kadar erteleyebilir ve yazı tipi kullanılabilir olduğunda yeniden çizmeyi tetikleyebilirsiniz.



0

Öncelikle, diğer cevapta önerildiği gibi Google'ın Web Yazı Tipi yükleyicisini kullanın ve çizim kodunuzu, yazı tiplerinin yüklendiğini belirtmek için sağladığı geri aramaya ekleyin. Ancak bu hikayenin sonu değil. Bu noktadan itibaren tarayıcıya çok bağlıdır. Çoğu zaman iyi çalışır, ancak bazen birkaç yüz milisaniye beklemek veya yazı tiplerini sayfada başka bir yerde kullanmak gerekebilir. Farklı seçenekleri denedim ve afaik'in her zaman çalıştığı yöntemlerden biri, kullanacağınız yazı tipi ailesi ve yazı tipi boyutu kombinasyonlarıyla tuvalde hızlıca bazı test mesajları çizmek. Bunu arka planla aynı renkte yapabilirsiniz, böylece görünmezler ve çok hızlı gerçekleşir. Bundan sonra yazı tipleri her zaman benim için ve tüm tarayıcılarda çalıştı.


0

Cevabım @ font-face yerine Google Web yazı tiplerini ele alıyor. Tuvalde görünmeyen yazı tipi sorununa çözüm bulmak için her yerde aradım. Zamanlayıcılar, setInterval, font-delay kitaplıkları ve her türlü hileyi denedim. Hiçbir şey işe yaramadı. (Yazı tipi ailesini tuval için CSS'ye veya tuval öğesinin kimliğine koymak dahil.)

Ancak, bir Google yazı tipinde oluşturulan animasyon metninin kolayca çalıştığını gördüm . Fark ne? Kanvas animasyonda, animasyonlu öğeleri tekrar tekrar çiziyoruz. Böylece metni iki kez oluşturma fikrini aldım.

Bu da işe yaramadı - kısa (100 ms) bir zamanlayıcı gecikmesi de ekleyene kadar. Şimdiye kadar yalnızca bir Mac'te test ettim. Chrome, 100 ms'de iyi çalıştı. Safari bir sayfanın yeniden yüklenmesini gerektirdi, bu yüzden zamanlayıcıyı 1000'e çıkardım ve sonra sorun yoktu. Google yazı tiplerini (animasyon sürümü dahil) kullansaydım, Firefox 18.0.2 ve 20.0 tuvale hiçbir şey yüklemezdi.

Tam kod: http://www.macloo.com/examples/canvas/canvas10.html

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


0

Aynı problemle karşı karşıya. "Bobince" ve diğerlerinin yorumlarını okuduktan sonra, geçici çözüm için aşağıdaki javascript'i kullanıyorum:

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

0

Her yeni bir yazı tipi yüklendiğinde (ve muhtemelen oluşturmayı değiştirdiğinde) yeniden çizmek istiyorsanız, yazı tipi yükleme apisinin bunun için de güzel bir olayı vardır . Tamamen dinamik bir ortamda Promise ile sorunlar yaşadım.

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

Kanvas, DOM yüklemesinden bağımsız olarak çizilir. Ön yükleme tekniği yalnızca önyüklemeden sonra kanvas çizilirse işe yarar.

En iyisi olmasa bile çözümüm:

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

Aşağıdaki gibi bir gecikme ekleyin

<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>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.