Правильная загрузка шрифтов на сайт

Опубликовано 29 декабря 2021 в 22:07 (Обновлено 13 ноября 2023 в 00:44)

Время чтения: 5 мин

Веб-шрифты великолепны и делают Интернет более красивым пространством, однако их загрузка может быть медленной, что приводит к нежелательным побочным эффектам, которые в англоязычном интернете называют FOIT (Flash of Invisible Text), т.е. мерцающий или невидимый текст.

FOIT
FOIT

Что такое FOIT?

Часто этот эффект можно увидеть на своем сайте, особенно при мобильном подключении. С помощью некоторых действий, ряда ресурсов, немного JavaScript и CSS, может получиться это исправить.

FOIT является результатом тайм-аута браузера, когда текст отображается шрифтом по умолчанию после попытки загрузки веб-шрифта.

При попытке загрузить веб-шрифт браузер скрывает текст в течение некоторого периода времени, обычно несколько секунд, но известно, что это может дойти и до 30 секунд.

Вот как выглядела FOIT на моем сайте, когда я загружал шрифты с правилами CSS @font-face:

Обратите внимание, что при загрузке страницы текст становится невидимым в кадрах 2 и 3 (тип устанавливается в Helvetica в кадре 1 и в Roboto в кадре 4), что делает содержимое страницы нечитаемым.

Хотя во многих случаях FOIT длился всего 2–4 секунды, это определенно заметно. Я также столкнулся с наихудшим сценарием, когда контент постоянно невидим.

Цель здесь - как можно быстрее донести контент до пользователей за счет FOUT (Flash of Unstyled Text) и визуальных изменений, которые происходят, когда становится доступным шрифт.

Определяем момент загрузки шрифтов

Именно здесь появляется Font Load Events, которое предназначено именно для этого. Брэм Стейн создал Font Face Observer, облегченный (2.7KB минимизированный, 1.1KB gzip) полифилл для API событий шрифтов, чтобы вы могли определить, были ли и когда загружены шрифты.

В качестве альтернативы я мог бы использовать полнофункциональный загрузчик шрифтов, такой как Web Font Loader от Google и Typekit, но я решил пойти с Font Face Observer в пользу его веса и подхода.

Загружайте свои шрифты, как обычно (будь то пользовательские правила @font-face в вашем CSS или используя службу шрифтов, такую как Google Fonts или Typekit). Затем настройте Font Face Observer для каждого семейства шрифтов:

var observer = new FontFaceObserver("Font Name", {
  weight: 400
});

observer.check().then(function() {
  console.log("Font is available");
}, function() {
  console.log("Font is not available");
});

check() начинает наблюдать за загрузкой шрифта, а then() обрабатывает обратный вызов, когда он завершается (что делается через Promise).

Насчет Font Face Observer следует отметить, что по умолчанию он перестает работать, если загрузка шрифта превышает 3 секунды. Вы можете изменить продолжительность тайм-аута, передав целое число в миллисекундах в качестве второго параметра check():

observer.check(null, 5000).then(function() {
  console.log("Font is available");
}, function() {
  console.log("Font is not available after waiting 5 seconds");
});

Узнайте больше о том, как использовать Font Face Observer, прочитав его документацию.

Прогрессивная загрузка шрифтов с помощью Font Events

Используя события шрифта (font events) с Font Face Observer, укажите резервный шрифт для body при загрузке шрифтов, а затем добавьте класс в <html> после завершения загрузки шрифтов.

body {
  font-family: Helvetica, Arial, sans-serif;
}

.fonts-loaded body {
  font-family: "Roboto", Helvetica, Arial, sans-serif;
}

Javascript:

var roboto = new FontFaceObserver("Roboto", {
  weight: 400
});

roboto.check().then(function() {
  document.getElement.className += "fonts-loaded";
});

Вы также можете подключить это к нескольким семействам шрифтов и/или к свойству weight, что я смог выяснить с помощью умных людей из Filament Group и их публикации, написанной Скоттом Джелом:

var roboto400 = new FontFaceObserver("Roboto", {
  weight: 400
});
var roboto500 = new FontFaceObserver("Roboto", {
  weight: 500
});
var roboto700 = new FontFaceObserver("Roboto", {
  weight: 700
});

Promise.all([
  roboto400.check(),
  roboto500.check(),
  roboto700.check()
]).then(function() {
  document.documentElement.className += " fonts-loaded";
});

Этот метод также использует Promise. Имейте в виду, что если вы не используете автономную версию Font Face Observer (которая поставляется вместе с полифиллом Promise), обязательно включите ее отдельно.

Результат? Никаких признаков FOIT!

Вы можете видеть, что хотя Roboto был доступен только в 3-м кадре, текст был виден все время.

Ещё одно замечание о Font Face Observer - если соединение крайне медленное и он перестаёт проверять загрузку шрифта, приведённое выше решение приведёт к тому, что документ останется в резервном состоянии (в этом случае текст останется установленным в Helvetica, Arial, sans-serif).

Мы можем использовать ловушку, чтобы дать элементу html уникальное имя класса для обработки тайм-аута.

Promise.all([
  roboto400.check(),
  roboto500.check(),
  roboto700.check()
]).then(function() {
  document.documentElement.className += " fonts-loaded";
}, function() {
  document.documentElement.className += " fonts-unavailable";
});

Локальные шрифты

Загрузив шрифты с правилами @font-face и событиями шрифтов, мы устранили FOIT, но пользователь все равно заметит FOUT (Flash of Unstyled Text).

Идеальной ситуацией является правильный стиль текста в момент загрузки, что требует от пользователя локальной установки шрифта.

Несмотря на то, что есть лишь небольшая вероятность того, что пользователь может установить его, мы можем работать над решением, чтобы приспособиться к идеальной ситуации, пытаясь ссылаться на локальный шрифт и используя запасные шрифты, если он не справится с FOUT, и все это при одновременном устранении FOIT.

Наряду с решением, представленным выше, давайте также создадим пользовательское имя семейства шрифтов font-family, используя пользовательское правило @font-face, которое ссылается на локальный шрифт в src. Затем включим это имя в качестве первого из стека семейства шрифтов font-family. Строка внутри local(") должна быть именем семейства шрифтов и/или именем PostScript.

@font-face {
  font-family: "RobotoLocal";
  font-style: normal;
  font-weight: 400;
  src: local("Roboto"),
       local("Roboto-Regular");
}

body {
  font-family: "RobotoLocal", Helvetica, Arial, sans-serif;
}

.fonts-loaded body {
  font-family: "Roboto", Helvetica, Arial, sans-serif;
}

Это гарантирует, что локальный шрифт будет использоваться до завершения события шрифта (font even), если локальный шрифт доступен, и если он доступен, мы исключили как FOIT, так и FOUT.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.