Jak sprawdzić, czy czcionka (@ font-face) została już załadowana?

80

Używam Font-Awesome, ale chociaż pliki czcionek nie są załadowane, ikony są oznaczone .

Dlatego chcę, aby te ikony były widoczne, display:nonegdy pliki nie są ładowane.

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

Skąd mam wiedzieć, że te pliki zostały załadowane i w końcu mogę wyświetlić ikony?

Edycja: nie mówię, kiedy strona jest ładowana (onload), ponieważ czcionka mogłaby zostać załadowana przed całą stroną.

Shankar Cabus
źródło
Miejmy nadzieję, że wkrótce będziemy mieć wydarzenia związane z natywnymi czcionkami blog.typekit.com/2013/02/05/more-reliable-font-events
Pacerier
2
To pytanie jest duplikatem, a na pierwotne pytanie opublikowałem odpowiedź z aktualizacji 2015 .
Dan Dascalescu
Jest rozwiązanie, które wykorzystuje wykrywanie przewijania na smnh.me/web-font-loading-detection-without-timers
Pacerier

Odpowiedzi:

44

Teraz na GitHub: https://github.com/patrickmarabeas/jQuery-FontSpy.js

Zasadniczo metoda działa poprzez porównanie szerokości ciągu w dwóch różnych czcionkach. Używamy Comic Sans jako czcionki do testowania, ponieważ jest to najbardziej różna z bezpiecznych czcionek internetowych i miejmy nadzieję, że różni się wystarczająco od każdej niestandardowej czcionki, której będziesz używać. Dodatkowo używamy bardzo dużego rozmiaru czcionki, więc nawet małe różnice będą widoczne. Po obliczeniu szerokości ciągu Comic Sans rodzina czcionek zostaje zmieniona na czcionkę niestandardową, z możliwością powrotu do Comic Sans. Po zaznaczeniu, jeśli szerokość elementu string jest taka sama, czcionka zastępcza Comic Sans jest nadal używana. Jeśli nie, twoja czcionka powinna działać.

Przepisałem metodę wykrywania ładowania czcionek do wtyczki jQuery zaprojektowanej tak, aby dać programiście możliwość stylizacji elementów w oparciu o to, czy czcionka została załadowana, czy nie. Dodano licznik czasu awaryjnego, aby użytkownik nie pozostał bez zawartości, jeśli czcionka niestandardowa nie zostanie załadowana. To po prostu zła użyteczność.

Dodałem również większą kontrolę nad tym, co dzieje się podczas ładowania czcionek i przy niepowodzeniach, włączając dodawanie i usuwanie klas. Możesz teraz zrobić, co chcesz, z czcionką. Zalecałbym tylko modyfikację rozmiaru czcionek, odstępów między wierszami itp., Aby Twoja czcionka rezerwowa była jak najbardziej zbliżona do niestandardowej, aby Twój układ pozostał nienaruszony, a użytkownicy uzyskają oczekiwane wrażenia.

Oto demo: http://patrickmarabeas.github.io/jQuery-FontSpy.js

Wrzuć poniższy plik do pliku .js i odwołaj się do niego.

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

Zastosuj to do swojego projektu

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

});

Usuń to FOUC!

.hideMe {
    visibility: hidden !important;
}

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

EDYCJA: Usunięto kompatybilność FontAwesome, ponieważ nie działała ona poprawnie i występowały problemy z różnymi wersjami. Hacky fix można znaleźć tutaj: https://github.com/patrickmarabeas/jQuery-FontFaceSpy.js/issues/1

Patrick
źródło
Porównywanie długości czcionek ... Czy to właśnie robi WebFont Loader (zobacz drugą odpowiedź)?
Pacerier
1
W każdym razie porównywanie długości znaków nie zadziała w przypadku wielu czcionek, ponieważ wiele czcionek „kopiujących” ma równe długości . Na przykład Arial, Helvetica i Liberation Sans mają identyczne szerokości znaków dla wszystkich znaków. Zobacz także en.wikipedia.org/wiki/Arial . Jak dotąd wydaje się, że sprawdzanie piksela na pikselu za pomocą płótna może być jedyną
niezawodną
1
Musiałem to wykorzystać, aby rozwiązać problem, który miałem z iScroll nieprawidłowo obliczającym rozmiary elementów przed załadowaniem czcionek. Ale nie używam jQuery, więc stworzyłem waniliową wersję js: github.com/keithswright/vanilla-fontspy wydaje się działać dla mnie.
Keith
@Pacerier - zwróć uwagę, że tylko wybrany przez Ciebie test i wybrana czcionka ładująca muszą mieć różne długości. Tak więc, chyba że Comic Sans ma wiele czcionek, z którymi ma identyczną szerokość znaków, w większości przypadków powinno to nadal działać.
BryanGrezeszak
20

