SVG多色描邊變形動畫實現(xiàn)流光溢彩圖標

今天在codepen看到一個效果如下:


覺得十分有趣,作者是SVG結合canvas完成的,里面所有的路徑部分是SVG完成的,但動畫效果是canvas完成的。canvas能看懂一丟丟,能改一丟丟,但不會寫(硬傷~),那就用最熟悉的SVG+CSS3看看能不能完成咯。

1.路徑變形動畫

先來個拆分,動畫是兩部分的結合,流動的描邊和變形動畫。為了和原作者有點區(qū)別,我準備做四個形狀的動畫,哦吼吼,升級版!在繪制時恍恍惚惚有種兒童簡筆畫的感覺……

基礎圖形變形過程

如果沒有任何的變形動畫基礎,請先移步這三篇文章,了解一下變形動畫的實現(xiàn)原理和實操方法(自己推自己的文章,我是該有多臉皮厚呀):

https://juejin.im/post/591272f6da2f6000536f1aec
https://juejin.im/post/591514b2570c3500692d7235
https://juejin.im/post/59195c22a0bb9f005ff711b2

在AI中如何處理只在這里簡單概括:
圓形:閉合路徑剪開(頂點),轉(zhuǎn)成開放路徑,輕微拖動除起始錨點外的三個錨點,消除導出路徑<path>中小s的存在,使路徑變成標準的小c開頭的路徑。
三角形:閉合路徑剪開(頂點),轉(zhuǎn)成開放路徑,輕微拖動除起始錨點外的兩個錨點,使路徑變成標準的小c開頭的路徑。(沒錯,我就是傳說中的復讀機君,我有什么辦法,處理方法滿滿的都是套路?。?br> 矩形:閉合路徑剪開(左下角),轉(zhuǎn)成開放路徑,輕微拖動除起始錨點外的兩個錨點,使路徑變成標準的小c開頭的路徑。(關于剪開路徑的位置?這個嘛?沒有為什么,我就想看看有什么不同效果啦)
五邊形:閉合路徑剪開(左下角)……(此處省略重復步驟,巴拉巴拉……)

2.多邊形<polygon>轉(zhuǎn)成<path>小c標準路徑

突然插入這么一段小直播,是我發(fā)現(xiàn)在這個動畫效果中,因為除了圓形,剩下的都是多邊形,其實<polygon points="X1,Y1 X2,Y2 …… "/>這個繪制方法是很容易理解的,都是多邊形頂點對應的絕對坐標,但因為有圓形存在,我們不得已才要把很簡單的事情復雜化,然后在AI里手柄拖來拖去的真的好煩的好嘛,而且有可能導出的SVG還有大C開頭的,反反復復,不勝其煩,那么有沒有一種簡單的方法可以把這種多邊形路徑直接轉(zhuǎn)成小c繪制的標準路徑的方法呢?有!
我以五邊形為例,圖示一下:

多邊形與路徑的轉(zhuǎn)換

我的五邊形的五個頂點坐標依次為X1,Y1 X2,Y2 X3,Y3 X4,Y4 X5,Y5,注意,這里說的坐標都是絕對坐標,即在AI中選中錨點之后的X值和Y值。關于具體的轉(zhuǎn)換,我拿其中一段路徑舉例。我們先看三次貝塞爾曲線繪制路徑的指令,也就是右側綠色的曲線,每一段曲線都由起點和終點兩個端點以及對應的兩個控制點(也就是我們AI中手柄的位置)組成的,而當我們的控制點坐標越接近路徑端點,曲線越平,當控制點與端點重合時,就得到了直線。
有了這個概念基礎,理解起來就方便多了,我需要把<polygon>轉(zhuǎn)換成<path>,首先,起點M的坐標(絕對坐標)顯而易見就是多邊形頂點的坐標,當用絕對路徑C表示路徑1時,起點A控制點坐標就是起點A坐標,終點B控制點坐標就是終點B坐標。這樣還不夠,我們需要的是相對坐標表示方法的c指令,也就是我喜歡稱之為“標準曲線”的東西。對于小c繪制方法指令而言,起點和終點控制點的相對坐標最簡單,就是0,0,但最后一組相對坐標則要經(jīng)過計算,B相對于A的移動距離,也就是終點B的絕對坐標與起點A的相對坐標差。

當然了,如果你懶得看原理,覺得很煩的話,就可以直接看解決方法,即c0,0 0,0 X(終點-起點),Y(終點-起點)。坐標點可以在AI里面直接獲得,但計算公式還是少不了的。
所以,最終我的五邊形成功的轉(zhuǎn)換成了<path d="MX1,Y1 c0,0 0,0 X2-X1,Y2-Y1 c0,0 0,0 X3-X2,Y3-Y2 c0,0 0,0 X4-X3,Y4-Y3 c0,0 0,0 X5-X4,Y5-Y4 c0,0 0,0 X5-X1,Y5-Y1">路徑表示方法,這里說明一下,如果剪開路徑時不錯開,最后一段路徑是大C對應的絕對路徑繪制方法,也就是CX5,Y5 X1,Y1 X1,Y1。

3.添加虛擬曲線

做完上面的工作仍然沒有算完,對于變形動畫而言,曲線的數(shù)量要相等才能完成,而我們的這四個圖形,曲線數(shù)量分別是:圓→4,三角形→3,矩形→4,五邊形→5,還好,沒有選擇太復雜的圖形,那就給圓和矩形加1個虛擬曲線,給三角形加兩個虛擬曲線,大家全部補齊成5個咯。(什么?你問我什么是虛擬曲線?打滾……上面的文章鏈接你沒看,沒看)
好啦,加過虛擬曲線,處理過的四個圖形的<path>路徑已經(jīng)統(tǒng)一起來了,這樣就可以套用我們的變形動畫了。
來看一下變形動畫的定義部分:

@keyframes deform{
0% {d:path('');} /*圓形路徑*/
25% {d:path('');}/*三角形路徑*/
50% {d:path('');}/*矩形路徑*/
75% {d:path('');}/*五邊形路徑*/
100%
}
#deform {animation:deform 3s ease infinite};

然后我們的<path>引用這個動畫就好了。就得到了變形動畫:

單純的變形動畫

嗯,只是動了,但起來看上去不是很炫,沒事,go on。

4.流光溢彩動效

關于這種不同顏色沿著描邊路徑流動的效果,我起了個名字叫“流光溢彩”。先拿五邊形為例,看一下單色流動動畫的設置,之所以沒有拿圓形舉例,是怕你想用旋轉(zhuǎn)來實現(xiàn)啊:

<style>
@keyframes animate {
0%{stroke-dashoffset:0}
100%{stroke-dashoffset:1356} /*五邊形的周長*/
}

#animate{
animation:animate 2s linear infinite;
stroke-dasharray:678;  /*五邊形1/2周長*/
}
</style>

