本文做者來自 360 奇舞團的前端開發工程師何文力,同時也是 W3C CSS 工做組的成員javascript
當咱們在瀏覽一些使用自定義字體的網站,或在開發中使用 @font-face
設置自定義字體時,時常會看到一個現象:頁面結構和圖片出來了,但文字區域是空白的。這種現象被稱之爲 FOIT (Flash Of Invisible Text)。css
一般,咱們經過@font-face
規則定義讓瀏覽器加載使用第三方字體。這些寫在 CSS 文件中的規則,瀏覽器必須待文件下載結束並解析以後才能開始下載字體文件。而要真正地觸發字體文件下載,還要知足一些條件,根據 Zach Leatherman ① 的這篇文章,要觸發字體下載,還要知足如下的條件:前端
@font-face
規則,而且當前瀏覽器須要支持 src
列表中給出的格式@font-face
中相同的 font-family
font-family
的節點不能爲空@font-face
中指定了 unicode-range
,出現的文字內容還必須落在設定的 Unicode 範圍中當上述全部條件知足,瀏覽器纔會開始下載字體文件,這也意味着,瀏覽器不單單須要解析 CSS 內容,還要解析頁面內容才能決定是否須要下載字體。當瀏覽器開始下載字體,使用了該 font-family
的全部文本被隱藏,致使頁面出現文本空白的狀況。java
在字體相關的 W3C 標準中,CSS Fonts Module Level 3 中的 font-display
屬性以及 CSS Font Loading API 標準能夠解決相關問題。web
font-display
屬性添加於 CSS Fonts Module Level 3 中,已有大部分瀏覽器支持該屬性。api
font-display
在 CSS 層面上提供了此類問題的解決方法,它提供了五個屬性:瀏覽器
swap
屬性值行爲上大體相同,但瀏覽器會給設定的字體設定加載的時間限制,一旦加載所需的時長大於這個限制,設定的字體將不會替換備用字體進行顯示。 Webkit 和 Firefox 中設定此時間爲 3s;除了 CSS 層面上解決問題,CSS Font Loading API ② 在 JavaScript 層面上也提供瞭解決方案。經過監聽加載事件,咱們能夠在字體加載完成後經過替換 class 達到 CSS 中 swap
屬性值的效果。字體
瀏覽器支持方面也仍是通常優化
FontFace
接口支持狀況網站
FontFaceSet
接口支持狀況
標準中主要提供了FontFace
接口加載字體,而且 document.fonts 對象爲一個 FontFaceSet
接口,他是一組FontFace
的集合,管理了頁面上全部字體的狀態。
FontFace
接受三個參數:font-family
名稱、字體資源位置以及字體設定(可選)。
首先,要在 JavaScript
中加載字體,咱們要new
一個 FontFace
並將其添加到全局 FontFaceSet
中:
const Aclonica = new FontFace('Aclonica', 'url(./Aclonica.ttf)');
// 添加到全局的 FontFaceSet 中
document.fonts.add(Aclonica);
複製代碼
第二步:調用 FontFace
的 load
方法開始加載,load
方法將返回一個 Promise
。當咱們的字體加載完以後,就能夠經過變換 class 的換上新字體。
Aclonica.load().then(() => {
// 當字體加載完以後,咱們就能夠經過替換 class 的方法替換掉默認的字體
// 此處的邏輯也能夠是你的字體渲染策略
document.body.classList.add('use-aclonica');
})
複製代碼
.use-aclonica {
font-family: Aclonica;
}
複製代碼
咱們經過上述方法進行優化以後,雖然沒有了 FOIT 現象,可是實際效果倒是這樣的:
咱們發現,字體加載完以後頁面仍是不可避免地閃了,這是因爲備用字體和定義的字體外形相差過大致使的視覺效果,這種現象又被稱之爲 FOUT (Flash Of Unstyled Text)。對於正在閱讀文章的用戶來講,顯然不是很好的體驗。
CSS Fonts Module 以及 CSS Font Loading API 這兩組標準都給前端字體不管在渲染上仍是控制上賦予了更多能力,隨着咱們更深刻地研究規範,相信會給字體加載以及渲染方面的問題帶來更多的解決方案。
① www.zachleat.com/web/compreh…
② drafts.csswg.org/css-font-lo…
感謝李鬆峯老師對本文提出的修改建議