Як дізнатися, чи вже завантажений шрифт (@ font-face)?


80

Я використовую Font-Awesome, але поки шрифтові файли не завантажуються, піктограми відображаються з .

Отже, я хочу, щоб ці значки були, display:noneпоки файли не завантажуються.

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

Як мені дізнатися, що ці файли завантажено, і я нарешті зможу показати піктограми?

Редагувати: я не говорю, коли сторінка завантажується (onload), оскільки шрифт можна було завантажити до цілої сторінки.


Сподіваємось, що незабаром у нас можуть бути власні події шрифтів blog.typekit.com/2013/02/05/more-reliable-font-events
Pacerier


2
Це питання є дублікатом, і я опублікував відповідь на оновлення 2015 року на вихідне питання.
Дан Даскалеску

Є рішення, яке використовує виявлення прокрутки за адресою smnh.me/web-font-loading-detection-without-timers
Pacerier

Відповіді:


44

Тепер на GitHub: https://github.com/patrickmarabeas/jQuery-FontSpy.js

По суті, метод працює, порівнюючи ширину рядка у двох різних шрифтах. Ми використовуємо Comic Sans як шрифт для тестування, оскільки він є самим різним із веб-безпечних шрифтів і, сподіваємось, досить відрізняється від будь-якого спеціального шрифту, який ви будете використовувати. Крім того, ми використовуємо дуже великий розмір шрифту, тому будуть помітні навіть невеликі відмінності. Коли обчислюється ширина рядка Comic Sans, сімейство шрифтів змінюється на ваш власний шрифт із відхиленням до Comic Sans. Якщо встановлено прапорець, якщо ширина рядкового елемента однакова, запасний шрифт Comic Sans все ще використовується. Якщо ні, ваш шрифт повинен працювати.

Я переписав метод виявлення навантаження шрифтів у плагін jQuery, розроблений, щоб дати розробнику можливість стилізувати елементи залежно від того, завантажений шрифт чи ні. Додано безпечний таймер, тому користувач не залишається без вмісту, якщо користувальницький шрифт не вдається завантажити. Це просто погана зручність використання.

Я також додав більший контроль над тим, що відбувається під час завантаження шрифту та при відмові з включенням додавання та видалення класів. Тепер ви можете робити зі шрифтом все, що вам подобається. Я рекомендував би лише змінити розмір шрифтів, міжрядковий інтервал тощо, щоб ваш шрифт, що повертається назад, був якомога ближчим до спеціального, щоб ваш макет залишався цілим, а користувачі отримували очікуваний досвід.

Ось демо: http://patrickmarabeas.github.io/jQuery-FontSpy.js

Перенесіть у файл .js файл із посиланням на нього.

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

Застосуйте це до свого проекту

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

});

Видаліть це FOUC!

.hideMe {
    visibility: hidden !important;
}

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

EDIT: сумісність FontAwesome вилучено, оскільки вона не працювала належним чином і зіткнулася з проблемами в різних версіях. Швидке виправлення можна знайти тут: https://github.com/patrickmarabeas/jQuery-FontFaceSpy.js/issues/1


Порівняння довжин шрифтів ... Це те, що робить WebFont Loader (див. Іншу відповідь)?
Pacerier

1
І в будь-якому випадку, порівняння довжин символів не буде працювати для багатьох шрифтів, оскільки багато шрифти "копіювання" розроблені з однаковою довжиною . Наприклад, Arial, Helvetica та Liberation Sans мають однакову ширину символів для всіх символів. Також див. En.wikipedia.org/wiki/Arial . Поки що здається, що перевірка пікселів за пікселем за допомогою полотна може бути єдиним надійним варіантом .....
надійним

1
Мені потрібно було використовувати це, щоб виправити проблему, з якою я стикався з iScroll, неправильно обчислюючи розміри елементів перед завантаженням шрифтів. Але я не використовую jQuery, тому зробив ванільну js-версію: github.com/keithswright/vanilla-fontspy, здається, працює на мене.
Кіт,

@Pacerier - зауважте, що лише вибраний вами тест і вибраний вами шрифт для завантаження повинні мати різну довжину. Отже, якщо в Comic Sans немає багато шрифтів, які мають однакову ширину символів, це все одно має працювати в більшості випадків.
BryanGrezeszak