得到效果如下:

單色描邊動畫

原理我簡單解釋一下,dashoffset為虛線偏移位置,dasharray定義了虛線的樣式,只有一個值的話,則表示線長和間距等長,如下圖示:

dasharray和dashoffset

當我們把stroke-dasharray定義成1/2周長時,相當于讓圖形實現(xiàn)了一半描邊效果,而CSS中stroke-dashoffset的值的變化,則對應生成了動效,定義差值為周長是為了實現(xiàn)首尾相接連綿不斷的效果。注意一下,這里說差值為周長,也就是說如果初始0%對應的 stroke-dashoffset如果不是0, 那結束時100%對應的也要變化,這是我們下面實現(xiàn)四個顏色流動的基礎。
這里如果把stroke-dashoffset的值改成等值負數(shù),會得到相反方向的動畫效果,感興趣的話可以自己試一下。

好了,逐步推進,實現(xiàn)了單色流動,那雙色怎么辦?要再定義一個動態(tài)單色流動動畫,然后進行疊加么,哎,我們這種懶人總是想方設法偷懶,因為我只要給這個單色流動的動效的底層加一個相同路徑實色描邊,就得到了這種效果:

雙色描邊動畫

嗯,雙色流動已完成(此為懶人法,非正解,無需掌握,看過算完)。
好了正式進階開始了,上面偷懶法只能解決兩個顏色的問題,當我需要多個顏色,腫么辦?
嗯,乖乖的多定義幾個描邊動畫設置,去寫CSS屬性吧。因為每個<path>路徑只識別一個描邊效果,那這種多色的只能用多條相同路徑疊加來實現(xiàn)了。我用圖示來表示一下:

多色拼接原理

當然了,針對我們四個顏色,如果把相同的五邊形路徑重復四遍是慘絕人寰的,這里我們可以用<defs>元素或者<symbol>元素來定義需要重復的路徑,然后用<use>元素來引用,推薦<symbol>,是由于<symbol>支持的屬性更多,雖然在這個案例中無法體現(xiàn)出來,但養(yǎng)成好習慣,需要用<defs>的都可以用<symbol>來代替。這里因為dasharray的定義相同,所以統(tǒng)一到了路徑內(nèi)聯(lián)屬性里。
來看看代碼部分:

<svg>
<style>
@keyframes animate1 {
0%{stroke-dashoffset:0}
100%{stroke-dashoffset:1356}/*1356是路徑的長度*/
}
@keyframes animate2 {
0%{stroke-dashoffset:339}/*定義了四個顏色,所以339是1/4周長*/
100%{stroke-dashoffset:1695}/*需要dashoffset變化值是一個周長來實現(xiàn)首尾相接*/
}
@keyframes animate3 {
0%{stroke-dashoffset:678}
100%{stroke-dashoffset:2034}
}
@keyframes animate4 {
0%{stroke-dashoffset:1017}
100%{stroke-dashoffset:2373}
}
#animate1 {
animation:animate1 2s linear infinite;
stroke:#ffb850;
}
#animate2 {
animation:animate2 2s linear infinite;
stroke:#ff7e5d;
}
#animate3 {
animation:animate3 2s linear infinite;
stroke:#8cd2a4;
}
#animate4 {
animation:animate4 2s linear infinite;
stroke:#62adea;
} 
</style>
<symbol><!--用symbol來定義需要重復引用的相同路徑-->
<path id="pentagon" d="" stroke-width="10" stroke-dasharray="339 1017" fill="none"/></symbol>
<use xlink:href="#pentagon" id="animate1"/>
<use xlink:href="#pentagon" id="animate2"/>  
<use xlink:href="#pentagon" id="animate3"/>
<use xlink:href="#pentagon" id="animate4"/>  
</svg>

還算是很清晰的,而且如果用五個顏色,那就初始的dashoffset遞增1/5周長,然后改一下dasharray為線長1/5 間距4/5 就可以了。

得到的效果如下:

四種顏色流動

5.雙效合一

獨立設計形狀之間的變形動畫和同一形狀的不同顏色描邊的動畫都已經(jīng)實現(xiàn)了,現(xiàn)在要做的就是把這兩個效果合在一起了。在我們上面實現(xiàn)“流光溢彩”動效時把需要重復定義的路徑用<symbol>進行了定義,定義的<path>的id值不是被賦予用了某個屬性,而是作為標簽存在,便于被<use xlink:href="#">反復引用,但當這個效果運用到變形動畫中時,會發(fā)現(xiàn)<path>路徑的id對應的是繪制路徑的變形動畫,那我們來換個思路,把這四個路徑當做獨立的存在,每個路徑在進行變形動畫的同時也在進行描邊動畫,此時我們的SVG定義的變形動畫deform的關鍵幀不變,四個不同顏色的描邊動畫的定義animate1-4的關鍵幀也不變,需要變化的是動畫屬性:

#animate1 {
animation:deform  4s ease  infinite, animate1 2s linear infinite;
stroke:#ffb850;
}
#animate2 {
animation:deform  4s ease  infinite, animate2 2s linear infinite;
stroke:#ff7e5d;
}
#animate3 {
animation:deform  4s ease  infinite, animate3 2s linear infinite;
stroke:#8cd2a4;
}
#animate4 {
animation:deform  4s ease  infinite, animate4 2s linear infinite;
stroke:#62adea;
} 

即每個路徑的動畫屬性同時賦予了兩種動效,一個是變形的deform動畫,一個是對應的描邊動畫。
為了盡可能的優(yōu)化代碼,我把相同定義的<path>屬性統(tǒng)一定義到了CSS里面,如下:

path{stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-width:10;stroke-dasharray:339 1017;fill:none}

這樣,我們的對應的四條路徑的代碼就簡化成了如下:

<path id="animate1" />
<path id="animate2" />
<path id="animate3"/>
<path id="animate4"/>

這里如果有好奇的小伙伴可能會提出疑問,我們的描邊動畫在定義時用的周長是五邊形的周長,但這個動畫里的幾個形狀并不是等長,腫么辦?
其實不要理會,只要選一個最長的路徑進行定義就可以了。因為我們的路徑是一層層疊加的,如果圖形的周長比定義時選擇的短,出現(xiàn)的結果就是最頂層的路徑會略長一些,但對于這類動畫而言,很難看出差別。

雙效合一動畫

另外這里如果對變形的效果不滿意,可以自行調(diào)整路徑的方向和起點位置,以前的文章里都有詳細的方法,不再贅述。
當然了,手癢癢的我還是改了一下各個參數(shù),看了一下效果,比如我定義了stroke-dasharry:100 300 (線長100 間距300的虛線),同時改了其他的stroke-dashoffset的值,依次差階100,然后得到了一個效果:

重新定義虛線樣式后的效果

即使得到了相要的動畫效果,但積極努力追求上進的我卻依然不滿意啊,因為我想讓變形動畫在完成一個變形之后略作停留之后再進行下一個變換。而不是像現(xiàn)在這種唰唰唰一氣呵成,于是乎,我改進了一下,得到了下面這種效果:

修改關鍵幀之后動畫效果

我是用了偷懶的效果,把變形動畫的關鍵幀改成了下面這種:

@keyframes deform{
0% {d:path('')} /*圓形路徑*/
15% {d:path('')} /*三角形路徑*/
25% {d:path('')} /*三角形路徑*/
40% {d:path('')} /*矩形形路徑*/
50% {d:path('')} /*矩形形路徑*/
65% {d:path('')} /*五邊形路徑*/
75%{d:path('')} /*五邊形路徑*/
90% {d:path('')} /*圓形路徑*/
100% {d:path('')} /*圓形路徑*/
}

嗯,滿意。
直接附上codepen的地址,
https://codepen.io/yangyangbeiqiu/pen/ayNGdW
小伙伴們自行查看咯。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容