Wypróbuj WebFont Loader ( repozytorium github ), opracowane przez Google i Typekit.

Ten przykład najpierw wyświetla tekst przy użyciu domyślnej czcionki szeryfowej; następnie po załadowaniu czcionek wyświetla tekst w określonej czcionce. (Ten kod odtwarza domyślne zachowanie Firefoksa we wszystkich innych nowoczesnych przeglądarkach).

cassi.lup
źródło
9

Oto inne podejście do rozwiązań od innych.

Używam FontAwesome 4.1.0 do tworzenia tekstur WebGL. To podsunęło mi pomysł, aby użyć małego płótna do renderowania fa-kwadrat, a następnie sprawdzić piksel na tym płótnie, aby sprawdzić, czy został załadowany:

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

Ponieważ używa płótna, wymaga dość nowoczesnej przeglądarki, ale może działać również w IE8 z polyfillem.

Leeft
źródło
Mam ten sam problem podczas ładowania font-awesome w KonvaJS
Mahdi Alkhatib
4

Właściwie istnieje dobry sposób na zrozumienie wszystkich czcionek zaczynają się pobierać lub ładowane w całości lub nie i popadają w błędy, ale nie dotyczy to tylko określonej czcionki , zwróć uwagę na następujący kod:

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

Jest też opcja, która powraca Promisei może sobie z nią poradzić.then funkcją:

document.fonts.ready
 .then(() => console.log('do someting at the final with each status'))
AmerllicA
źródło
Dziękuję Ci. to całkowicie działa! Ale muszę wywołać ładowanie czcionki, umieszczając gdzieś element <span> używający tej czcionki.
tyt2y3
nieobsługiwany w IE11, który niestety nadal jest dostarczany w pakiecie z Windows Server
Guy Passy
@GuyPassy, ​​nie wiem, co to jest IE11 !!
AmerllicA
4
@AmerllicA Żałuję, że pewnego dnia nie mogę być tak szczęśliwy
Guy Passy
3

Oto inny sposób sprawdzenia, czy czcionka @ font-face została już załadowana, bez konieczności korzystania w ogóle z liczników czasu: użyj zdarzenia „scroll”, aby otrzymać natychmiastowe zdarzenie, gdy rozmiar starannie wykonanego elementu zostanie zmieniony.

Napisałem post na blogu o tym, jak to się robi i opublikowałem bibliotekę na Github .

smnh
źródło
0

Spróbuj czegoś takiego

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

a potem zrób

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

Zdarzenie load powinno zostać uruchomione po załadowaniu czcionek.

hayk.mart
źródło
To jest to samo, $(function(){...})co działa po załadowaniu całej strony.
Shankar Cabus
1
to nie jest to samo. Przykład hayk.mart zostanie wywołany po zakończeniu ładowania zasobów DOM (HTML) ORAZ na stronie (CSS, JS, obrazy, ramki). Twój przykład, gdy tylko DOM się załadował.
Blaise,
Ciekawe, dlaczego ta odpowiedź jest odrzucana, szybkie wyszukiwanie pokazuje, że jest to właściwe podejście, np. Eager.io/blog/how-to-decide-when-your-code-should-run
-1

Jest to alternatywne podejście, które przynajmniej zapewni załadowanie font-awesome, a NIE kompletne rozwiązanie dla OP. Oryginalny kod znaleziony na forach wordpress tutaj https://wordpress.stackexchange.com/a/165358/40636 .

Jest agnostyczny i będzie działał z każdym zasobem stylu czcionki, takim jak font-awesome, w którym można sprawdzić rodzinę czcionek. Przy odrobinie zastanowienia założę się, że można to zastosować do znacznie więcej ...

<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
źródło
To jest awaryjna w przypadku Font-awesome nie ładuje (lub jeśli ładuje się zbyt wolno! ), Ale nie powiadamia , gdy czcionka zakończeniu ładowania.
Dan Dascalescu
@DanDascalescu Zaktualizowałem odpowiedź, aby wyraźniej wskazywała, że ​​jest to alternatywne podejście, które zapewnia tylko załadowanie niesamowitej biblioteki czcionek, a nie kompletne rozwiązanie. Mam nadzieję, że to trochę wyjaśnia, ponieważ zdobyłem kilka głosów przeciw w poprzedniej iteracji.
oucil
-3

Użyj poniższego kodu:

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

Potrafi sprawdzić wiele czcionek:

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

Poniższy kod działa tylko w operze, ale jest łatwy:

if(!document.defaultView.getComputedStyle(document.getElementById('mydiv'))['fontFamily'].match(/myfont/i))
          alert("font do not loaded ");
ali bagheri
źródło
Ten sam pomysł „renderowania na płótnie”, który Leeft opublikował rok wcześniej .
Dan Dascalescu