¿Cómo saber si ya se ha cargado una fuente (@ font-face)?

80

Estoy usando Font-Awesome, pero mientras los archivos de fuentes no están cargados, los íconos aparecen con .

Entonces, quiero que estos íconos tengan display:nonemientras los archivos no están cargados.

@font-face {
  font-family: "FontAwesome";
  src: url('../font/fontawesome-webfont.eot');
  src: url('../font/fontawesome-webfont.eot?#iefix') format('eot'), url('../font/fontawesome-webfont.woff') format('woff'), url('../font/fontawesome-webfont.ttf') format('truetype'), url('../font/fontawesome-webfont.svg#FontAwesome') format('svg');
  font-weight: normal;
  font-style: normal;
}

¿Cómo sé que estos archivos se han cargado y finalmente puedo mostrar los iconos?

Editar: No estoy hablando cuando la página está cargada (onload), porque la fuente podría cargarse antes que toda la página.

Shankar Cabus
fuente
Ojalá podamos tener eventos de fuentes nativas pronto blog.typekit.com/2013/02/05/more-reliable-font-events
Pacerier
2
Esta pregunta es un duplicado y he publicado una respuesta de actualización de 2015 sobre la pregunta original.
Dan Dascalescu
Hay una solución que usa la detección de desplazamiento en smnh.me/web-font-loading-detection-without-timers
Pacerier

Respuestas:

44

Ahora en GitHub: https://github.com/patrickmarabeas/jQuery-FontSpy.js

Básicamente, el método funciona comparando el ancho de una cadena en dos fuentes diferentes. Estamos usando Comic Sans como fuente para probar, porque es la más diferente de las fuentes seguras para la web y, con suerte, lo suficientemente diferente a cualquier fuente personalizada que esté usando. Además, estamos usando un tamaño de fuente muy grande, por lo que incluso las pequeñas diferencias serán evidentes. Cuando se ha calculado el ancho de la cadena Comic Sans, la familia de fuentes se cambia a su fuente personalizada, con una alternativa a Comic Sans. Cuando se marca, si el ancho del elemento de la cadena es el mismo, la fuente alternativa de Comic Sans todavía está en uso. De lo contrario, su fuente debería estar operativa.

Reescribí el método de detección de carga de fuentes en un complemento de jQuery diseñado para brindar al desarrollador la capacidad de diseñar elementos en función de si la fuente se ha cargado o no. Se ha agregado un temporizador a prueba de fallas para que el usuario no se quede sin contenido si la fuente personalizada no se carga. Eso es simplemente mala usabilidad.

También he agregado un mayor control sobre lo que sucede durante la carga de fuentes y en caso de falla con la inclusión de la adición y eliminación de clases. Ahora puede hacer lo que quiera con la fuente. Solo recomendaría modificar el tamaño de las fuentes, el espaciado entre líneas, etc. para que su fuente alternativa sea lo más cercana posible a la personalizada, de modo que su diseño permanezca intacto y los usuarios obtengan una experiencia esperada.

Aquí hay una demostración: http://patrickmarabeas.github.io/jQuery-FontSpy.js

Lanza lo siguiente en un archivo .js y haz referencia a él.

(function($) {

    $.fontSpy = function( element, conf ) {
        var $element = $(element);
        var defaults = {
            font: $element.css("font-family"),
            onLoad: '',
            onFail: '',
            testFont: 'Comic Sans MS',
            testString: 'QW@HhsXJ',
            delay: 50,
            timeOut: 2500
        };
        var config = $.extend( defaults, conf );
        var tester = document.createElement('span');
            tester.style.position = 'absolute';
            tester.style.top = '-9999px';
            tester.style.left = '-9999px';
            tester.style.visibility = 'hidden';
            tester.style.fontFamily = config.testFont;
            tester.style.fontSize = '250px';
            tester.innerHTML = config.testString;
        document.body.appendChild(tester);
        var fallbackFontWidth = tester.offsetWidth;
        tester.style.fontFamily = config.font + ',' + config.testFont;
        function checkFont() {
            var loadedFontWidth = tester.offsetWidth;
            if (fallbackFontWidth === loadedFontWidth){
                if(config.timeOut < 0) {
                    $element.removeClass(config.onLoad);
                    $element.addClass(config.onFail);
                    console.log('failure');
                }
                else {
                    $element.addClass(config.onLoad);
                    setTimeout(checkFont, config.delay);
                    config.timeOut = config.timeOut - config.delay;
                }
            }
            else {
                $element.removeClass(config.onLoad);
            }
        }
        checkFont();
    };

    $.fn.fontSpy = function(config) {
        return this.each(function() {
            if (undefined == $(this).data('fontSpy')) {
                var plugin = new $.fontSpy(this, config);
                $(this).data('fontSpy', plugin);
            }
        });
    };

})(jQuery);

Aplícalo a tu proyecto

