UI設計師福利之手把手做一枚與眾不同的loading動圖

泱泱發表於2017-05-24

SVG動畫本質上是動圖,我們並不能把它當做實現真正動畫的工具,它比gif動圖的優勢在於小的體量,可互動,可向量放大,方便修改色值,但它的劣勢也很明顯,渲染需要耗記憶體(所以能用基本的移動旋轉等等實現就不要用路徑變形),而且移動端應用需要第三方框架,即使web應用還要考慮到瀏覽器的支援(千刀萬剮的ie),UI設計師也不是專業的動畫設計師,所以關於SVG動畫的應用,更多的是在一些微互動的細節上,比如現在要說的loading動圖,就是其中一個應用場景。
關於載入時的動圖,最常見的菊花已經被用濫了,在最開始接觸SVG的時候,為了練手,做過一堆,如下:

UI設計師福利之手把手做一枚與眾不同的loading動圖
各種樣式的loading動圖

這些都是最基本的樣式,即使眼花繚亂,但實現起來都簡單無比,無非就是旋轉+寬高變化+位移。當你有充裕的時間,可以考慮多花費一點精力來打造一枚獨一無二的loading圖示。融入一點品牌或產品的調性,在小細節處體現一下設計感。下面由淺入深,步步拆解,看一枚特別的loading動圖的做法。(老規矩,會附上可以複用的程式碼,方便UI設計師拿去用)

基本效果

先看一下下面這個動圖

UI設計師福利之手把手做一枚與眾不同的loading動圖
圓環長度變化

這裡面是兩個動畫屬性疊加,一個是旋轉,一個是圓周的長度由0增加到全部周長。CSS樣式部分程式碼如下:

@keyframes animate {
0%{stroke-dashoffset:943;transform:rotate(0)}
100%{stroke-dashoffset:0;transform:rotate(360deg)}
}
#load{
animation:animate 2s  ease infinite;
transform-origin:center center;/*定義旋轉的原點*/
stroke-dasharray:943;/*計算得到的圓周周長*/
stroke-width:20px;/*圓形的描邊各種屬性值*/
stroke:#8879aa;
stroke-linecap: round;
fill:none
}複製程式碼

描邊長度逐漸增加用虛線偏移位置stroke-dashoffset配合虛線樣式stroke-dasharray來實現是最合適不過,第一次接觸描邊動畫可以參考我的這篇文章 UI福利,svg描邊動畫效果,零程式碼基礎手把手教你完成 www.jianshu.com/p/a3d66a920…
這裡面注意一下圓形周長的計算,其他就是套用。
旋轉動畫就比較乏善可陳了,最基本的用法。
然後用<circle>標籤引用load屬性(id="load")就可以實現這種長短變化+旋轉的動效。
瞭解了這些,就可以看一枚與眾不同的loading圖示了

創意來源

UI設計師福利之手把手做一枚與眾不同的loading動圖
創意來源

這是我在dribbble看到的一枚圖示,當時就震驚了,原來除了那些千篇一律的載入圖示,這貨居然還可以做成這樣!也深深的感慨到自己的差距,空有一些技法,但創意渣到空白。
不過既然看到了,那就想個方法來實現這種效果。先模仿再創新嘛。
先說這個cat的選取,很恰到好處,本來喵星人的身體就有這種拉伸的屬性,你能想象如果換成一頭身子被拉伸的大象嘛?當然了,這也導致我在模仿這枚圖示的時候思索了好久,不過還是選了我喜歡的一頭小獅子。效果差強人意。

UI設計師福利之手把手做一枚與眾不同的loading動圖
底圖

我的底圖就是上面這隻可愛的小獅子,我想象中的效果是獅子被分割成3部分,頭(A)+身體(B)+尾巴(C)。旋轉的過程就是身體拉伸的過程。思路如下圖所示。

UI設計師福利之手把手做一枚與眾不同的loading動圖
動畫思路

改造底圖

