片頭警告:涉及動畫,流量巨大(看,我還壓了個韻)

前兩章我可勁的折騰這片雲,但都是靜靜呆在那(靜靜是誰?),我抬頭看天,天上的雲可不是,它雖然有時候很緩慢,很緩慢,但好歹也是會動的。作為一個我頁面HTML、SVG和CSS的國王、H5統治者、文件的保護者、二進位的MAKER、IE破除者、HEADER領主、DIV主人、帥哥、宅男、造雲師,BUG之王怎麼能夠容忍它一動不動,我跨上我的龍,飛到天上,對著雲說:「起來走兩步」。

1、會呼吸的雲(SVG ANIMATE)

首先,我想到的動畫手段是javascript,但我覺得吧,內部矛盾內部解決,既然是svg的事,那麼先看看svg自身能不能搞定動畫。真別說,還真有,那就是animate。官方的解釋是這樣的:

animate,動畫元素放在形狀元素的內部,用來定義一個元素的某個屬性如何踩著時點改變。在指定持續時間裡,屬性從開始值變成結束值。它的屬性有這些:

  • attributeName要對哪個屬性值做變化
  • attributeType 這個屬性值是屬於哪個類型的組件(比如CSS,HTML)
  • from 這個屬性值要從多少開始變化
  • to 這個屬性值變化到哪個值結束
  • dur這個變化過程要持續多久
  • repeatCount 動畫過程要持續幾次(如果一直反覆,用indefinite)

只看這個估計大家印象不深,不如我們開始擼代碼吧,先拿一層雲來試試手。我通過控制feDisplacementMap中決定雲化程度的SourceGraphic值的變化,讓雲有一種在慢慢飄散的效果。

<div class="cloud" id="cloud-back"></div>
<svg width_="0" height="0">
<filter id="filter-back">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" seed="0" />
<feDisplacementMap in="SourceGraphic">
<animate attributeName="scale" from="100" to="170" dur="2s" repeatCount="indefinite"></animate>
</feDisplacementMap>
</filter>
</svg>

animate作用於feDisplacementMap,所以就放在它的標籤裡面,雲化程度從100到170,變化的過程設置在2秒內完成。最終看到的結果是這樣的

雲倒是真的動起來了,而且這瀰漫的過程,也還真像那麼回事,可惜每次循環的時候,都簡單粗暴的從頭開始,看起來這雲跟在跳幀似的。作為造雲師,這種雲太容易被看出破綻了。萬一頁面世界的人看到這朵雲,發現不是真的,開始覺醒,從 the matrix中吞下紅色藥丸逃出來,成為the one,和我搗亂怎麼辦(最近沃卓斯基姐妹(xiongdi)要重啟黑客帝國,講墨菲斯年輕時候的事,這段和本文章無關,也不是廣告,就是我的個人愛好)。

循環實在是動畫裡面的一件麻煩事,通常的思路是把動畫的過程分為兩段,比如我們這片雲,一段是瀰漫開,瀰漫開後再接一段動畫,是雲從瀰漫到收縮,收縮完了再接上之前瀰漫的動畫,這樣一直循環下去,代碼如下:

<feDisplacementMap in="SourceGraphic">
<animate attributeName="scale" id="first" begin="0s;second.end" from="130" to="170" dur="3s" ></animate>
<animate attributeName="scale" id="second" begin="first.end" from="170" to="130" dur="3s" ></animate>
</feDisplacementMap>

這裡面有幾點要注意:

  • 加了id,方便標記兩段動畫的設置
  • 加了begin屬性,用來銜接第一段動畫的開始和第二段動畫的開始,第二段結束後回到第一段的開始
  • 去掉了repeatCount屬性,如果這個屬性還在,只會循環第一段動畫配置,不會跳到第二段,哪怕你配置了begin也不行

加後,看到的效果是這樣的

變化不是很明顯,請看官盯仔細了

這樣,雲的簡單動畫就完成了,然後為了效果,我們把三層雲疊加上,體現出明暗的立體效果。

#cloud-back {
filter: url(#filter-back);
box-shadow: 300px 300px 30px -20px #fff;
}
#cloud-mid {
filter: url(#filter-mid);
box-shadow: 300px 340px 70px -60px rgba(158, 168, 179, 0.5);
left: -25vw;
}
#cloud-front {
filter: url(#filter-front);
box-shadow: 300px 370px 60px -100px rgba(0, 0, 0, 0.3);
left: -25vw;
}

<div class="cloud" id="cloud-back"></div>
<div class="cloud" id="cloud-mid"></div>
<div class="cloud" id="cloud-front"></div>
<svg width_="0" height="0">
<filter id="filter-back">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" seed="0" />
<feDisplacementMap in="SourceGraphic">
<animate attributeName="scale" id="first" begin="0s;second.end" from="130" to="170" dur="2s" ></animate>
<animate attributeName="scale" id="second" begin="first.end" from="170" to="130" dur="2s" ></animate>
</feDisplacementMap>
</filter>
<filter id="filter-mid">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" seed="0"/>
<feDisplacementMap in="SourceGraphic">
<animate attributeName="scale" id="first1" begin="0s;second1.end" from="100" to="140" dur="2s" ></animate>
<animate attributeName="scale" id="second1" begin="first1.end" from="140" to="100" dur="2s" ></animate>
</feDisplacementMap>
</filter>
<filter id="filter-front">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" seed="0"/>
<feDisplacementMap in="SourceGraphic">
<animate attributeName="scale" id="first2" begin="0s;second2.end" from="80" to="110" dur="2s" ></animate>
<animate attributeName="scale" id="second2" begin="first2.end" from="110" to="80" dur="2s" ></animate>
</feDisplacementMap>
</filter>
</svg>