.bannerTextChecked {
        font-family: "Lobster";
        /* don't specify fallback font here, do this in onFail class */
}

$(document).ready(function() {

    $('.bannerTextChecked').fontSpy({
        onLoad: 'hideMe',
        onFail: 'fontFail anotherClass'
    });

});

¡Quita ese FOUC!

.hideMe {
    visibility: hidden !important;
}

.fontFail {
    visibility: visible !important;
    /* fall back font */
    /* necessary styling so fallback font doesn't break your layout */
}

EDITAR: Se eliminó la compatibilidad de FontAwesome porque no funcionaba correctamente y tenía problemas con diferentes versiones. Se puede encontrar una solución hacky aquí: https://github.com/patrickmarabeas/jQuery-FontFaceSpy.js/issues/1

Patricio
fuente
Comparando longitudes de fuente ... ¿Es esto lo que WebFont Loader (ver la otra respuesta) también está haciendo?
Pacerier
1
Y en cualquier caso, comparar longitudes de caracteres no funcionará para muchas fuentes porque muchas fuentes de "copia" están diseñadas para tener longitudes iguales . Por ejemplo, Arial, Helvetica y Liberation Sans tienen anchos de caracteres idénticos para todos los caracteres. Consulte también en.wikipedia.org/wiki/Arial . Hasta ahora, parece que la comprobación de píxel por píxel utilizando lienzo podría ser la única opción
infalible
1
Necesitaba usar esto para solucionar un problema que tenía con iScroll al calcular los tamaños de los elementos incorrectamente antes de que se cargaran las fuentes. Pero no estoy usando jQuery, así que hice una versión vanilla js: github.com/keithswright/vanilla-fontspy parece estar funcionando para mí.
Keith
@Pacerier: tenga en cuenta que solo la prueba elegida y la fuente de carga elegida deben tener diferentes longitudes. Entonces, a menos que Comic Sans tenga muchas fuentes que tienen anchos de caracteres idénticos, esto debería funcionar en la mayoría de los casos.
BryanGrezeszak
20

Pruebe WebFont Loader ( repositorio de github ), desarrollado por Google y Typekit.

Este ejemplo primero muestra el texto en la fuente serif predeterminada; luego, una vez que se han cargado las fuentes, muestra el texto en la fuente especificada. (Este código reproduce el comportamiento predeterminado de Firefox en todos los demás navegadores modernos).

cassi.lup
fuente
9

Aquí hay un enfoque diferente a las soluciones de otros.

Estoy usando FontAwesome 4.1.0 para crear texturas WebGL. Eso me dio la idea de usar un lienzo pequeño para representar un cuadrado fa, luego verificar un píxel en ese lienzo para probar si se ha cargado:

function waitForFontAwesome( callback ) {
   var retries = 5;

   var checkReady = function() {
      var canvas, context;
      retries -= 1;
      canvas = document.createElement('canvas');
      canvas.width = 20;
      canvas.height = 20;
      context = canvas.getContext('2d');
      context.fillStyle = 'rgba(0,0,0,1.0)';
      context.fillRect( 0, 0, 20, 20 );
      context.font = '16pt FontAwesome';
      context.textAlign = 'center';
      context.fillStyle = 'rgba(255,255,255,1.0)';
      context.fillText( '\uf0c8', 10, 18 );
      var data = context.getImageData( 2, 10, 1, 1 ).data;
      if ( data[0] !== 255 && data[1] !== 255 && data[2] !== 255 ) {
         console.log( "FontAwesome is not yet available, retrying ..." );
         if ( retries > 0 ) {
            setTimeout( checkReady, 200 );
         }
      } else {
         console.log( "FontAwesome is loaded" );
         if ( typeof callback === 'function' ) {
            callback();
         }
      }
   }

   checkReady();
};

Como usa un lienzo, requiere un navegador bastante moderno, pero también podría funcionar en IE8 con polyfill.

Leeft
fuente
Me enfrento al mismo problema al cargar font-awesome en KonvaJS
Mahdi Alkhatib
4

En realidad, hay una buena forma de entender que todas las fuentes comienzan a descargarse o se cargan completamente o no y caen en algunos errores, pero no es solo para una fuente en específico , preste atención al siguiente código:

document.fonts.onloading = () => {
  // do someting when fonts begin to download
};
document.fonts.onloadingdone = () => {
  // do someting when fonts are loaded completely
};
document.fonts.onloading = () => {
  // do someting when fonts fall into some error
};

Y también hay una opción que regresa Promisey podría manejar con la .thenfunción:

document.fonts.ready
 .then(() => console.log('do someting at the final with each status'))
