用 CSS 實現 Netflix Logo 動畫
本文譯自: Netflix Logo In CSS
這篇博客是 Gregor Adams 講他如何用 CSS 重現 Netflix 商標效果。Gregor 是 CSS 方面冉冉升起的新星。能在這里分析他的案例也是非常榮幸的。
我嘗試使用 Netflix(譯者注:一家在線影片租賃提供商)時,立即就把我吸引住了。我觀看了一些不得不在它處才能觀看的節目。每一集電視劇或者電影都以 Netflix 動畫作為開始。

在觀看了幾集電視劇之后,我想到可以用 CSS 來實現 Netflix 的 logo 動畫,于是我看了幾部作品之后,就用 CodePen 來重現這個 logo。
第一個概念
因為我想要嘗試某些技術方案導致我的第一種實現方式有些累贅。
例如:我想使用純 CSS 技術來實現它,并且我也想當我點擊按鈕的時候,這個動畫再執行一次,所以我要使用一些不可思議技巧。幸運的是當我寫 CSS 代碼的時候,總會有一些小技巧會在我的腦海里涌現。
我們來談論一下實際的動畫。
我錄下這個動畫并且在 Quicktime 中循環播放,這樣可以詳細檢查。我傾向于這么做,能讓我停在某些特定幀弄清楚到底發生了什么。
這個商標:
- 以一個白屏幕開始。
- 彈出白色的 3D 字母。
- 投射陰影。
- 消失。
- 把字體顏色變成紅色。
這就是我需要重現的動畫步驟。但是這里有另外一些關于這個 logo 的東西需要解決: 字母在商標中心是傾斜的。
大家一直問我如何做到這些。 都是從積累中獲取地 :)
我做過許多 3D 案例,所以這對我來說不是很難。
使字母變傾斜
以這個詞 “Netflix” 的一些基本標記開始。
<div class="logo">
<span>N</span>
<span>E</span>
<span>T</span>
<span>F</span>
<span>L</span>
<span>I</span>
<span>X</span>
</div>
我用類 logo 做了一個包裹,并且用 span 標簽包裹每一個字母。
然后我在Y軸上旋轉這個字母并且在 X 軸上縮放這個字母以保持它的原始寬度。重要的部分是在 class=”logo” 包裝上設置一個 perspective ,并且定義它的 perspective-origin 。
/* 基礎的字母樣式 */
span {
font-size: 8em;
font-family: impact;
display: block;
}
/* 開啟三維效果 */
.logo {
perspective: 1000px;
perspective-origin: 50% 0;
}
/* 給字母變換 */
.logo span {
transform-origin: 0 0;
transform: scaleX(80) rotateY(89.5deg);
}
這里還有一些其它的方式來實現這些技巧,例如使用一個不同 perspective(比如500px),旋轉角度(比如9deg)和縮放(比如0.5),但是這些值能最大滿足我的需求。
下面是在 CodePen 實現的小例子:(譯者注:原 demo 是頁面中嵌入的 iframe 實現嵌入 CodePen ,但是 markdown 沒有嵌入 iframe 的方法,所以采用 CodePen 來展示,并且把原 demo 的 jade 和 scss 寫法轉換成 html 和 css 方便沒有使用過兩種技術的讀者閱讀)
實際效果
接下來我要對所有的字母應用這個樣式,中間的字母不要變化。右邊的字母朝著相反的方向傾斜,并且字母高度發生變化。
為了實現這些需要增加一些新邏輯:我使用 Sass 的標準語法來實現。
Sass 代碼:
.logo {
perspective: 1000px;
perspective-origin: 50% 0;
font-size: 8em;
display: inline-flex;
span {
font-family: impact;
display: block;
$letters: 7;
@for $i from 1 through $letters {
$offset: $i - ceil($letters / 2);
$trans: if($offset > 0, -89.5deg, 89.5deg);
&:nth-child(#{$i}) {
// trans/de-form the letters transform-origin: 50% + 50%/$offset 200%;
font-size: if($offset == 0,
0.85em,
0.9em + 0.015*pow(abs($offset),2));
transform:
if($offset == 0, scale(1, 1), scale(95.9 - abs($offset) * 10, 1))
if($offset == 0, translatey(0%), rotatey($trans));
}
}
}
}
為了方便不懂 scss 同學理解,這是我編譯后的 css 代碼:
.logo {
perspective: 1000px;
perspective-origin: 50% 0;
font-size: 8em;
display: inline-flex;
}
.logo span {
font-family: impact;
display: block;
}
.logo span:nth-child(1) {
transform-origin: 33.33333333% 200%;
font-size: 1.035em;
transform: scale(65.9, 1) rotatey(89.5deg);
}
.logo span:nth-child(2) {
transform-origin: 25% 200%;
font-size: 0.96em;
transform: scale(75.9, 1) rotatey(89.5deg);
}
.logo span:nth-child(3) {
transform-origin: 0% 200%;
font-size: 0.915em;
transform: scale(85.9, 1) rotatey(89.5deg);
}
.logo span:nth-child(4) {
transform-origin: Infinity% 200%;
font-size: 0.85em;
transform: scale(1, 1) translatey(0%);
}
.logo span:nth-child(5) {
transform-origin: 100% 200%;
font-size: 0.915em;
transform: scale(85.9, 1) rotatey(-89.5deg);
}
.logo span:nth-child(6) {
transform-origin: 75% 200%;
font-size: 0.96em;
transform: scale(75.9, 1) rotatey(-89.5deg);
}
.logo span:nth-child(7) {
transform-origin: 66.66666667% 200%;
font-size: 1.035em;
transform: scale(65.9, 1) rotatey(-89.5deg);
}
下面是在 CodePen 實現的小例子:(譯者注:原 demo 是頁面中嵌入的 iframe 實現嵌入 CodePen ,但是 markdown 沒有嵌入 iframe 的方法,所以采用 Codepen 來展示,并且把原 demo 的 jade 和 scss 寫法轉換成 html 和 css 方便沒有使用過兩種技術的讀者閱讀)。
實際效果:
一個用于陰影的函數
一個用于陰影的函數寫一個實現 3d 效果和陰影的函數。我把視頻停在某一幀,并仔細查看細節。
正如你所看到的,當這個陰影到達右下角,3d 效果的消失點在中間。現在知道我們函數需要做什么了。
我們將會在 keyframes 中調用這個函數,所以我們希望他能處理一些值,例如:
colorxyblurmix
我們還需要一個參數來定義陰影的深度或者 3d 效果。
下面就是用來處理這些需求的函數:
/// 在特定方向創創建三維陰影 /// @author Gregor Adams /// @param {Number} $depth - 陰影長度 /// @param {Unit} $color - 陰影顏色 /// @param {Unit} $x - 在x軸上到下一個陰影的距離 /// @param {Unit} $y - 在y軸上到下一個陰影的距離 /// @param {Unit} $blur - text-shadow的模糊距離 /// @param {Color|false} $mix - 添加一個可選的顏色來混合 /// @return {List} - 返回text-shadow列表 @function d3($depth, $color, $x: 1px, $y: 1px, $blur: 0, $mix: false) {
$shadow: ();
@for $i from 1 through $depth {
// append to the existing shadow @if type-of($mix) != 'color' {
$shadow: append($shadow, round($i * $x) round($i * $y) $blur $color, comma);
} @else {
$shadow: append($shadow, round($i * $x) round($i * $y) $blur mix($mix, $color, 0.3%*$i), comma);
}
}
@return $shadow;
}
這個函數對于 Sass 菜鳥或者只使用基本語言特性的開發者和設計師來說可能有點難理解,所以讓我來詳細解釋一下。
我以一個 $shadow 的變量開始, list 是一個空的列表。
$shadow: ();
我是從1開始循環到 $depth 。在 Sass 中會使迭代器迭代到 through 這個值。
from 0 to 5 返回 0, 1, 2, 3, 4from 0 through 5 返回 0, 1, 2, 3, 4, 5
每一次迭代我都添加一個 text-shadow 到這個列表。所以最后這個列表看起來就是下面這個樣子:
$shadow: (0 1px 0 red, 1px 2px 0 red, 2px 3px 0 red, ...);
使用的時候就像下面這樣:
text-shadow: d3(5, red, [$x], [$y], [$blur], [$mix]);
$x,$y,$blur 和 $mix 都是可選的參數。我已經提到我將會在 keyframes 中調用這個函數,所以我需要可選擇性地改變他們。 $mix 允許添加第二個顏色,實現這個陰影從一種顏色淡出成另外一種顏色。
下面是在 CodePen 實現的小例子:(譯者注:原 demo 是頁面中嵌入的 iframe 實現嵌入 CodePen ,但是 markdown 沒有嵌入 iframe 的方法,所以采用 CodePen 來展示,并且把原 demo 的 jade 和 scss 寫法轉換成 html 和 css 方便沒有使用過兩種技術的讀者閱讀)
實際效果:
組裝在一起
組裝在一起因為我已經創造了許多我需要的部分,現在可以建立動畫了。
1. 組裝在一起
我使用兩個上面已經定義的變量 $offset 和 $trans ,動畫有三個階段,我需要仔細地決定何時到達某幀。
@keyframes pop-out {
0% {
transform:
if($offset == 0, scale(1, 1), scale(95.9 - abs($offset) * 10, 1))
if($offset == 0, translatey(0%), rotatey($trans));
text-shadow:
d3(15, rgba($c_3d, 0), 0, 0),
d3(50, rgba($c_shadow, 0), 0, 0);
}
50% {
transform:
if($offset == 0, scale(1.2, 1.2), scale(126.2 - abs($offset) * 10, 1.2))
if($offset == 0, translatey(-16%), rotatey($trans));
text-shadow:
d3(15, $c_3d, if($offset == 0, 0, -0.25px * $offset), 1px),
d3(50, $c_shadow, 1px, 3px, 3px, $c_shadow-mix);
}
100% {
transform:
if($offset == 0, scale(1.1, 1.1), scale(116.2 - abs($offset) * 10, 1.1))
if($offset == 0, translatey(-12%), rotatey($trans));
text-shadow:
d3(15, $c_3d, if($offset == 0, 0, -0.25px * $offset), 1px),
d3(50, $c_shadow, 1px, 3px, 3px, $c_shadow-mix);
}
}
2. 淡出(動畫結尾)同樣的步驟實現淡出的效果。
@keyframes fade-back {
0% {
transform:
if($offset == 0, scale(1.1, 1.1), scale(116.2 - abs($offset) * 10, 1.1))
if($offset == 0, translatey(-12%), rotatey($trans));
text-shadow:
d3(15, $c_3d, if($offset == 0, 0, -0.25px * $offset), 1px),
d3(50, $c_shadow, 1px, 3px, 3px, $c_shadow-mix);
}
20% {
transform:
if($offset == 0, scale(1.05, 1.05), scale(105.9 - abs($offset) * 10, 1.05))
if($offset == 0, translatey(-7%), rotatey($trans));
text-shadow:
d3(15, rgba($c_3d, 0), 0, 0),
d3(50, rgba($c_shadow, 0), 0, 0);
}
100% {
transform:
if($offset == 0, scale(1, 1), scale(95.9 - abs($offset) * 10, 1))
if($offset == 0, translatey(0%), rotatey($trans));
text-shadow:
d3(15, rgba($c_3d, 0), 0, 0),
d3(50, rgba($c_shadow, 0), 0, 0);
}
}
3. 改變字體顏色
還需要提供一個動畫改變字體顏色。
@keyframes change-color {
0% {
color: $c_bg;
}
100% {
color: $c_fg;
}
}
4. 觸發這個動畫
現在我們可以像下面這樣把動畫連接在一起。
animation-name: pop-out, fade-back, change-color;
animation-duration: 4s, 2s, 0.1s;
animation-delay: 0s, 2s, 3.2s
上面的代碼只是一個近似的實現,每個字母有不同的動畫延遲和間隔,可以點擊 這里 查看最終的實現效果。
最后注意一下,我使用了一些不可思議的技巧來實現在純 CSS 中再次觸發動畫,我將會在接下來的文章中解釋。
寫案例的時候并不是十分滿意,因為寫文章的時候我又想到了其它幾個提高效果的方法。
為了寫這篇文章我重新寫了整個 Sass 代碼,但是我仍然覺得我能提升一些。這就是我不間斷做案例的主要原因。讓我變得更加聰明,和在一些以前沒有涉足過的方向有新的突破。
我幾乎沒有在實際的項目中用到這樣的技術,但是我經常使用函數來提升效果。不論如何希望你喜歡這篇文章。
Gregor Adams 是一位來自 Hamburg 的前端開發者,他對 CSS 和 Sass 有極大的熱情。從他的 CodePen 中可以看出他強大的 CSS 技術。