最後呈現出的效果是這樣的

同樣需要仔細看才能看出細節的動畫

2、看雲捲雲舒(JQUERY ANIMATE)

我是個不容易滿足的人,上面的雲雖然動起來了,但太小家子氣,不仔細看,真看不出來是動的,為了讓它能以肉眼可見的變化動起來,我決定在上面動畫的基礎上再加戲,動的猛一點,這次,用上大家熟悉的老朋友,jquery.animate(),讓雲舒展開來。

var cssback = {width:700px};
$("#cloud-back").animate(cssback,12000,goBack);
function goBack(){
if(cssback.width_===700px)
cssback.width_=300px;
else if(cssback.width_===300px)
cssback.width_=700px;
$("#cloud-back").animate(cssback,12000,goBack);
}

cssback是我們需要變化的屬性值,12000是動畫的時間長度,goBack是動畫執行完後執行的函數。goBack其實和上面svg的animate一樣,是解決循環問題的,通過這個遞歸函數,可以讓用戶在300-700px的長度中循環往複,單一層的動畫效果是這樣的。

這個效果疊加了svg animate和jquery animate兩種動畫效果

最後,我把疊加三層效果的完整動畫雲代碼貼出來,然後有個問題希望有前端大拿能幫忙解決下:

.cloud {
width: 300px;
height: 275px;
border-radius: 50%;
position: absolute;
top: -35vh;
left: -25vw;
}

#cloud-back {
filter: url(#filter-back);
box-shadow: 300px 300px 30px -20px #fff;
}

#cloud-mid {
filter: url(#filter-mid);
box-shadow: 300px 340px 70px -60px rgba(158, 168, 179, 0.5);
left: -25vw;
}

#cloud-front {
filter: url(#filter-front);
box-shadow: 300px 370px 60px -100px rgba(0, 0, 0, 0.3);
left: -25vw;
}

<div class="cloud" id="cloud-back"></div>
<div class="cloud" id="cloud-mid"></div>
<div class="cloud" id="cloud-front"></div>

<svg width_="0" height="0">
<filter id="filter-back">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="4" seed="0" />
<feDisplacementMap in="SourceGraphic">
<animate attributeName="scale" id="first" begin="0s;second.end" from="130" to="170" dur="3s" ></animate>
<animate attributeName="scale" id="second" begin="first.end" from="170" to="130" dur="3s" ></animate>
</feDisplacementMap>
</filter>
<filter id="filter-mid">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" seed="0"/>
<feDisplacementMap in="SourceGraphic">
<animate attributeName="scale" id="first1" begin="0s;second1.end" from="100" to="140" dur="3s" ></animate>
<animate attributeName="scale" id="second1" begin="first1.end" from="140" to="100" dur="3s" ></animate>
</feDisplacementMap>
</filter>
<filter id="filter-front">
<feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="2" seed="0"/>
<feDisplacementMap in="SourceGraphic">
<animate attributeName="scale" id="first2" begin="0s;second2.end" from="80" to="110" dur="3s" ></animate>
<animate attributeName="scale" id="second2" begin="first2.end" from="110" to="80" dur="3s" ></animate>
</feDisplacementMap>
</filter>
</svg>

var cssback = {width:700px};
$("#cloud-back").animate(cssback,12000,goback);
function goback(){
if(cssback.width===700px)
cssback.width=300px;
else if(cssback.width===300px)
cssback.width=700px;
$("#cloud-back").animate(cssback,12000,goback);
}
var cssmid = {width:700px};
$("#cloud-mid").animate(cssmid,12000,goBackmid);
function goBackmid(){
if(cssmid.width===700px)
cssmid.width=300px;
else if(cssmid.width===300px)
cssmid.width=700px;
$("#cloud-mid").animate(cssmid,12000,goBackmid);
}
var cssfront = {width:700px};
$("#cloud-front").animate(cssfront,12000,goBackfront);
function goBackfront(){
if(cssfront.width===700px)
cssfront.width=300px;
else if(cssfront.width===300px)
cssfront.width=700px;
$("#cloud-front").animate(cssfront,12000,goBackfront);
}

最後這段js的代碼,goBack,goBackmid和goBackfront我一直想合成一個函數,減少代碼量,但卻一直做不到(合起來動畫效果會中斷,部分動畫效果不執行),希望有大拿能夠幫忙實現,多謝了。讓我們看下最終完全體的動畫效果,這次用視頻來看。

視頻封面

00:22完全體雲捲雲舒

最後的警告:這個動畫效果極其耗電腦CPU和內存,我在寫這篇文章的時候,我都能感受到我的筆記本在咆哮(燙手啊),所以實用性要打折扣,大家使用時請自行斟酌。

源代碼地址


推薦閱讀:
相關文章