AmerllicA
fuente
gracias. ¡Funciona totalmente! Pero necesito activar la carga de la fuente colocando un elemento <span> usando esa fuente en algún lugar.
tyt2y3
no es compatible con IE11, que desafortunadamente todavía viene incluido con Windows Server
Guy Passy
@GuyPassy, ​​¡no sé qué es el IE11!
AmerllicA
4
@AmerllicA Ojalá pudiera algún día tener tanta suerte
Guy Passy
3

Aquí hay otra forma de saber si un @ font-face ya se ha cargado sin tener que usar temporizadores en absoluto: utilice un evento "scroll" para recibir un evento instantáneo cuando se cambia el tamaño de un elemento cuidadosamente elaborado.

Escribí una publicación de blog sobre cómo se hace y publiqué la biblioteca en Github .

smnh
fuente
0

Prueba algo como

$(window).bind("load", function() {
       $('#text').addClass('shown');
});

y luego haz

#text {visibility: hidden;}
#text.shown {visibility: visible;}

El evento de carga debería activarse después de que se carguen las fuentes.

hayk.mart
fuente
Este es el mismo $(function(){...})que se ejecuta cuando se carga toda la página.
Shankar Cabus
1
no es lo mismo. El ejemplo de hayk.mart se activará cuando el DOM (HTML) Y los activos dentro de la página (CSS, JS, imágenes, marcos) terminen de cargarse. Tu ejemplo cuando solo el DOM ha terminado de cargarse.
Blaise
Curioso por qué se rechaza esta respuesta, una búsqueda rápida muestra que es el enfoque correcto, por ejemplo, eager.io/blog/how-to-decide-when-your-code-should-run
-1

Este es un enfoque alternativo que al menos asegurará que se cargue font-awesome, NO una solución completa para el OP. El código original se encuentra en los foros de wordpress aquí https://wordpress.stackexchange.com/a/165358/40636 .

Es agnóstico y funcionará con cualquier recurso de estilo de fuente como font-awesome, donde se puede verificar una familia de fuentes. Con un poco más de pensamiento, apuesto a que esto podría aplicarse a mucho más ...

<link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet">
<script>
    (function($){
        var faSpan = $('<span class="fa" style="display:none"></span>').appendTo('body');
        if (faSpan .css('fontFamily') !== 'FontAwesome' ) {
            // Fallback Link
            $('head').append('<link href="https://stackoverflow.com/css/font-awesome.min.css" rel="stylesheet">');
        }
        faSpan.remove();
    })(jQuery);
</script>
oucil
fuente
Esta es una alternativa en caso de que Font-Awesome no se cargue (¡o si se carga demasiado lento! ), Pero no notifica cuando la fuente ha terminado de cargarse.
Dan Dascalescu
@DanDascalescu He actualizado la respuesta para indicar más claramente que este es un enfoque alternativo que solo garantiza que se cargue la biblioteca de fuentes impresionantes, y no una solución completa. Con suerte, eso lo aclara un poco, ya que obtuve algunos votos negativos de la iteración anterior.
oucil
-3

Utilice el siguiente código:

<!DOCTYPE HTML>
<html>
    <head>
    </head>

<body>
<canvas id="canvasFont" width="40px" height="40px" style="position: absolute; display: none;"></canvas>

<script>
function IsLoadedFonts()
    {
        var Args = arguments;
        var obj = document.getElementById('canvasFont');
        var ctx = obj.getContext("2d");
        var baseFont = (/chrome/i.test(navigator.userAgent))?'tims new roman':'arial';
         //................
          function getImg(fon)
          { 
            ctx.clearRect(0, 0, (obj).width, (obj).height);
            ctx.fillStyle = 'rgba(0,0,0,1.0)';
            ctx.fillRect( 0, 0, 40, 40 );
            ctx.font = '20px '+ fon;
            ctx.textBaseline = "top";
            ctx.fillStyle = 'rgba(255,255,255,1.0)';
            ctx.fillText( '\u0630', 18, 5 );
            return ctx.getImageData( 0, 0, 40, 40 );
          };
        //..............
          for(var i1=0; i1<Args.length; i1++)
          {
            data1 = getImg(Args[i1]);
            data2 = getImg(baseFont);
            var isLoaded = false;
            //...........
            for (var i=0; i<data1.data.length; i++)
            {
                if(data1.data[i] != data2.data[i])
                    {isLoaded = true; break;}
            }
            //..........
            if(!isLoaded)
                    return false;
         }
         return true;
    };

     setTimeout(function(){alert(IsLoadedFonts('myfont'));},100);
   </script>
   </body>

Puede comprobar muchas fuentes:

setTimeout(function(){alert(IsLoadedFonts('font1','font2','font3'));},100);

El siguiente código funciona solo en Opera, pero es fácil:

if(!document.defaultView.getComputedStyle(document.getElementById('mydiv'))['fontFamily'].match(/myfont/i))
          alert("font do not loaded ");
Ali Bagheri
fuente
La misma idea de "renderizar en lienzo" que Leeft publicó un año antes .
Dan Dascalescu