突破限制,CSS font-variation 可變字型的魅力

chokcoco發表於2022-02-28

今天,在 CodePen 上看到一個很有意思的效果 -- GSAP 3 ETC Variable Font Wave,藉助了 JS 動畫庫 GSAP 實現,一起來看看:

我尋思著能否使用 CSS 復刻一版,鼓搗了一會,利用純 CSS 成功實現了原效果。

上述效果,最核心的就是文字的動畫,文字從較細貼著較緊,到較粗隔著較遠不斷變化。有人會認為這裡是 transform: scale(),實則不然。

scale 是等比例放大縮小一個物體,而仔細觀察上述效果,明顯是有字型的粗細、字型的字寬的變化。這裡,其實用到了 CSS 比較新的特性 -- 可變字型,也就是 font-variation

本文,將藉助這個效果,介紹一下什麼是 CSS font-variation。

什麼是 CSS font-variation,可變字型?

根據 MDN -- Variable fonts可變字型(Variable fonts)是 OpenType 字型規範上的演進,它允許將同一字型的多個變體統合進單獨的字型檔案中。從而無需再將不同字寬、字重或不同樣式的字型分割成不同的字型檔案。我們只需通過CSS與一行 @font-face 引用,即可獲取包含在這個單一檔案中的各種字型變體。

emm,概念有點難理解,簡單解釋一下。

與可變字型對應的,是標準(靜態)字型

標準(靜態)字型就是隻代表字型的某一特定的寬度/字重/樣式的組合的字型檔案,通常我們在頁面引入的字型檔案都是這種,只代表這個字型的某一特定的寬度/字重/樣式的組合。

而如果我們想引入一個字型家族(譬如 Roboto 字型族),它可能包含了 “Roboto Regular”(常規字重)、“Roboto Bold”(粗體),或是 “Roboto Bold Italic”(粗體+斜體) 等一系列字型檔案。這意味著我們可能需要 20 或 30 個不同的字型檔案才能算是有了一整個字型家族(對於有著不同寬度的大型字型來說,這個數量還要翻上幾倍)。

而可變字型 -- font-variation,可以將它理解為 all in one,通過使用可變字型,所有字重、字寬、斜體等情況的排列組合都可以被裝進一個檔案中。當然,這個檔案可能比常規的單個字型檔案大一些。

靜態字型的侷限性

舉個例子,在 Google Font,我隨便選取一個標準靜態字型,實現一個字型 font-weight 的變化動畫:

<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
<p>CSS font-variation</p>
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300&display=swap');
p {
    font-family: 'Lato', sans-serif;
    font-size: 48px;
}
p:nth-child(1) {
    font-weight: 100;
}
p:nth-child(2) {
    font-weight: 200;
}
p:nth-child(3) {
    font-weight: 300;
}
p:nth-child(4) {
    font-weight: 400;
}
p:nth-child(5) {
    font-weight: 500;
}
p:nth-child(6) {
    font-weight: 600;
}

看看結果:

並沒有我們想象中的,因為字型粗細從 100 到 600,所以字型依次變粗的情況,一共只有兩種字重:

  1. font-weight: 處於 100 - 500 的時候,其實都是 font-weight: normal;
  2. font-weight: 600 的時候,其實是命中了 font-weight: bold

這個也就是傳統靜態字型的侷限性,單一字型檔案中,其實是不會有該字型的所有粗細、字寬的型別的。

可變字型的多樣性

接下來,我們換上可變字型。

載入可變字型的語法與其他 web 字型非常相似,但有一些顯著的差異,這些差異是通過對現代瀏覽器中可用的傳統 @font-face 語法的升級提供的。

基本語法是相同的,但是字型技術是不一樣的,並且可變字型可以提供像對 font-weightfont-stretch 等描述符的允許範圍,而不是根據載入的字型檔案來命名。

下面,我們將載入一個支援字型粗細從 100900,字型伸縮變形支援從 10%400%AnyBody 可變字型。

@font-face {
    font-family: 'Anybody';
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
    font-display: block;
    font-style: normal italic;
    font-weight: 100 900;
    font-stretch: 10% 400%;
}
p {
    font-family: 'Anybody', sans-serif;
    font-size: 48px;
}
p:nth-child(1) {
    font-weight: 100;
}
// ...
p:nth-child(6) {
    font-weight: 600;
}

同樣是設定字型粗細從 100 - 600,效果如下:

這一次,可以看到,字型有明顯的均勻變化,支援 font-weight: 100font-weight: 600 的逐漸變化。這兒就是可變字型的魅力。

理解 font-variation-settings

除了直接通過 font-weight 去控制可變字型的粗細,CSS 還提供了一個新的屬性 font-variation-settings 去同時控制可變字型的多個屬性。

可變字型新格式的核心是可變軸的概念,其描述了字型設計中某一特性的允許變化範圍。