20

Спробуйте WebFont Loader ( github repo ), розроблений Google та Typekit.

У цьому прикладі спочатку відображається текст шрифтом із зарубками; потім після завантаження шрифтів відображається текст вказаним шрифтом. (Цей код відтворює поведінку Firefox за замовчуванням у всіх інших сучасних браузерах.)


9

Тут інший підхід до рішень від інших.

Я використовую FontAwesome 4.1.0 для побудови текстур WebGL. Це дало мені ідею скористатися крихітним полотном, щоб зробити фа-квадрат, а потім перевірити піксель на цьому полотні, щоб перевірити, чи завантажилось воно:

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

Оскільки він використовує полотно, для нього потрібен досить сучасний браузер, але він може працювати на IE8, а також з поліфілом.


Я стикаюся з тією ж проблемою під час завантаження чудового шрифту в KonvaJS
Махді Алхатіб

4

Насправді, є хороший спосіб зрозуміти, що всі шрифти починають завантажуватися або завантажуватися повністю чи ні, і потрапляють у деякі помилки, але це стосується не лише певного шрифту , зверніть увагу на такий код:

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

А також є опція, яка повертається, Promiseі вона може працювати з .thenфункцією:

document.fonts.ready
 .then(() => console.log('do someting at the final with each status'))

Дякую. це повністю працює! Але мені потрібно ініціювати завантаження шрифту, розмістивши десь елемент <span>, використовуючи цей шрифт.
tyt2y3 06.03.20

не підтримується в IE11, який, на жаль, досі постачається в комплекті з Windows Server
Гай Пасі

@GuyPassy, ​​я не знаю, що таке IE11 !!
AmerllicA

4
@AmerllicA Я би хотів, щоб одного дня мені так повезло
Гай Пасі,

3

Ось ще один спосіб дізнатись, чи @ font-face вже завантажено, і взагалі не потрібно використовувати таймери: використовуйте подію "прокрутка", щоб отримати миттєву подію при зміні розміру ретельно створеного елемента.

Я написав допис у блозі про те, як це робиться, і опублікував бібліотеку на Github .


0

Спробуйте щось на зразок

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

а потім зробіть

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

Подія завантаження повинна запускатися після завантаження шрифтів.


Це те саме, $(function(){...})що виконується, коли завантажується вся сторінка.
Шанкар Кабус

1
це не те саме. Приклад hayk.mart спрацює, коли DOM (HTML) І ресурси на сторінці (CSS, JS, зображення, кадри) закінчаться завантаження. Ваш приклад, коли лише DOM закінчив завантаження.
Блез

Цікаво, чому ця відповідь голосує проти, швидкий пошук показує, що це правильний підхід, наприклад eager.io/blog/how-to-decide-when-your-code-should-run

-1

Це альтернативний підхід, який, принаймні, забезпечить завантаження чудового шрифту, а НЕ повне рішення OP. Оригінальний код можна знайти на форумах WordPress тут https://wordpress.stackexchange.com/a/165358/40636 .

Він агностичний і працюватиме з будь-яким ресурсом стилю шрифту, наприклад, awesome font, де можна перевірити сімейство шрифтів. Трохи подумавши, я впевнений, що це можна застосувати до набагато більше ...

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

Це резервний варіант у випадку, якщо Font-Awesome не завантажується (або якщо завантажується занадто повільно! ), Але не повідомляє , коли шрифт по закінченню завантаження.
Дан Даскалеску,

@DanDascalescu Я оновив відповідь, щоб чіткіше вказати, що це альтернативний підхід, який гарантує лише завантаження чудової бібліотеки шрифтів, а не повне рішення. Будемо сподіватися, що це трохи прояснить, оскільки я заробив кілька голосів проти попередньої ітерації.
oucil 01.03.16

-3

Використовуйте код нижче:

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

Може перевірити багато шрифтів:

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

Наведений нижче код працює лише в опері, але це легко:

if(!document.defaultView.getComputedStyle(document.getElementById('mydiv'))['fontFamily'].match(/myfont/i))
          alert("font do not loaded ");

Та сама ідея "відтворити на полотні", яку Ліфт опублікував роком раніше .
Дан Даскалеску,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.