От автора: Использование правила @font-face для загрузки кастомных (пользовательских) веб-шрифтов — это великолепная возможность создать для наших сайтов уникальный и запоминающийся стиль. Однако, когда кастомные шрифты применяются в вебе с использованием стандартных техник, то они могут снизить скорость загрузки и отрицательно сказаться на производительности (как реальной, так и воспринимаемой пользователями). К счастью, мы определили некоторые методы, внимательное применение которых обеспечит вашему сайту баланс юзабилити, производительности и стиля.
Проблема с правилом @font-face
Объявление CSS правила @font-face является стандартным подходом для указания кастомных шрифтов в вебе:
1 2 3 4 5 6 7 8 9 10 11 |
/* Определяем кастомный веб-шрифт */ @font-face { font-family: 'MyWebFont'; url('webfont.woff2') format('woff2'), url('webfont.woff') format('woff'), url('webfont.ttf') format('truetype'), } /* Используем данный шрифт на странице */ body { font-family: 'MyWebFont', sans-serif; } |
Просто и понятно, но, к сожалению, стандартная обработка правила @font-face в большинстве браузеров является проблематичной. Когда вы ссылаетесь на внешний веб-шрифт, используя @font-face, большинство браузеров сделают любой текст, к которому применяется данный шрифт, полностью невидимым на время загрузки данного внешнего шрифта. [См. рис. 1]. Некоторые браузеры подождут загрузки шрифта какое-то предустановленное количество времени (обычно три секунды), прежде чем покажут данный текст, используя альтернативный шрифт, указанный в свойстве font-family. Но браузеры на «движке» WebKit (Safari, стандартный браузер Android, Blackberry), прямо как преданные песики, будут ждать целую вечность (ладно, обычно 30 или более секунд) загрузки нужного шрифта. Это означает, что ваши кастомные шрифты представляют собой потенциальную «единую точку отказа» для доступного сайта.
Рис. 1: Скриншот веб-страницы, загружаемой в iOS Safari, где текст остается невидимым во время загрузки кастомных шрифтов.
Даже когда шрифты загружаются корректно, кастомные шрифты замедляют воспринимаемую пользователями скорость работы сайта, в основном из-за того, что страница, на которой много невидимого текста, не является полезной для пользователей. Конечно, если страница была уже посещена однажды, то кастомные шрифты будут закешированы и будут отображаться быстро, но воспринимаемая пользователями скорость при первом просмотре страницы является критическим параметром. Если наша страница не может быть отрисована за несколько секунд, то мы потеряем большое количество пользователей.
Например, на рис. 2 показана временная линия на сайте webpagetest.org, на которой видно, как сайт filamentgroup.com будет выглядеть при стабильном 3G соединении, если бы использовалось стандартное поведение при загрузке шрифтов. Обратите внимание на то, что текст, к которому применяется правило @font-face, начинает отображаться только спустя целую секунду после первичного рендеринга:
Рис. 2: Временная линия загрузки сайта Filament Group (использование стандартной загрузки шрифтов). При 3G соединении загрузка шрифтов задерживается на целую секунду.
Нашим пользователям нужна готовая страница, как можно раньше — в идеале, в течение одной секунды — т.е. нам нужно, чтобы текст начинал отображаться, как можно ближе к данному показателю. Есть несколько подходов, которые можно использовать в данном случае, но самое основное, что вы можете сделать, — это отказаться от стандартного способа загрузки шрифтов, о котором мы говорили ранее.
Вот критерии, которыми вы должны руководствоваться при оценке подхода по загрузке шрифтов:
CSS запрос, содержащий правило(а) font-face, не должен блокировать рендеринг страницы. Вместо подключения ваших шрифтов через элемент в области
или через @import во внешней таблице стилей, попробуйте загружать ваши шрифты асинхронно. Не беспокойтесь, мы покажем вам, как это делать.Запросы шрифтов должны быть настроены таким образом, чтобы во время их загрузки отображался альтернативный текст, что позволит избежать мерцаний/бликов при загрузке текста (the Flash of Invisible Text или FOIT).
Как загружают шрифты в Filament Group
Чтобы оптимизировать первое отображение текста, мы сначала убеждаемся в том, что в свойстве font-family помимо кастомного шрифта у нас указан еще и встроенный шрифт, в нашем случае font-family: Open Sans, sans-serif;. Это стадия, на которой определяется рендеринг текста с использованием запасного шрифта, пока происходит загрузка кастомного шрифта, согласно нашему новому методу загрузки. JavaScript может быть применен, чтобы определить наилучший для использования формат шрифта (WOFF2, WOFF, TTF) и для асинхронной загрузки таблицы стилей, в которой содержатся все шрифты, прописанные с помощью data: URI. Это немного нетрадиционный подход, но это позволяет нам загружать все кастомные шрифты за один HTTP запрос. А это прекрасно сказывается как на сокращении количества перерасчетов размеров и положения элементов на странице (все шрифты появляются сразу), так и на уменьшении количества HTTP запросов в целом. Мы можем пойти еще дальше, и, после запроса шрифта, установить куки (cookie), чтобы отметить, что кастомные шрифты были закешированы, и что мы можем избежать бликов/мерцаний стандартных шрифтов на последующих страницах.
ШАГ 1: ПОДГОТОВЬТЕ ВАШИ ШРИФТЫ
Кастомные шрифты могут быть очень тяжелыми, поэтому прежде всего нужно минимизировать количество шрифтов для загрузки. Помните, что каждый вариант насыщенности (regular, light, bold) и начертания (regular italic, bold italic) шрифта является отдельным файлом, который может быстро увеличить общий объем загружаемых шрифтов. Старайтесь, чтобы общее количество кастомных шрифтов не превышало 5 штук, но мы обычно, по возможности, стараемся использовать 2-3 шрифта.
Чтобы еще улучшить доставку шрифтов, используйте технику «разбиения» шрифта, которая позволяет вам убрать из шрифта буквы и символы, которые вам не потребуются. С помощью сервиса FontSquirrel это довольно легко сделать.
ШАГ 2: ПОДГОТОВЬТЕ ТАБЛИЦЫ СТИЛЕЙ СО ШРИФТАМИ
Кодирование шрифтов с помощью data: URI
Предположим, что вы используете шрифт Open Sans в двух вариантах насыщенности: 400 и 700 (жирный). Чтобы обеспечить поддержку наибольшего числа браузеров, нам потребуется по три разных формата для каждого варианта: WOFF2, WOFF и TrueType (TTF):
OpenSans-Regular.ttf
OpenSans-Bold.ttf
OpenSans-Regular.woff
OpenSans-Bold.woff
OpenSans-Regular.woff2
OpenSans-Bold.woff2
Если у вас не хватает, например, одного из этих форматов, то вы можете создать его с помощью генератора шрифтов Font Squirrel.
Затем возьмите каждый из этих файлов со шрифтом и закодируйте их для последующей вставки в таблицу стилей с помощью data:URI. Если вы не знакомы с тем, как это сделать, то существует много вариантов: SASS (Compass), PHP, онлайн-генераторы или OpenSSL в командной строке (openssl base64 -in filename.woff).
Скопируйте полученный результат в три отдельные таблицы стилей, один CSS файл для каждого формата: WOFF2 (data-woff2.css), WOFF (data-woff.css) и TTF (data-ttf.css для Android). Вот, например, как может выглядеть data-woff.css:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@font-face { font-family: Open Sans; src: url("data:application/x-font-woff;charset=utf-8;base64,...") format("woff"); font-weight: 400; font-style: normal; } @font-face { font-family: Open Sans; src: url("data:application/x-font-woff;charset=utf-8;base64,...") format("woff"); font-weight: 700; /* Жирный */ font-style: normal; } |
Внутри всех других файлов со шрифтами запись src: url(…) format(…) должна соответствовать конкретному формату. Например, внутри файла data-woff2.css у вас будет указано url(«data:application/font-woff2;charset=utf-8;base64,…») format(«woff2»);, а для data-ttf.css — url(«data:application/x-font-ttf;charset=utf-8;base64,…») format(«truetype»);
ШАГ 3: НАСТРОЙТЕ ЗАГРУЗЧИК ТАБЛИЦ СТИЛЕЙ
После того как файл со шрифтом подготовлен, нам потребуется загружать его асинхронно, чтобы избежать бликов/мерцаний текста. Мы используем для этого нашу утилиту loadCSS. Например, вот как мы можем использовать loadCSS для загрузки шрифтов в формате WOFF2:
1 |
loadCSS( '/url/to/data-woff2.css' ); |
Конечно, нам нужно загружать подходящую таблицу стилей со шрифтом для каждого браузера, посещающего наш сайт. Как нам определить, какой формат использовать? По умолчанию мы используем формат WOFF благодаря его широкой браузерной поддержке. Если браузер проходит проверку на поддержку формата WOFF2, мы используем формат WOFF2, потому что его размер обычно на 30% меньше. Если мы можем достаточно уверенно определить, что текущим браузером является стандартный браузер Android на «движке» Webkit (не Chrome), мы переключаемся на формат TTF с поддержкой для Android 4.X. Помните о том, что если будет загружен некорректный формат, то браузер просто будет использовать альтернативный вариант с локальными шрифтами.
Вот отрывок JavaScript кода, который мы используем для загрузки шрифтов. Мы рекомендуем размещать данный JavaScript код внутри элемента script в области head вашего HTML документа, чтобы как можно раньше отправить запрос шрифта (более подробная информация о том, как мы конфигурируем область head для наших страниц с помощью Enhance.js):
1 2 3 4 5 6 7 8 9 10 11 12 |
// Внимание!! Проверка поддержки формата WOFF2 и утилита loadCSS не приведены здесь для краткости var ua = window.navigator.userAgent; // Используем формат WOFF2, если он поддерживается if( supportsWoff2 ) { loadCSS( "/url/to/data-woff2.css" ); } else if( ua.indexOf( "Android 4." ) > -1 && ua.indexOf( "like Gecko" ) > -1 && ua.indexOf( "Chrome" ) === -1 ) { // Стандартный браузер Android использует формат TTF вместо WOFF loadCSS( "/url/to/data-ttf.css" ); } else { // По умолчанию используется WOFF loadCSS( "/url/to/data-woff.css" ); } |
Браузер не будет делать текст невидимым, поскольку наш CSS файл с data:URI загружается асинхронно. Это означает, что можно будет прочитать альтернативный текст, пока происходит загрузка наших веб-шрифтов (даже если запрос «повис» и ответа так и не было получено).
На рис. 3 показаны изменения: с помощью данной техники мы сразу же, при первичном рендеринге, получаем текст, который можно читать. Вот что мы получаем (временная линия при 3G соединении):
Рис. 3: Успех! Вот временная линия загрузки нашего сайта с использованием нашего способа загрузки кастомных шрифтов. Если используется 3G соединение, то шрифт уже отображается при первичном рендеринге.
Чтобы вы могли узнать подробнее о данном подходе, мы также подготовили для вас исходный код на GitHub.
Используем «куки», чтобы было еще лучше
На данный момент мы сфокусировались на подготовке и вдумчивой загрузке наших кастомных шрифтов. Таким образом, мы можем отображать альтернативный шрифт, пока мы ждем загрузки наших кастомных шрифтов. Когда нужные шрифты действительно загрузятся, браузер заменит стандартные шрифты на кастомные. Это вызовет переотрисовку и, как правило, небольшие сдвиги в разметке, т.к. шрифты имеют немного разные размеры. Мы считаем, что при первом посещении страницы подобная «жертва» является оправданной, поскольку мы в разы быстрее предоставляем пользователям готовую страницу, но подобная ситуация может раздражать пользователей при осуществлении навигации по сайту.
Чтобы решить эту проблему, мы используем куки, чтобы определить, загрузились ли кастомные шрифты и находятся ли они в кеше браузера. Если да, то мы сразу же показываем кастомные шрифты, чтобы избежать любых сдвигов в разметке.
Вместо загрузчика шрифтов нам потребуется другой загрузчик, который приведен ниже. Мы будем добавлять куки, чтобы отметить, что наши шрифты закешированы. Плюс, куки также содержат URL на конкретный формат шрифта (data-woff2.css, data-woff.css или data-ttf.css). Не забудьте использовать утилиту Filament Group для работы с куками:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Внимание!! Проверка поддержки формата WOFF2, утилиты loadCSS и cookie не приведены здесь для краткости // По умолчанию используется WOFF var fontFileUrl = "/url/to/data-woff.css", ua = window.navigator.userAgent; // Используем формат WOFF2, если он поддерживается if( supportsWoff2 ) { fontFileUrl = "/url/to/data-woff2.css"; } else if( ua.indexOf( "Android 4." ) > -1 && ua.indexOf( "like Gecko" ) > -1 && ua.indexOf( "Chrome" ) === -1 ) { // Стандартный браузер Android использует формат TTF вместо WOFF fontFileUrl = "/url/to/data-ttf.css"; } // Добавлено: Убедитесь в том, что шрифты еще не закешированы if( fontFileUrl && !cookie( "fonts" ) ) { // Загружаем шрифты асинхронно loadCSS( fontFileUrl ); // Добавлено: Устанавливаем куки, показывающие, что шрифты закешированы // Куки также указывают на используемый формат (WOFF, WOFF2 или TTF) cookie( "fonts", fontFileUrl, 7 ); } |
Затем добавляем следующий код с разметкой в область
, заменяя значения каждой переменной fontsWOFF, fontsWOFF2, fontsTTF на URL, содержащий адрес к CSS файлу со шрифтом. Обратите внимание на то, что когда куки были установлены и содержат значение URL, содержащий формат шрифта для загрузки, вставляется блокирующий элемент link, указывающий на CSS файл с data:URI. Однако, блокирующее поведение этого запроса в данном случае нормально, поскольку данный CSS запрос уже был закеширован браузером и будет загружен практически мгновенно.
1 2 3 4 5 6 7 8 9 10 |
<!--#set var="fontsWOFF" value="/css/data-woff.css" --> <!--#set var="fontsWOFF2" value="/css/data-woff2.css" --> <!--#set var="fontsTTF" value="/css/data-ttf.css" --> <!--#if expr="$HTTP_COOKIE=/fonts\=$fontsWOFF/" --> <link rel="stylesheet" href="<!--#echo var="fontsWOFF" -->"> <!--#elif expr="$HTTP_COOKIE=/fonts\=$fontsWOFF2/" --> <link rel="stylesheet" href="<!--#echo var="fontsWOFF2" -->"> <!--#elif expr="$HTTP_COOKIE=/fonts\=$fontsTTF/" --> <link rel="stylesheet" href="<!--#echo var="fontsTTF" -->"> <!--#endif --> |
Вышеприведенный код требует наличия специального модуля сервера Apache, но вы можете использовать нечто похожее на любом серверном языке.
Чтобы узнать больше о данном подходе с использованием кук, мы также подготовили для вас исходный код на GitHub.
Подводя итог
Использование веб-шрифтов может быть по-настоящему отличным способом улучшения качества вашего веб-проекта, но загрузка шрифтов обычным способом может быть очень губительной для воспринимаемой пользователями производительности сайта. Вышеприведенный метод отлично справляется с проблемой невидимого текста, ассоциирующейся обычно с правилом @font-face, и гораздо быстрее делает наши страницы доступными для пользователей. Мы надеемся, что для вас (и ваших пользователей) это окажется полезным!
Автор: Zach Leatherman
Источник: //www.filamentgroup.com/
Редакция: Команда webformyself.