moztw.org

六個神奇 CSS 特效,搭起七夕鵲橋

Firefox 愛在鵲橋狐你幸福

在八月份的七夕搭鵲橋活動裡,用網頁動畫呈現了牛郎織女團聚慶祝的畫面。不知道有沒有眼尖的網友發現,其實活動網頁裡的動畫效果都是運用 CSS animationCSS transition 所做出的特效喔!揪~竟~要怎麼做出這些特效呢?讓我們繼續看下去。

這邊也順便工商服務一下,之前香蕉人也介紹了一些 CSS 的特異功能

星星堆滿天

See the Pen Pure CSS Starry Night by Eddie Lin (@yshlin) on CodePen.0

你可能沒料到,星空的效果完全不需要用到任何一張圖片,為了減輕設計師的負擔,筆者從網路上 A 來純手工打造了純 CSS 的星空效果,這到底是怎麼辦到的呢?其實說穿了原理並不難,大家可能有印象 CSS 的 
background-image  屬性可以設定
radial-gradient ,也就是圓形漸層,簡單來說一個星點只需要一個從白色漸層到全透明的圓形漸層即可。

background-image: radial-gradient(#FFFFFF, rgba(255,255,255,.2) 2px, transparent 40px);

1

background-image: radial-gradient(#FFFFFF, rgba(255,255,255,.2) 2px, transparent 40px);

如上例 
radial-gradient  的三個參數,分別指定了三階段漸層的顏色以及寬度,
rgba  的第四個參數代表 Alpha 透明度,可設定的數值範圍從 1 ~ 0,數字愈小愈透明。為了做出星星的光暈效果,透明度 0.2 到
transparent (也就是全透明)這段漸層的寬度設定
40px ,讓它看起來像是照亮了一個範圍。

但是問題來了,星空總不能只有一顆星星吧?難道要塞一堆 
div  在網頁裡再一個一個定位嗎?當然不用。首先 
radial-gradient  的背景是可以當作圖片一樣設定 
repeat  的。但是光是 
repeat  看起來太死板不像星空,沒關係,並沒有人規定你背景只能設定單一的
radial-gradient ,只需要多設定幾組不同大小的圓形漸層,再分別設定不同的 
background-size  及
background-position ,即可營造出隨機排列的假象。

body {
background-image:
radial-gradient(#FFFFFF, rgba(255,255,255,.2) 2px, transparent 40px),
radial-gradient(#FFFFFF, rgba(255,255,255,.15) 1px, transparent 30px),
radial-gradient(rgba(255,255,255,.9), rgba(255,255,255,.1) 2px, transparent 40px),
radial-gradient(rgba(255,255,255,.4), rgba(255,255,255,.05) 2px, transparent 30px);

background-size: 350px 310px, 160px 200px, 120px 100px, 80px 90px;
background-position: 20px 0, 0px 30px, 10px 130px, 80px 90px;
}

12345678910

body { background-image: radial-gradient(#FFFFFF, rgba(255,255,255,.2) 2px, transparent 40px), radial-gradient(#FFFFFF, rgba(255,255,255,.15) 1px, transparent 30px), radial-gradient(rgba(255,255,255,.9), rgba(255,255,255,.1) 2px, transparent 40px), radial-gradient(rgba(255,255,255,.4), rgba(255,255,255,.05) 2px, transparent 30px); background-size: 350px 310px, 160px 200px, 120px 100px, 80px 90px; background-position: 20px 0, 0px 30px, 10px 130px, 80px 90px;}

在這個範例中我們設定了四組大小不一的圓形漸層,並分別設定四組不同的寬高及起始位置,
background-size  以及 
background-position  裡以逗號分隔開的兩兩一組 px 值就是了,四組數值以設定順序彼此對應。另外 
background-repeat  因為預設就是 
repeat  所以不需特別設定。這樣一來就等於有四張不同大小的星點圖片重覆排列,排起來就很像是滿天的星斗啦!

另外要注意的是 
radial-gradient  設定目前尚未完全標準化,並非所有瀏覽器都有依照同樣的標準來實作,因此建議加上不同瀏覽器的特定寫法以達到最大相容性,完整的範例請見上面的 codepen

飛翔的喜鵲

See the Pen Pure CSS Magpie Fly by Eddie Lin (@yshlin) on CodePen.0

可能有人已經知道,CSS 可以製作角色動畫,只要將角色動作表依序等距排列在一張圖片裡,就可以透過 CSS 來設定角色動畫。

喜鵲

這張圖片就是喜鵲的飛行動作表,其實我們只弄了一張喜鵲的剪影,設計師花了一眨眼的工夫,分別修出兩張左右翅膀各去掉一邊的圖,就可以拿來用了。

@keyframes magpie-action {
from {
background-position: 0 0;
}
to {
background-position: 98% 0;
}
}
.magpie {
animation: magpie-action .3s steps(3) infinite forwards;
}

1234567891011

@keyframes magpie-action { from { background-position: 0 0; } to { background-position: 98% 0; }}.magpie { animation: magpie-action .3s steps(3) infinite forwards;}

這是標準的 CSS 角色動畫寫法。簡單來說就是透過 
@keyframes  設定角色動作的第一格 (
from ) 以及最後一格 (
to ) 的位置,以這個範例來說就是 
background-position  從 
0 0  到
98% 0。那麼要如何讓動畫照順序一格一格播放呢?這時候就要使用 
steps()  時間函式 (Timing Function)。可以看到在 
.magpie  的 
animation  設定裡指定了
steps(3)  作為 
magpie-action  的時間解析函式,它的參數作用是指定動畫的格數,在這裡指定為 3 表示 0 ~ 98% 這個區間會被分為三等份,以本例的 0.3 秒動畫長度來算,0 ~ 0.1 秒間 
background-position  會停留在 0 0 不動,0.1 ~ 0.2 秒間則是停留在 32.66%,0.2 ~ 0.3 秒間則是在 65.33 % 處。咦?那 98% 的那一格怎麼沒用到呢?因為經過實驗讓喜鵲飛翔其實只要前三格就夠了,再多一格動作看起來反而怪怪的,所以沒用它。但這裡大家要注意的是「最後一格」的位置是不會用到的,所以在設定時要注意把位置設在最後一個動作的「後面一格」,關於 
steps()  函式的詳細介紹和使用方式詳見 MDN 說明文件

為了讓 CSS 動畫可以跨瀏覽器運作,目前需要加上不同瀏覽器的 prefix,因為不想針對每個 prefix 重新定義動畫的 CSS 設定,這個範例使用了 LESS 來簡化設定,我把上面的 
magpie-action  抽出來變成一個 LESS 類別
.magpie-keyframes ,並在不同的 prefixed keyframes 設定裡引用它:

.magpie-keyframes {
from {
background-position: 0 0;
}
to {
background-position: 98% 0;
}
}

@-webkit-keyframes magpie-action {
.magpie-keyframes;
}

@-moz-keyframes magpie-action {
.magpie-keyframes;
}

@-ms-keyframes magpie-action {
.magpie-keyframes;
}

@-o-keyframes magpie-action {
.magpie-keyframes;
}

@keyframes magpie-action {
.magpie-keyframes;
}

.magpie {
.animation(magpie-action .3s steps(3) infinite forwards);
}

1234567891011121314151617181920212223242526272829303132

.magpie-keyframes { from { background-position: 0 0; } to { background-position: 98% 0; }} @-webkit-keyframes magpie-action { .magpie-keyframes;} @-moz-keyframes magpie-action { .magpie-keyframes;} @-ms-keyframes magpie-action { .magpie-keyframes;} @-o-keyframes magpie-action { .magpie-keyframes;} @keyframes magpie-action { .magpie-keyframes;} .magpie { .animation(magpie-action .3s steps(3) infinite forwards);}

這邊用到的 
.animation  是我在裡面定義的 LESS mixin,用以產生跨瀏覽器的 CSS 動畫設定。喜鵲動畫就這樣完成啦!是不是很簡單呢?

搭鵲橋

See the Pen Pure CSS Magpie Bridge by Eddie Lin (@yshlin) on CodePen.0

這個效果是最花時間的,主要的功夫是花在對齊每隻喜鵲的位置和出現的時機。簡單來說就是要事先把所有的喜鵲排列成鵲橋,在這邊為了減少設定所需的行數我定義了一個 LESS 類別
.magpiePos :

.magpiePos(@left, @top, @delay: 0s) {
left: @left;
top: @top;
.transition-vararg-transform(1s ease-in-out, opacity 0.2s linear @delay);
}

12345

.magpiePos(@left, @top, @delay: 0s) { left: @left; top: @top; .transition-vararg-transform(1s ease-in-out, opacity 0.2s linear @delay);}

主要的參數是
@left 、
@top  作為絕對定位的位置,以及 
@delay  將每隻喜鵲依序設定不同的出現時間,這邊引用了另一個自訂的 LESS mixin
.transition-vararg-transform ,用以指定跨瀏覽器的 
transform  漸變效果,以及讓 
opacity  可以有另一組漸變設定,有興趣的可以研究 此 codepen 裡的原始碼,在這裡就不多贅述。接下來就是一隻一隻把位置和出現時機調整好,這部分的 code 為了不嚇到大家就不直接貼出來,請自行參閱上面的 codepen

鵲橋搭好以後接下來就海闊天空啦!在這個活動裡把搭鵲橋的動作切分為三個步驟,所以我把喜鵲分成
stage0 、
stage1 、
stage2 、
stage3  四群,讓它們在完成步驟時依序出現(
stage3  一開始就出現在畫面上)。而因為所有喜鵲都包在一個 
id  為 
magpie-bridge  的元素內,所以要讓鵲橋一步一步往另一邊飛其實不需要每隻喜鵲都各別設定飛行的動畫,只需要設定 
magpie-bridge  元素的旋轉角度,並套用漸變效果即可:

.step0 {
#magpie-bridge {
.transform(rotate(-63deg));
}
.stage3 {
opacity: 1;
}
}
.step1 {
#magpie-bridge {
.transform(rotate(-44deg));
}
.stage3, .stage2 {
opacity: 1;
}
}
.step2 {
#magpie-bridge {
.transform(rotate(-24deg));
}
.stage3, .stage2, .stage1 {
opacity: 1;
}
}
.step3 {
#magpie-bridge {
.transform(rotate(0deg));
}
.stage3, .stage2, .stage1, .stage0 {
opacity: 1;
}
}

1234567891011121314151617181920212223242526272829303132

.step0 { #magpie-bridge { .transform(rotate(-63deg)); } .stage3 { opacity: 1; }}.step1 { #magpie-bridge { .transform(rotate(-44deg)); } .stage3, .stage2 { opacity: 1; }}.step2 { #magpie-bridge { .transform(rotate(-24deg)); } .stage3, .stage2, .stage1 { opacity: 1; }}.step3 { #magpie-bridge { .transform(rotate(0deg)); } .stage3, .stage2, .stage1, .stage0 { opacity: 1; }}

這樣一來只要再透過一點 Javascript 把外層元素的 CSS 類別改成
step1 、
step2 、
step3 ,即可輕鬆啟動不同步驟的動畫囉!

附帶一提這種巢狀式的 CSS 定義也是 LESS 特有的簡化寫法,避免元素重構時要同時修改很多 CSS 選擇器。

雀躍的牛郎織女

See the Pen Pure CSS Jumping Sprites by Eddie Lin (@yshlin) on CodePen.0

要讓牛郎織女跳上鵲橋需要用到另一種 CSS 動畫的寫法,這是牛郎的 LESS 類別定義:

.boyJumpKeyframes {
0% {
left: 20%;
}
10% {
margin-top: -10%;
}
20% {
left: 24%;
margin-top: -7%;
}
30% {
margin-top: -13%;
}
40% {
left: 28%;
margin-top: -10%;
}
50% {
margin-top: -16%;
}
60% {
left: 33%;
margin-top: -13%;
}
70% {
margin-top: -17%;
}
80% {
left: 39%;
margin-top: -14%;
}
90% {
margin-top: -18%;
}
100% {
left: 45.6%;
margin-top: -15%;
}
}

12345678910111213141516171819202122232425262728293031323334353637383940

.boyJumpKeyframes { 0% { left: 20%; } 10% { margin-top: -10%; } 20% { left: 24%; margin-top: -7%; } 30% { margin-top: -13%; } 40% { left: 28%; margin-top: -10%; } 50% { margin-top: -16%; } 60% { left: 33%; margin-top: -13%; } 70% { margin-top: -17%; } 80% { left: 39%; margin-top: -14%; } 90% { margin-top: -18%; } 100% { left: 45.6%; margin-top: -15%; }}

可以看到這邊和前面的喜鵲用的
from /
to  不同,而是用百分比來定義,每個百分比設定裡定義的是動畫時間走到某百分比時元素的 CSS 值,兩個百分比設定中間的 CSS 值會由瀏覽器根據時間函式來計算填補,以達到動畫的效果。
from /
to  其實在意義上和
0% /
100%  相同。在這邊我想讓角色跳五下跳到鵲橋的中心,因此以 10% 為一個單位,10%、30%、50%、70%、90%這五組設定就是五次跳躍的頂點,而 
left  值因為一開始上坡讓它增加少一點,快到中心點時再跳遠一點,慢慢調整讓角色看起來像是真的在鵲橋上跳躍。織女的設定也是一樣,只是位置反過來。最後只要像喜鵲的範例一樣,將這個 LESS 類別引入各個瀏覽器 prefix 的 
@keyframes  設定即可!

給你愛、愛、愛不完

See the Pen Pure CSS Popping Hearts by Eddie Lin (@yshlin) on CodePen.0

這個範例一樣不需要用到圖片,設計師只需給我一個比較不刺眼的紅色色碼,愛心本身其實只要用文字符號
♥ 即可。這邊想做到的效果是用幾個不同大小的愛心,各自左右搖晃往上冒,做出有點像冒泡泡的效果,為了達到這個效果,又不必重覆定義太多同樣的東西,在這裡我寫了一個可接收參數的 LESS 類別:

.loveKeyframes(@multiply: 1, @direction: 1) {
0% {
font-size: 0;
opacity: 0;
padding-left: 10px / @multiply;
}
25% {
font-size: 16px @multiply;
opacity: 1;
padding-left: (10px + 10px
@direction) / @multiply;
}
50% {
padding-left: (10px - 10px @direction) / @multiply;
}
75% {
font-size: 28px
@multiply;
opacity: 1;
padding-left: (10px + 10px @direction) / @multiply;
}
100% {
font-size: 32px
@multiply;
opacity: 0;
line-height: 32px * @multiply;
padding-left: 10px / @multiply;
}
}

1234567891011121314151617181920212223242526

.loveKeyframes(@multiply: 1, @direction: 1) { 0% { font-size: 0; opacity: 0; padding-left: 10px / @multiply; } 25% { font-size: 16px @multiply; opacity: 1; padding-left: (10px + 10px @direction) / @multiply; } 50% { padding-left: (10px - 10px @direction) / @multiply; } 75% { font-size: 28px @multiply; opacity: 1; padding-left: (10px + 10px @direction) / @multiply; } 100% { font-size: 32px @multiply; opacity: 0; line-height: 32px * @multiply; padding-left: 10px / @multiply; }}

簡單來說 
font-size  是用來控制愛心的大小,
padding-left  控制左右搖晃的位置,
line-height  控制愛心的高度,
opacity  則是讓愛心淡入淡出。而傳入的參數 
@multiply  愈大,愛心就愈大,但因為較笨重,左右搖晃的幅度就讓它相對變小。
@direction  則是控制它從左邊或右邊開始搖晃。

@keyframes love1 {
.loveKeyframes;
}

@keyframes love2 {
.loveKeyframes(1.2, -1);
}

@keyframes love3 {
.loveKeyframes(2);
}

1234567891011

@keyframes love1 { .loveKeyframes;} @keyframes love2 { .loveKeyframes(1.2, -1);} @keyframes love3 { .loveKeyframes(2);}

接著利用這個 LESS 類別來產生三個不同大小的「愛的冒泡」動畫定義,就可以拿來用啦!這邊為了展示重點省略了不同瀏覽器的 prefix,完整的 LESS 定義請參閱上面的 codepen

放煙火

See the Pen Pure CSS Fireworks by Eddie Lin (@yshlin) on CodePen.0

重頭戲來了,為了讓參與者有一種普天同慶的感覺,當然少不了煙火啦!但是如果要讓設計師手工畫出煙火可能會畫到手斷掉。這時就想有沒有辦法用 CSS 動畫來做出煙火呢?沒想到網路上找了一下還真的有!只要拿來改一下就可以使用。不過這個範例稍微進階一點,用了 SASS 的一些特殊功能例如 @for 以及 random 等等。別害怕, SASS 在這裡的功用和 LESS 差不多,只是方便產生較繁複的 CSS,如果有耐心的話其實純手工刻 CSS 也是可以做到同樣效果的。

首先來說明一下原理,煙火其實和星空的效果作法有點類似,只是運用的屬性不同,且加上了爆發的動畫。這個範例同樣不需要使用任何一張圖片,那麼煙火的小圓亮點是怎麼做的呢?其實是透過元素的 
border-radius  屬性,設定一個 
border-radius  為 
50%  的圓角矩形,其實就等同於圓形。但是就算可以用 CSS 做出圓形,如果我們要純手工一顆一顆去定位煙火的位置也太累人了吧?多想一分鐘,其實可以不用這麼累。前面的星空範例在背景設定使用多組
radial-gradient 。同樣的道理,其實 
box-shadow  也可以設定多組哦!這樣一來我們只需要一個 HTML 元素,就能產生好幾個小圓亮點。在這個範例我們使用兩組煙火交錯發放,所以僅需要這樣的 HTML 定義:

<div class="pyro">
<div class="before"></div>
<div class="after"></div>
</div>

1234

<div class="pyro"> <div class="before"></div> <div class="after"></div></div>

其實原本在網路上找到的範例使用的是
:before /
:after  虛擬元素來定義煙火,但為了最大化瀏覽器的相容性,我把它改成兩個實體的 
div  元素。

接著用這段 SASS 來定義煙火的起始位置和爆發後的位置:

$particles: 50;
$width: 500;
$height: 500;

// Create the explosion...
$box-shadow: ();
$box-shadow2: ();
@for $i from 0 through $particles {
$box-shadow: $box-shadow,
random($width)-$width / 2 + px
random($height)-$height / 1.2 + px
hsl(random(360), 100, 50);
$box-shadow2: $box-shadow2, 0 0 #fff
}

1234567891011121314

$particles: 50;$width: 500;$height: 500; // Create the explosion...$box-shadow: ();$box-shadow2: ();@for $i from 0 through $particles { $box-shadow: $box-shadow, random($width)-$width / 2 + px random($height)-$height / 1.2 + px hsl(random(360), 100, 50); $box-shadow2: $box-shadow2, 0 0 #fff}

這裡的 
$box-shadow  變數是爆發後的設定,
$box-shadow2  是啟始設定,可以看到兩個變數一開始都是空序列
() ,隨著迴圈每跑一次,就在
$box-shadow 裡多加了一組隨機位置和顏色的陰影設定,而 
$box-showdow2  裡則是重覆加上同樣的一組陰影設定
0 0 #FFF 。有了這兩組設定後,就可以運用 CSS 動畫設定,把 
$box-shadow2  設為
from ,
$box-shadow  設為 
to  即可產生煙火的爆發效果。

.pyro > .before, .pyro > .after {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
box-shadow: $box-shadow2;
@include animation(1s bang ease-out infinite backwards);
}

@include keyframes(bang) {
to {
box-shadow:$box-shadow;
}
}

1234567891011121314

.pyro > .before, .pyro > .after { position: absolute; width: 5px; height: 5px; border-radius: 50%; box-shadow: $box-shadow2; @include animation(1s bang ease-out infinite backwards);} @include keyframes(bang) { to { box-shadow:$box-shadow; }}

backwards  設定是讓動畫結束後回到最初的起始位置,以確保下次發放不會看到奇怪的殘影。這邊用到的 
@include animation()  以及 
@include keyframes()  是我在 SASS 檔案裡定義的兩個 mixin ,作用和之前用到的 LESS 類似,用以產生跨瀏覽器的動畫設定。

但煙火光是爆發還是看起來不太逼真,這邊再加上一組重力的動畫設定,做出煙火下降的效果。

@include keyframes(gravity) {
to {
@include transform(translateY(200px));
opacity: 0;
}
}

123456

@include keyframes(gravity) { to { @include transform(translateY(200px)); opacity: 0; }}

若煙火總在同一個定點爆發,看起來還是會很假,所以再加上另一組定位用的動畫設定:

@include keyframes(position) {
0%, 19.9% {
margin-top: 10%;
margin-left: 40%;
}
20%, 39.9% {
margin-top: 40%;
margin-left: 30%;
}
40%, 59.9% {
margin-top: 20%;
margin-left: 70%
}
60%, 79.9% {
margin-top: 30%;
margin-left: 20%;
}
80%, 99.9% {
margin-top: 30%;
margin-left: 80%;
}
}

12345678910111213141516171819202122

@include keyframes(position) { 0%, 19.9% { margin-top: 10%; margin-left: 40%; } 20%, 39.9% { margin-top: 40%; margin-left: 30%; } 40%, 59.9% { margin-top: 20%; margin-left: 70% } 60%, 79.9% { margin-top: 30%; margin-left: 20%; } 80%, 99.9% { margin-top: 30%; margin-left: 80%; }}

這組設定讓煙火隨時間推移出現在五個不同位置,兩組設定的間隔設定必須很小 (19.9% ~ 20%) 才不會出現煙火到處亂漂移的奇怪效果,讓煙火時間到了就直接跳到下一個定點發放。

.pyro > .before, .pyro > .after {
position: absolute;
width: 5px;
height: 5px;
border-radius: 50%;
box-shadow: $box-shadow;
@include animation((1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards));
}

.pyro > .after {
@include animation-delay((1.25s, 1.25s, 1.25s));
@include animation-duration((1.25s, 1.25s, 6.25s));
}

12345678910111213

.pyro > .before, .pyro > .after { position: absolute; width: 5px; height: 5px; border-radius: 50%; box-shadow: $box-shadow; @include animation((1s bang ease-out infinite backwards, 1s gravity ease-in infinite backwards, 5s position linear infinite backwards));} .pyro > .after { @include animation-delay((1.25s, 1.25s, 1.25s)); @include animation-duration((1.25s, 1.25s, 6.25s));}

這就是最後的動畫設定,注意 
bang  以及 
gravity  兩個動畫的持續時間必須一致,而 
position  動畫的時間必須為五倍,這樣煙火的爆發時間才能搭配上前面設定的五個定位時間點。為了讓煙火的發放看起來像是隨機交錯,我們再另外為 
.pyro > .after  元素指定另一組爆發的頻率以及起始時間。喔耶!大功告成啦!

總結

看完這篇文章你可能會有點驚訝,原來光是用 CSS 就能做出這麼多酷炫的效果,習慣用 Javascript 來做網頁動畫的人,可以試試 CSS 動畫的威力和效能。不懂 Javascript 的人也可以慢慢為你的網頁增添一些有趣的效果。覺得自己的 CSS 寫得太亂難以維護的話,也不妨開始用 LESS/SASS 來整理看看喔。

另外提醒一下,參加這次的七夕搭鵲橋活動有機會可以抽到精美贈品哦!活動截止日期為 2014/08/13,還沒玩過的快去玩玩看吧!