今天,在 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,所以字體依次變粗的情況,一共只有兩種字重:
- 當
font-weight:
處於 100 - 500 的時候,其實都是font-weight: normal
; - 當
font-weight: 600
的時候,其實是命中了font-weight: bold
。
這個也就是傳統靜態字體的局限性,單一字體文件中,其實是不會有該字體的所有粗細、字寬的類型的。
可變字體的多樣性
接下來,我們換上可變字體。
加載可變字體的語法與其他 web 字體非常相似,但有一些顯著的差異,這些差異是通過對現代瀏覽器中可用的傳統 @font-face 語法的升級提供的。
基本語法是相同的,但是字體技術是不一樣的,並且可變字體可以提供像對 font-weight
和 font-stretch
等描述符的允許范圍,而不是根據加載的字體文件來命名。
下面,我們將加載一個支持字體粗細從 100
到 900
,字體伸縮變形支持從 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: 100
到 font-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 個注冊軸,並且簡單介紹了它們的使用。再羅列一次:
- 字重軸 "wght":對應
font-weight
,控制字體的粗細 - 寬度軸 "wdth":對應
font-stretch
,控制字體的伸縮 - 斜度軸 "slnt" (slant):對應字體的
font-style: oblique + angle
,控制字體的傾斜 - 斜體軸 "ital":對應字體的
font-style: italic
,控制字體的傾斜(注意,和font-style: oblique
是不一樣的傾斜) - 光學尺寸軸 "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 技術文章匯總在我的 Github -- iCSS ,持續更新,歡迎點個 star 訂閱收藏。
如果還有什么疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。