所有可變字型都有至少有 5 個可以通過 font-variation-settings 控制的屬性軸,它們屬於註冊軸(registered),能夠對映現有的 CSS 屬性或者值。

它們是:

  • 字重軸 "wght":對應 font-weight,控制字型的粗細
  • 寬度軸 "wdth":對應 font-stretch,控制字型的伸縮
  • 斜度軸 "slnt" (slant):對應字型的 font-style: oblique + angle,控制字型的傾斜
  • 斜體軸 "ital":對應字型的 font-style: italic,控制字型的傾斜(注意,和 font-style: oblique 是不一樣的傾斜)
  • 光學尺寸軸 "opsz":對應字型的 font-optical-sizing,控制字型的光學尺寸

好吧,可能會有一點點懵,沒事,通過一個例子馬上就能理解什麼意思。

還是利用上述的可變字型,我們利用 font-variation-settings 實現一個字型粗細的變化的動畫:

<p>Anybody</p>
@font-face {
    font-family: 'Anybody';
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
    font-display: block;
    font-style: normal italic;
    font-weight: 100 900;
    font-stretch: 10% 400%;
}
p {
    font-family: 'Anybody';
    font-size: 48px;
    animation: fontWeightChange 2s infinite alternate linear;
}
@keyframes fontWeightChange {
    0% {
        font-variation-settings: 'wght' 100;
    }
    100% {
        font-variation-settings: "wght" 600;
    }
}

效果如下:

其中,其實可以理解為,利用了 font-variation-settings: "wght" 的變化的動畫,等同於 font-weight 變化動畫:

利用 font-variation-settings 進行字型的多個特徵同時變化

OK,那麼如果既然是一樣的效果,為什麼還要多此一舉搞個 font-variation-settings 呢?

那是因為 font-variation-settings 除了支援字型的粗細變化外,還支援上述說的註冊軸設定的多個樣式屬性變化,以及自定義軸的一些字型樣式屬性變化。

這次,除了字型粗細外,我們再新增上 "wdth" 的變化,也就是字型的伸縮。

<p>Anybody</p>
@font-face {
    font-family: 'Anybody';
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
    font-display: block;
    font-style: normal italic;
    font-weight: 100 900;
    font-stretch: 10% 400%;
}
p {
    font-family: 'Anybody';
    font-size: 48px;
    animation: fontWeightChange 2s infinite alternate linear;
}
@keyframes fontWeightChange {
    0% {
        font-variation-settings: 'wght' 100, 'wdth' 60;
    }
    100% {
        font-variation-settings: "wght" 600, 'wdth' 400;
    }
}

這次,進行的是字型粗細從 100 到 600,字型伸縮從 60% 到 400% 的動畫效果,效果如下:

也就是說,font-variation-settings 是同時支援多個字型樣式一起變化的,這一點非常重要。

到這裡,其實我們已經可以利用這個實現題圖所示的效果了,我們簡單改造下,新增多行,再給每行設定一個負的動畫延遲即可:

<div class="g-container">
    <ul>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
        <li>ANYBODY</li>
    </ul>
</div>

藉助 SCSS 簡化下程式碼,下述程式碼核心就是給每個 li,新增一個相同的動畫 font-variation-settings 動畫,並且依次設定了等差的 animation-delay

li {
    animation: change 0.8s infinite linear alternate;
}
@for $i from 1 to 9 {
    li:nth-child(#{$i}) {
        animation-delay: #{($i - 1) * -0.125}s;
    }
}
@keyframes change {
    0% {
        font-variation-settings: 'wdth' 60, 'wght' 100;
        opacity: .5;
    }
    100% {
        font-variation-settings: 'wdth' 400, 'wght' 900;
        opacity: 1;
    }
}

效果如下:

好,接下來,利用 CSS 3D 簡單構造一下 3D 場景即可,完整的 CSS 程式碼如下:

@font-face {
    font-family: 'Anybody';
    src: url('https://s3-us-west-2.amazonaws.com/s.cdpn.io/61488/ETCAnybodyPB.woff2') format('woff2-variations');
    font-display: block;
    font-style: normal italic;
    font-weight: 100 900;
    font-stretch: 10% 400%;
}
.g-container {
    position: relative;
    margin: auto;
    display: flex;
    font-size: 48px;
    font-family: 'Anybody';
    color: #fff;
    transform-style: preserve-3d;
    perspective: 200px;
}
ul {
    background: radial-gradient(farthest-side at 110px 0px, rgba(255, 255, 255, 0.2) 0%, #171717 100%);
    padding: 5px;
    transform-style: preserve-3d;
    transform: translateZ(-60px) rotateX(30deg) translateY(-30px);
    animation: move 3s infinite alternate;
    
    &::before {
        content: "";
        position: absolute;
        left: 0;
        bottom: 0;
        right: 0;
        height: 45px;
        background: #141313;
        transform: rotateX(-230deg);
        transform-origin: 50% 100%;
    }
}
li {
    width: 410px;
    animation: change 0.8s infinite linear alternate;
}
@for $i from 1 to 9 {
    li:nth-child(#{$i}) {
        animation-delay: #{($i - 1) * -0.125}s;
    }
}
@keyframes change {
    0% {
        font-variation-settings: 'wdth' 60, 'wght' 100;
        opacity: .5;
    }
    100% {
        font-variation-settings: 'wdth' 400, 'wght' 900;
        opacity: 1;
    }
}
@keyframes move {
    100% {
        transform: translateZ(-60px) rotateX(30deg) translateY(0px);
    }
}

效果如下,我們就基本還原了題圖的效果:

完整的程式碼及 DEMO 效果你可以戳這裡:CodePen Demo -- Pure CSS Variable Font Wave

font-variation 的可變軸 -- 註冊軸與自定義軸

迴歸到可變字型本身。上面提到了可變軸這個概念,它們分為註冊軸自定義軸,英文是:

  • 註冊軸 - registered axes
  • 自定義軸 - custom axes

可變字型新格式的核心是可變軸的概念,其描述了字型設計中某一特性的允許變化範圍。

例如‘字重軸’描述了字型的粗細;“寬度軸”描述了字型的寬窄;“斜體軸”描述是否使用斜體字形並且可相應地開關;等。請注意,軸既可以是範圍選擇又可以是開關選擇。字重可能在1-999之間,而斜體可能只是簡單的0或1(關閉或開啟)。

如規範中所定義,存在兩種變形軸,註冊軸和自定義軸:

  • 註冊軸最為常見,常見到制定規範的作者認為有必要進行標準化。 目前註冊的五個軸是字重,寬度,傾斜度,斜體和光學尺寸。

上文其實已經羅列了 5 個註冊軸,並且簡單介紹了它們的使用。再羅列一次:

  1. 字重軸 "wght":對應 font-weight,控制字型的粗細
  2. 寬度軸 "wdth":對應 font-stretch,控制字型的伸縮
  3. 斜度軸 "slnt" (slant):對應字型的 font-style: oblique + angle,控制字型的傾斜
  4. 斜體軸 "ital":對應字型的 font-style: italic,控制字型的傾斜(注意,和 font-style: oblique 是不一樣的傾斜)
  5. 光學尺寸軸 "opsz":對應字型的 font-optical-sizing,控制字型的光學尺寸
  • 自定義軸實際上是無限的:字型設計師可以定義和界定他們喜歡的任何軸,並且只需要給它一個四個字母的標籤以在字型檔案格式本身中識別它。

我們來看一個 自定義軸 的例子:

<p>Grade</p>
p {
    font-family: "Amstelvar VF", serif;
    font-size: 64px;
    font-variation-settings: 'GRAD' 88;
}

上述 font-family: "Amstelvar VF" 是一個可變字型,而 'GRAD' 屬於自定義軸的一個,意為等級軸

  • 等級軸 'GRAD':“等級”一詞指的是字型設計的相對重量或密度,但與傳統的“重量”不同之處在於文字佔據的物理空間不會改變,因此改變文字等級並不會改變文字或其周圍元素的整體佈局。 這使得等級成為有用的變化軸,因為它可以變化或動畫而不會引起文字本身的迴流。

MDN 上有關於改變 'GRAD' 的值,對應字型變化的一個 DEMO,效果如下:

值得注意的是,自定義軸可以是字型設計師想象的任何設計變化軸。可能有一些會逐漸變得相當普遍,隨著規範的發展甚至演變成註冊軸。

去哪找可變字型?

OK,如果現在我想在業務中使用一下可變字型,去實現一個效果或者動畫,可以上哪裡尋找可變字型的資源呢?

這裡有一個很不錯的網站 -- Variable Fonts

上面收集了非常多的 Variable Fonts,並且羅列出了它們在註冊軸上支援的字型屬性的範圍,譬如支援字重從 100 到 700,我們可以自由進行除錯預覽

Can i Use(2022-02-20)

現在能夠開始使用可變字型了嗎?

截止至今天,Can i Use 的截圖:

相容性已經非常的不錯了,不考慮 IE 系列的話可以上到實際的生產環境中去。

最後

目前關於 CSS Font Variation 的相關介紹非常少,如果你對它非常感興趣,這裡還有一篇非常好的文章值得細讀:Introduction to variable fonts on the web

本文到此結束,希望對你有幫助 :)

想 Get 到最有意思的 CSS 資訊,千萬不要錯過我的公眾號 -- iCSS前端趣聞 ?

更多精彩 CSS 技術文章彙總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。

如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

相關文章