題目有點繞,源起最近一個項目中所需的一枚loading圖標。SVG+CSS3動畫作了那麼多,真正應用在項目中的機會少之又少,因此,抓住一切機會,即便loading也不能放過,用系統自帶菊花有辱我這一年的修煉。在最後完美作成的過程當中,解決了兩個問題,第一,是非等粗交叉路徑的描邊動畫實現,第二,是多個拼接動畫的無限循環問題,後者困擾了許久,因此,當這個問題解決時,急於分享出來,便於其餘的設計師小夥伴遇坑時一笑而過,也就是這篇文章的原由了。優化
是的,我是說若是隻是若是。先看下要實現的動效。動畫
左邊是真身,很簡單的一個企業的logo,由於是這種連筆式的,因此本能的反應適合描邊動畫,動態展現logo繪製過程。右邊就是初步想法,繪製過程。看上去很簡單,啊哈,來,透過現象看本質。若是這個圖標是下面這種,對,就是我曾經的心頭好,網易雲音樂,由於隨手用鋼筆畫的,沒有用布爾運算,因此略顯粗糙。這種來個描邊動畫,那簡直是分分鐘搞定的事情。 這種描邊動效經過定義stroke-dashoffset
屬性來實現(from 0; to length),很是簡單,之前的文章中也寫過
戳這裏戳這裏,此處略過。好了,爲何說這種好實現呢,由於描邊只要定義三個樣式的屬性值
stroke-linecap
、
stroke-linejoin
和
stroke-width
就好。
好了,不說別人的logo描邊動效多好實現了,來分析一下如今的案例,難度在哪裏?非線性啊親們。若是這個圖標是下面這種的:url
簡單不簡單,你就說簡單不簡單!固然,讓甲方爸爸改圖標是不現實,若是妥協作個神似形不似版的那還不如菊花了(轉行作美工久了,沒點強迫症還真是不行)。如今開始,找解決方法,突破,分析問題的能力仍是有的。我想到的方法是萬能的蒙版。蒙版是個好東西啊,能遮住全部你不想看到的,那直接給路徑描邊動畫加個蒙版就行了唄。spa
白色蒙版是logo部分,其他黑色的部分遮住。之因此描邊給了很粗,就是由於這個logo自己起點處較粗,須要加粗的描邊路徑通過全部的logo部分。那麼,還覺得這樣就完了?是的,我是說若是隻是若是。繼續舉個栗子,若是是下面這種logo,這事就又簡單多了。這是個什麼,鬼知道,我就隨手搞了一個不等粗的描邊而已。設計
看,上面分析的使用蒙版的思路也是對的吧?輕鬆實現了不等粗的logo描邊效果。那來看看真實案例,準備好,打臉( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)。效果是這樣的!!!3d
其實 也蠻好理解的,主要是交叉部分出了問題。當第一遍描邊動畫路過交叉點的時候,已經透過蒙版顯示了與描邊等寬的部分。code
固然,這點區區的小困難是不會讓我放棄的。既然在交叉點那裏糾纏不清,那就快刀斬亂麻,把路徑剁開就好(暴露了暴力的本性)。cdn
每段各司其職,定義好時間延遲,ok了。簡單貼上點代碼湊湊字數,CSS部分blog
/* 定義一個統一的改變stroke-dashoffset值的動畫屬性*/
@keyframes dash{
to {stroke-dashoffset: 0;}
}
@keyframes dash{
#MH_Path1{
stroke-dasharray:705; /* 705爲第一段分段路徑的長度*/
stroke-dashoffset:705;
animation: dash 0.7s linear forwards; /* 0s開始,持續0.7s*/
}
#MH_Path2{
stroke-dasharray:645; /* 645爲第二段分段路徑的長度*/
stroke-dashoffset:645;
animation: dash 0.6s linear 0.7s forwards; /* 延遲0.7s開始,持續0.6s*/
}
#MH_Path3{
stroke-dasharray:108; /* 108爲第三段分段路徑的長度*/
stroke-dashoffset:108;
animation: dash 0.1s linear 1.3s forwards; /* 延遲1.3s開始,持續0.1s*/
}
複製代碼
DOM部分,由於三部分蒙版圖形要被複用一次做爲淺灰色logo底圖(或者也能夠單獨導出路徑,但畢竟不是最優化的方法),因此我用<symbol>
來定義三部分的圖形。get
<symbol id="logo1">
<path d="" /> <!-d值對應第一部分蒙版的路徑-->
</symbol>
<symbol id="logo2">
<path d="" /> <!-d值對應第二部分蒙版的路徑-->
</symbol>
<symbol id="logo3">
<path d="" /> <!-d值對應第三部分蒙版的路徑-->
</symbol>
<!--定義三部分蒙版,用use標籤去引用 -->
<mask id="MH1"><use xlink:href="#logo1" /></mask>
<mask id="MH2"><use xlink:href="#logo2" /></mask>
<mask id="MH3"><use xlink:href="#logo3" /></mask>
<!--底層淺灰色logo-->
<g fill="#ede8e6">
<use xlink:href="#logo1" />
<use xlink:href="#logo2" />
<use xlink:href="#logo3" />
</g>
<path mask="url(#MH1)" id="MH_Path1" d="" /> <!-d值對應第一段分段路徑-->
<path mask="url(#MH2)" id="MH_Path2" d="" /><!-d值對應第二段分段路徑-->
<path mask="url(#MH3)" id="MH_Path3" d="" /><!-d值對應第三段分段路徑-->
複製代碼
通過路徑和蒙版的剪切,獲得了下面這枚半成品的loading logo。
看起來彷佛沒有問題了,蒙版拼接+路徑拼接,交叉點的問題已然解決。但這不過是SVG+CSS3動效的活學活用,這樣的案例隨隨便便拿一個來均可以分析,不足以成文。
loading圖標算是完成了,but just done ,not perfect。咱們都知道,加載的時間是不可控的,那麼,完成一次描邊動畫後,理論上應該開始下一輪,animation有個屬性是animation-iteration-count
,也就是動畫播放次數,像咱們轉圈圈的菊花圖標,通常都會定義值爲infinite,也就是無限循環,那在這個案例中,問題出在什麼地方呢?
再回過頭看咱們的描邊動畫屬性的定義,我以最有表明性的第二段爲例:animation: dash 0.6s linear 0.7s forwards
,後面的0.7s是動畫延遲開始的時間,在進行無縫拼接的時候,第二段描邊動畫開始的延遲時間就是第一段動畫的時間,同理,第三段動畫開始的延遲時間爲動畫一加動畫二,當沒有定義執行次數時,默認執行一次。那麼,當咱們加上這個無限循環的屬性值以後,來看看動畫變成了什麼樣子。
看上去亂七八糟,那是由於被切割的每段都在單獨執行本身的循環,是的,動畫執行的次數能夠無限循環,但延遲只能被執行一次,並無什麼特殊的屬性能夠控制在每一個循環開始以前都執行延遲。至少如今沒有,但CSS3是否是能夠考慮加上新的規範(又在浮想翩翩中,醒醒吧)。經過最經常使用的infinite屬性來控制無限循環的泡沫已然破碎,但這個問題會是無解的麼?(又在廢話,無解的話這篇文章意義何在?)
如今來想一下,控制延遲時間,除了直接在animation屬性中直接寫時間值來定義,還有什麼方法。對,就是關鍵幀@keyframes
(嗯,儼然又開啓了愉快的自問自答模式)。從如今開始,爲了infinite屬性能夠發揮做用,個人三部分動畫再也不設任何延遲,共用相同的全程動畫時間,取而代之經過@keyframes
來控制開始和結束的時間。看一下下面這張圖或許有助於理解:
下面,我要經過控制@keyframes
的時間節點來控制每段動畫的時間區間,至於爲何選擇45%和90%做爲節點,無他,只是估摸了一下每段動畫的時間的比例,爲了好計算而已。我把整個動畫時間週期設計成了2s,DOM部分沒有變化,但CSS部分須要徹底從新定義。首先,通用的改變stroke-dashoffset值的動畫屬性的設置已經無用了,由於不是從0%到100%執行,而是打斷了,具體的打斷方法各個部分又有所不一樣。
@keyframes MH_Path1{
0%{stroke-dashoffset:705;}
45%, 100%{stroke-dashoffset: 0;}
}
/* 0s開始,持續0.9s,1.1s延遲*/
#MH_Path1{
stroke-dasharray:705;
animation: MH_Path1 2s linear both infinite;
}
@keyframes MH_Path2{
0%, 45% {stroke-dashoffset: 645;}
90%, 100%{stroke-dashoffset: 0;}
}
/* 0.9s開始,持續0.9s,0.2s延遲*/
#MH_Path2{
stroke-dasharray:645;
animation: MH_Path2 2s linear both infinite;
}
@keyframes MH_Path3{
0%, 90% {stroke-dashoffset: 108;}
100%{stroke-dashoffset: 0;}
}
/* 1.8s開始,持續0.2s*/
#MH_Path3{
stroke-dasharray:108;
animation: MH_Path3 2s linear both infinite;
}
複製代碼
在第一段路徑中,我在45%處即執行完成了整個描邊動畫過程,而剩下的從45%到100%部分,由於沒有任何變化,因此天然而然的生成了延遲時間,對應定義45%, 100%{stroke-dashoffset:0;}
;第二段則須要同時控制開始和結束,同理,第三段則只須要控制開始時間。
是時候檢驗一下效果了:
若是是整個動畫須要延遲開始,那就簡單的多,只須要在每一個animation屬性中寫入須要延遲的時間就能夠了,三個不分彼此,相同的定義,完美共享。
在作此次案例的過程當中,最大的收穫就是經過定義關鍵幀實現了多個拼接的動畫(或者稱之爲有延遲效果的動畫)的無限循環問題,因此,你壓軸!