為了保證獅子身體拉伸時與頭部和尾巴實現無縫對接,要對小獅子進行一些小改造,把圓弧的一段嵌入到身體中。
首先在AI裡做一個圓形,描邊的粗度就是可伸縮的小獅子的身體,調整小獅子的大小和位置。

UI設計師福利之手把手做一枚與眾不同的loading動圖
確定好各個元素的位置

剩下的工作對於UI來說還是比較簡單的,相同位置複製一個圓形,只取其中一小段弧形,進行擴充套件,由路徑變成形狀,調節獅子原來的身體,使圓弧能夠完全與頭部和尾巴契合。

UI設計師福利之手把手做一枚與眾不同的loading動圖
調整

調整之後,原來小獅子的身體就由一段圓弧代替了,此時,再對小獅子的身體進行切割,前半部分與頭部組合,後半部分與尾巴組合。
至此,對於小獅子的改造全部完成。

不同運動速率的頭部和尾巴

根據前面的分析,我們的頭部要走的快一點,尾巴要走的慢一點,這樣才能實現把身體拉伸的效果。
這裡有不同的方法來實現,我使用的方法是控制關鍵幀。程式碼如下:

@keyframes right{
0%{transform:rotate(0); } /*定義頭部旋轉動畫*/
75%{transform:rotate(360deg);}/*頭部完成動畫時間1.5s(2*0.75s)*/
100%{transform:rotate(360deg); }
}
@keyframes left{
0%{transform:rotate(0); } /*定義尾巴旋轉動畫*/
100%{transform:rotate(360deg);}
}
#moveRight{animation:right 2s ease  infinite;transform-origin:300px 300px}/*旋轉中心點為圓形原點*/
#moveLeft{animation:left 2s ease infinite;transform-origin:300px 300px}複製程式碼

通過增加時間節點75%的關鍵幀,使頭部旋轉的動畫時間縮短為定義時間2s的75%,即1.5s完成旋轉360度,而尾巴則是2s完成旋轉360度,從而實現頭部與尾巴的速度差,也就意味著頭部比尾巴提前0.5s到達終點。

可伸縮的身體部分

關於可伸縮的身體部分是這個動效的重點,因為身體的伸縮要與頭部和尾巴保持一致才能組成一個無懈可擊的整體。
還記得最開始的那個長度從零增加到圓周長的紫色圓環吧,我們利用的是stroke-dashoffset配合stroke-dasharray來完成的。這裡仍然利用這個原理來實現彈性伸縮的身體。思路如下:

UI設計師福利之手把手做一枚與眾不同的loading動圖

首先我把一圈的身體做為底圖,然後利用蒙版(不瞭解SVG蒙版動畫的小夥伴先看一下我的關於SVG蒙版動畫的文章)來實現。蒙版上分別畫兩條路徑,一條是白色,為了露出身體底圖部分,運動速率與頭部保持一致,同時,一條為黑色,運動速率落後於白色,為了消除掉多餘的身體部分,運動速率與尾巴保持一致。形象一點來說,就是白色描邊蒙版在前面跑,後面黑色描邊蒙版在追趕,直到完成一個週期的運動。
對應的CSS部分如下:

/*定義白色描邊動畫,速度與頭部旋轉動畫保持一致*/
@keyframes middle{
0%{stroke-dashoffset:942.48}
75%{stroke-dashoffset:0}
100%{stroke-dashoffset:0}
}
/*定義黑色描邊動畫,速度與尾巴旋轉動畫保持一致*/
@keyframes middle2{
0%{stroke-dashoffset:942.48; stroke:#000000}
100%{stroke-dashoffset:0; stroke:#000000}
}
#moveMiddle{stroke-dasharray:942.48;animation:middle  2s ease infinite;}
#moveMiddle2{stroke-dasharray:942.48;animation:middle2 2s ease infinite;}
#base{fill:#f8b602;mask:url(#shade)}/*定義底圖樣式,並呼叫蒙版*/複製程式碼

程式碼部分如下:

<mask id="shade">
<path id="moveMiddle" fill="none" stroke="#ffffff" stroke-width="40" stroke-miterlimit="10" d=""/> <!--用以逐漸顯示底圖的白色蒙版-->
<path id="moveMiddle2" fill="none" stroke="#ffffff" stroke-width="40" stroke-miterlimit="10" d=""/> <!--用以消除的黑色蒙版-->
</mask><!--以上蒙版的兩個d值相同,均為圓形對應的路徑-->
<path id="base" d=""/> <!--d值為組成身體底圖的路徑-->
<g id="moveLeft" >此處若干組成頭部的程式碼……</g>
<g id="moveRight" >此處若干組成尾巴的程式碼……</g>    複製程式碼

這裡需要注意,我的動態描邊蒙版的路徑(圓形)被我從獅子身體位置斷開,以便控制描邊動畫的起點/終點。加上個好看的底色看一下效果:

UI設計師福利之手把手做一枚與眾不同的loading動圖
loading lion

喲,看這彈性無比的身體。看這無縫拼接,完美啊。
打住,既然做都做了,這裡我們要繼續深入研究一下各個引數的修改都會對動效產生什麼影響。

引數調整對動效的影響

這裡面除了小獅子轉圈的速度,那個我們從程式碼裡能一眼看出來,一圈是2s,我們來看一下我定義的時間軸的75%的關鍵幀,如果修改成其他值,會對動效產生什麼影響。
其他不變,我只把關鍵幀的時間點改成50%看一下(記得白色描邊蒙版動畫與頭部是捆綁在一起的,要改一起改,否則可憐的小獅子就會出現身體斷開或者身體跑到頭部前面等等詭異的現象)效果。

UI設計師福利之手把手做一枚與眾不同的loading動圖
頭部完成一圈旋轉動畫關鍵幀定義在50%

哎呀呀,小獅子你腫麼了,身體被拉長好多……是的,當我們能理解這個關鍵幀的含義時,就會知道,當頭部和白色蒙版的旋轉一週的關鍵幀定義到50%時,意味著頭部在1s即50%就完成了一圈的旋轉動畫,剩下的50%在等尾巴跟上來,也就是說獅子的身體部分最長可被拉長到圓周的1/2。
那同樣當我把時間軸的關鍵幀定義為90%時,效果是下面這樣的:

UI設計師福利之手把手做一枚與眾不同的loading動圖
頭部完成一圈旋轉動畫關鍵幀定義在90%

小獅子的身體被拉長的很輕微。所以這裡程式碼如果要複用的時候,各位UI設計師可以根據實際情況自己去調整關鍵幀的位置。

一個週期旋轉N圈

回頭再看看帶給我們靈感的那隻喵星人,然後對比一下我們的小獅子,最大的區別在哪裡,嗯,小獅子每個動畫週期只旋轉了一週,而喵星人是兩週??
如果不考慮身體部分,甭說兩週,就是三週,四周,三千六百度空翻都沒有問題,因為我們的選擇動畫rotate那裡可以填角度。
比如我把旋轉動畫改成下面這樣

@keyframes right{
0%{transform:rotate(0); }
75%{transform:rotate(720deg);} /*720度為旋轉2圈*/
100%{transform:rotate(720deg); }
}
@keyframes left{
0%{transform:rotate(0); }
100%{transform:rotate(720deg);}
}複製程式碼

為了轉起來不頭暈,完成一個動畫週期的時間我同樣加倍了一下,改成了4s。

#moveRight{animation:right 4s ease  infinite;transform-origin:300px 300px}
#moveLeft{animation:left 4s ease infinite;transform-origin:300px 300px}複製程式碼

看一下頭部和尾巴是不是一個週期旋轉2圈

UI設計師福利之手把手做一枚與眾不同的loading動圖
一個動畫週期旋轉2圈的小獅子

那比較難實現的就是身體部分了,我們一直是讓描邊動態蒙版來完成的,而描邊路徑,除了做了一下斷開自定義起點,並沒有做其他的操作,那怎麼讓描邊的路徑也能任意加倍呢?那個,暫時放棄。下午做了幾種嘗試,均已失敗告終,給圓形路徑加倍不是問題,最主要的問題就是黑色描邊一圈後完全遮蓋住了下面的描邊效果。等有解決方法再更新。有空會嘗試用蒙版套蒙版的方法。

補充,方才靈光一現,問題迎刃而解,這感覺,彷彿被打通任督二脈,廢話不說了,直接說我的解決方法。
在做扇形掃描效果時,我用了白色蒙版圍繞底圖的圓圈中心點旋轉方法,這裡解決方法類似,不過比那個還要簡單。

UI設計師福利之手把手做一枚與眾不同的loading動圖
實現原理

我蒙版的建立是個“奧利奧”,上下黑色夾白色夾心。白色蒙版旋轉時黑色蒙版一直再追,設定幾圈就幾圈後追上。
看一下效果是否達到預期
UI設計師福利之手把手做一枚與眾不同的loading動圖
雙層蒙版實現的彈性身體部分

哇,成功,完全不受描邊動畫的那種限制。而且這個方法最合理的地方還在於可以和頭部及尾巴動畫共用動畫設定。完全實現了各種托馬斯旋轉。
在我的案例中,因為小獅子身體斷開的部分並不是嚴格的圓形頂部,而是逆時針偏差了10度左右。這個AI就幫我們處理了,當調整過角度之後,匯出的矩形<rect>標籤會有一個變形屬性transform="matrix()"。當然了,這裡我用的矩形,但實際使用過程,可以用半圓形等等其他形狀。
好了現在與獅子的頭部和尾巴合體!

UI設計師福利之手把手做一枚與眾不同的loading動圖
旋轉2圈

既然這個方法快又好,那麼就放上基礎程式碼並簡單註釋

<svg>
<style>
/*定義頭部以及白色蒙版的旋轉動畫 旋轉度數可以任意定義*/
@keyframes right{
0%{transform:rotate(0); }
85%{transform:rotate(720deg);}
100%{transform:rotate(720deg); }
}
/*定義尾巴以及黑色蒙版的旋轉動畫*/
@keyframes left{
0%{transform:rotate(0); }
100%{transform:rotate(720deg);}
}
/*由於兩個元素呼叫相同動畫,所以定義成類*/
.moveRight{animation:right 4s ease  infinite;transform-origin:300px 300px}
.moveLeft{animation:left 4s ease infinite;transform-origin:300px 300px}
/*定義底圖*/
#base{fill:#f8b602;mask:url(#shade)}
</style>
<rect x="0" y="0" width="600" height="600" fill="#e5dcc5"/><!--圖形底色-->
<mask id="shade">
<rect x="0" y="0" width="600" height="600" fill="#000000"/><!--“奧利奧”蒙版的最底層黑色部分-->
<rect  fill="#ffffff" width="300" height="600" class="moveRight"/><!--“奧利奧”蒙版的中間層白色部分-->
<rect  fill="#000000" width="300" height="600" class="moveLeft"/><!--“奧利奧”蒙版的最上層黑色部分-->
</mask>
<path id="base" d=""/>
<g class="moveLeft" ><!--以下為組成獅子頭部的程式碼-->

</g>
<g class="moveRight"><!--以下為組成獅子尾巴的程式碼-->

</g>
</svg>複製程式碼

有了基礎模板,修改起來就方便多了,比如我想實現轉3圈的效果,直接把transform:rotate(720deg)改成transform:rotate(1080deg)就可以了。
不過圈數越多,身體被拉伸的越厲害,可以適當調整一下頭部的結束的關鍵幀,使速度變慢,比如我改成92%後,就得到了下面這種效果:

UI設計師福利之手把手做一枚與眾不同的loading動圖
選擇3圈

各種隨意發揮,就醬,完美收工。

相關文章