Dibujar texto en con @ font-face no funciona en el primer momento

Cuando dibujo un texto en un canvas con un tipo de letra que se carga a través de @ font-face, el texto no se muestra correctamente. No se muestra en absoluto (en Chrome 13 y Firefox 5), o el tipo de letra está mal (Opera 11). Este tipo de comportamiento inesperado ocurre solo en el primer dibujo con el tipo de letra. Después de eso todo funciona bien.

¿Es el comportamiento estándar o algo así?

Gracias.

PD: lo que sigue es el código fuente del caso de prueba

    @font-face and <canvas>  @font-face { font-family: 'Press Start 2P'; src: url('fonts/PressStart2P.ttf'); }   canvas, pre { border: 1px solid black; padding: 0 1em; }    

@font-face and <canvas>

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. If you have visited this page before, you may have to refresh (or reload) it.

Your browser does not support the CANVAS element. Try the latest Firefox, Google Chrome, Safari or Opera.

@font-face


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); }); $('#view-css').text($('#css').text()); $('#view-script').text($('#script').text());

El dibujo en canvas tiene que suceder y volver inmediatamente cuando llame al método fillText . Sin embargo, el navegador aún no ha cargado la fuente desde la red, que es una tarea en segundo plano. Entonces tiene que recurrir a la fuente que tiene disponible.

Si quiere asegurarse de que la fuente esté disponible, tenga algún otro elemento en la página precargándola, por ej .:

 
.

Utiliza este truco y vincula un evento onerror a un elemento de Image .

Demostración aquí : funciona en la última versión de 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); }; 

El problema principal es que está tratando de usar la fuente pero el navegador aún no la ha cargado y posiblemente ni siquiera la haya solicitado. Lo que necesita es algo que cargará la fuente y le dará una callback una vez que esté cargada; una vez que recibes la callback, sabes que está bien usar la fuente.

Mire el WebFont Loader de Google; parece que un proveedor “personalizado” y una callback active después de que la carga lo haga funcionar.

Nunca lo había usado antes, pero a partir de un escaneo rápido de los documentos necesita hacer un archivo css fonts/pressstart2p.css , como este:

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

A continuación, agregue el siguiente 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); })(); 

¿Qué hay de usar CSS simple para ocultar un div usando la fuente como esta:

CSS:

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

HTML:

  
.
...

Puede cargar fonts con la API de FontFace antes de usarla en el canvas:

 const myFont = new FontFace('My Font', 'url(https://myfont.woff2)'); myFont.load().then((font) => { document.fonts.add(font); console.log('Font loaded'); }); 

El recurso de fuente myfont.woff2 primero se descarga. Una vez que se completa la descarga, la fuente se agrega al FontFaceSet del documento.

La especificación de la API de FontFace es un borrador en funcionamiento en el momento de escribir este documento. Vea la tabla de compatibilidad del navegador aquí.

https://drafts.csswg.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'); }); 

Me he topado con el problema cuando jugaba con él recientemente http://people.opera.com/patrickl/experiments/canvas/scroller/

funcionó al agregar la familia de fonts al canvas directamente en el CSS, por lo que puede agregar

canvas {font-family: PressStart; }

No estoy seguro si esto lo ayudará, pero para resolver el problema con mi código simplemente creé un bucle for en la parte superior de mi Javascript, el cual corría por todas las fonts que quería cargar. Luego ejecuté una función para borrar el canvas y precargar los elementos que quería en el canvas. Hasta ahora ha funcionado perfectamente. Esa fue mi lógica. He publicado mi código a continuación:

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

Escribí un jsfiddle que incorpora la mayoría de las soluciones sugeridas aquí pero ninguno resolvió el problema. Sin embargo, soy un progtwigdor novato, así que tal vez no codificó las correcciones sugeridas correctamente:

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

HTML:

  

Title Font

Paragraph font...

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

Solución alternativa Finalmente me rendí y, en la primera carga, usé una imagen del texto y también coloqué el texto con las caras de la fuente fuera del área de visualización del canvas. Todas las pantallas posteriores de las caras de las fonts dentro del área de visualización del canvas no generaron ningún problema. Esta no es una solución elegante de ninguna manera.

La solución está incluida en mi sitio web, pero si alguien necesita, intentaré crear un jsfiddle para demostrarlo.

Algunos navegadores admiten la especificación CSS de carga de fonts . Le permite registrar registrar una callback para cuando se hayan cargado todas las fonts. Puede retrasar el trazado de su canvas (o al menos dibujar texto en su canvas) hasta entonces y desencadenar un redibujado una vez que la fuente esté disponible.

En primer lugar, use el cargador de fonts web de Google como se indicó en la otra respuesta y agregue su código de dibujo a la callback que proporciona para indicar que las fonts se han cargado. Sin embargo, este no es el final de la historia. A partir de este punto, es muy dependiente del navegador. La mayoría de las veces funcionará bien, pero a veces puede ser necesario esperar unos cientos de milisegundos o utilizar las fonts en otro lugar de la página. Probé diferentes opciones y el único método que afaik siempre funciona es dibujar rápidamente algunos mensajes de prueba en el canvas con la familia de fonts y las combinaciones de tamaño de fuente que va a utilizar. Puedes hacerlo con el mismo color que el fondo, por lo que ni siquiera estarán visibles y sucederá muy rápido. Después de eso, las fonts siempre funcionaron para mí y en todos los navegadores.

Mi respuesta se dirige a las fonts web de Google en lugar de @ font-face. Busqué en todas partes una solución al problema de la fuente que no aparece en el canvas. Probé temporizadores, setInterval, bibliotecas de retardo de fuente y todo tipo de trucos. Nada funcionó. (Incluyendo poner font-family en el CSS para canvas o el ID del elemento canvas).

Sin embargo, descubrí que animar texto procesado en una fuente de Google funcionaba fácilmente. ¿Cual es la diferencia? En la animación de canvas, volvemos a dibujar los elementos animados una y otra vez. Entonces, tuve la idea de presentar el texto dos veces.

Eso tampoco funcionó, hasta que también agregué un retraso de temporizador corto (100 ms). Solo he probado en una Mac hasta ahora. Chrome funcionó bien a los 100 ms. Safari necesitaba una recarga de página, así que aumenté el temporizador a 1000, y luego todo estaba bien. Firefox 18.0.2 y 20.0 no cargarían nada en el canvas si usaba fonts de Google (incluida la versión de animación).

Código completo: http://www.macloo.com/examples/canvas/canvas10.html

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

Se enfrenta al mismo problema. Después de leer “bobince” y otros comentarios, utilizo el siguiente javascript para solucionarlo:

 $('body').append("
.
"); $('#loadfont').remove();

Si desea volver a dibujar cada vez que se carga una nueva fuente (y probablemente cambie la representación), la API de carga de la fuente también tiene un buen evento para eso. Tuve problemas con The Promise en un entorno completamente dynamic.

 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 } 

El canvas se dibuja independientemente de la carga DOM. La técnica de precarga solo funcionará si el canvas se dibuja después de la precarga.

Mi solución, incluso si no es la mejor:

CSS:

 .preloadFont { font-family: 'Audiowide', Impact, Charcoal, sans-serif, cursive; font-size: 0; position: absolute; visibility: hidden; } 

HTML:

  
.

JavaScript:

 function init() { myCanvas.draw(); } 

Agregue un retraso como se muestra a continuación