CSS3動畫實戰之多關鍵幀實現無限循環動效的時間間隔

題目有點繞,源起最近一個項目中所需的一枚loading圖標。SVG+CSS3動畫作了那麼多,真正應用在項目中的機會少之又少,因此,抓住一切機會,即便loading也不能放過,用系統自帶菊花有辱我這一年的修煉。在最後完美作成的過程當中,解決了兩個問題,第一,是非等粗交叉路徑的描邊動畫實現,第二,是多個拼接動畫的無限循環問題,後者困擾了許久,因此,當這個問題解決時,急於分享出來,便於其餘的設計師小夥伴遇坑時一笑而過,也就是這篇文章的原由了。優化

1.若是隻是簡單的描邊動畫

是的,我是說若是隻是若是。先看下要實現的動效。動畫

左邊是真身,很簡單的一個企業的logo,由於是這種連筆式的,因此本能的反應適合描邊動畫,動態展現logo繪製過程。右邊就是初步想法,繪製過程。看上去很簡單,啊哈,來,透過現象看本質。若是這個圖標是下面這種,對,就是我曾經的心頭好,網易雲音樂,由於隨手用鋼筆畫的,沒有用布爾運算,因此略顯粗糙。這種來個描邊動畫,那簡直是分分鐘搞定的事情。

這種描邊動效經過定義 stroke-dashoffset屬性來實現(from 0; to length),很是簡單,之前的文章中也寫過 戳這裏戳這裏,此處略過。好了,爲何說這種好實現呢,由於描邊只要定義三個樣式的屬性值 stroke-linecapstroke-linejoinstroke-width就好。

好了,不說別人的logo描邊動效多好實現了,來分析一下如今的案例,難度在哪裏?非線性啊親們。若是這個圖標是下面這種的:url

簡單不簡單,你就說簡單不簡單!固然,讓甲方爸爸改圖標是不現實,若是妥協作個神似形不似版的那還不如菊花了(轉行作美工久了,沒點強迫症還真是不行)。如今開始,找解決方法,突破,分析問題的能力仍是有的。我想到的方法是萬能的蒙版。蒙版是個好東西啊,能遮住全部你不想看到的,那直接給路徑描邊動畫加個蒙版就行了唄。spa

白色蒙版是logo部分,其他黑色的部分遮住。之因此描邊給了很粗,就是由於這個logo自己起點處較粗,須要加粗的描邊路徑通過全部的logo部分。那麼,還覺得這樣就完了?

2.若是隻是路徑不重合的描邊動畫

是的,我是說若是隻是若是。繼續舉個栗子,若是是下面這種logo,這事就又簡單多了。這是個什麼,鬼知道,我就隨手搞了一個不等粗的描邊而已。設計

看,上面分析的使用蒙版的思路也是對的吧?輕鬆實現了不等粗的logo描邊效果。

那來看看真實案例,準備好,打臉( ̄ε(# ̄)☆╰╮( ̄▽ ̄///)。效果是這樣的!!!3d

其實 也蠻好理解的,主要是交叉部分出了問題。當第一遍描邊動畫路過交叉點的時候,已經透過蒙版顯示了與描邊等寬的部分。code

3.來吧,解決問題吧

固然,這點區區的小困難是不會讓我放棄的。既然在交叉點那裏糾纏不清,那就快刀斬亂麻,把路徑剁開就好(暴露了暴力的本性)。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。

4.或許,這裏纔是真正的乾貨

看起來彷佛沒有問題了,蒙版拼接+路徑拼接,交叉點的問題已然解決。但這不過是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屬性中寫入須要延遲的時間就能夠了,三個不分彼此,相同的定義,完美共享。

在作此次案例的過程當中,最大的收穫就是經過定義關鍵幀實現了多個拼接的動畫(或者稱之爲有延遲效果的動畫)的無限循環問題,因此,你壓軸!

codepen預覽在這裏,戳戳戳

相關文章
相關標籤/搜索