今天在codepen看到一個(gè)效果如下:
覺(jué)得十分有趣生真,作者是SVG結(jié)合canvas完成的铣除,里面所有的路徑部分是SVG完成的闹丐,但動(dòng)畫(huà)效果是canvas完成的阱高。canvas能看懂一丟丟赚导,能改一丟丟,但不會(huì)寫(xiě)(硬傷~)赤惊,那就用最熟悉的SVG+CSS3看看能不能完成咯吼旧。
1.路徑變形動(dòng)畫(huà)
先來(lái)個(gè)拆分耸三,動(dòng)畫(huà)是兩部分的結(jié)合吏口,流動(dòng)的描邊和變形動(dòng)畫(huà)。為了和原作者有點(diǎn)區(qū)別航徙,我準(zhǔn)備做四個(gè)形狀的動(dòng)畫(huà)处面,哦吼吼厂置,升級(jí)版菩掏!在繪制時(shí)恍恍惚惚有種兒童簡(jiǎn)筆畫(huà)的感覺(jué)……
如果沒(méi)有任何的變形動(dòng)畫(huà)基礎(chǔ)魂角,請(qǐng)先移步這三篇文章,了解一下變形動(dòng)畫(huà)的實(shí)現(xiàn)原理和實(shí)操方法(自己推自己的文章智绸,我是該有多臉皮厚呀):
https://juejin.im/post/591272f6da2f6000536f1aec
https://juejin.im/post/591514b2570c3500692d7235
https://juejin.im/post/59195c22a0bb9f005ff711b2
在AI中如何處理只在這里簡(jiǎn)單概括:
圓形:閉合路徑剪開(kāi)(頂點(diǎn))野揪,轉(zhuǎn)成開(kāi)放路徑,輕微拖動(dòng)除起始錨點(diǎn)外的三個(gè)錨點(diǎn)瞧栗,消除導(dǎo)出路徑<path>
中小s的存在斯稳,使路徑變成標(biāo)準(zhǔn)的小c開(kāi)頭的路徑。
三角形:閉合路徑剪開(kāi)(頂點(diǎn))迹恐,轉(zhuǎn)成開(kāi)放路徑挣惰,輕微拖動(dòng)除起始錨點(diǎn)外的兩個(gè)錨點(diǎn),使路徑變成標(biāo)準(zhǔn)的小c開(kāi)頭的路徑。(沒(méi)錯(cuò)憎茂,我就是傳說(shuō)中的復(fù)讀機(jī)君珍语,我有什么辦法,處理方法滿滿的都是套路笆!)
矩形:閉合路徑剪開(kāi)(左下角)板乙,轉(zhuǎn)成開(kāi)放路徑,輕微拖動(dòng)除起始錨點(diǎn)外的兩個(gè)錨點(diǎn)拳氢,使路徑變成標(biāo)準(zhǔn)的小c開(kāi)頭的路徑募逞。(關(guān)于剪開(kāi)路徑的位置?這個(gè)嘛馋评?沒(méi)有為什么放接,我就想看看有什么不同效果啦)
五邊形:閉合路徑剪開(kāi)(左下角)……(此處省略重復(fù)步驟,巴拉巴拉……)
2.多邊形<polygon>
轉(zhuǎn)成<path>
小c標(biāo)準(zhǔn)路徑
突然插入這么一段小直播栗恩,是我發(fā)現(xiàn)在這個(gè)動(dòng)畫(huà)效果中透乾,因?yàn)槌藞A形,剩下的都是多邊形磕秤,其實(shí)<polygon points="X1,Y1 X2,Y2 …… "/>
這個(gè)繪制方法是很容易理解的乳乌,都是多邊形頂點(diǎn)對(duì)應(yīng)的絕對(duì)坐標(biāo),但因?yàn)橛袌A形存在市咆,我們不得已才要把很簡(jiǎn)單的事情復(fù)雜化汉操,然后在AI里手柄拖來(lái)拖去的真的好煩的好嘛,而且有可能導(dǎo)出的SVG還有大C開(kāi)頭的蒙兰,反反復(fù)復(fù)磷瘤,不勝其煩,那么有沒(méi)有一種簡(jiǎn)單的方法可以把這種多邊形路徑直接轉(zhuǎn)成小c繪制的標(biāo)準(zhǔn)路徑的方法呢搜变?有采缚!
我以五邊形為例,圖示一下:
我的五邊形的五個(gè)頂點(diǎn)坐標(biāo)依次為X1,Y1 X2,Y2 X3,Y3 X4,Y4 X5,Y5挠他,注意扳抽,這里說(shuō)的坐標(biāo)都是絕對(duì)坐標(biāo),即在AI中選中錨點(diǎn)之后的X值和Y值殖侵。關(guān)于具體的轉(zhuǎn)換贸呢,我拿其中一段路徑舉例。我們先看三次貝塞爾曲線繪制路徑的指令拢军,也就是右側(cè)綠色的曲線楞陷,每一段曲線都由起點(diǎn)和終點(diǎn)兩個(gè)端點(diǎn)以及對(duì)應(yīng)的兩個(gè)控制點(diǎn)(也就是我們AI中手柄的位置)組成的,而當(dāng)我們的控制點(diǎn)坐標(biāo)越接近路徑端點(diǎn)茉唉,曲線越平固蛾,當(dāng)控制點(diǎn)與端點(diǎn)重合時(shí)结执,就得到了直線。
有了這個(gè)概念基礎(chǔ)艾凯,理解起來(lái)就方便多了昌犹,我需要把
<polygon>
轉(zhuǎn)換成<path>
,首先览芳,起點(diǎn)M的坐標(biāo)(絕對(duì)坐標(biāo))顯而易見(jiàn)就是多邊形頂點(diǎn)的坐標(biāo)斜姥,當(dāng)用絕對(duì)路徑C表示路徑1時(shí),起點(diǎn)A控制點(diǎn)坐標(biāo)就是起點(diǎn)A坐標(biāo)沧竟,終點(diǎn)B控制點(diǎn)坐標(biāo)就是終點(diǎn)B坐標(biāo)铸敏。這樣還不夠,我們需要的是相對(duì)坐標(biāo)表示方法的c指令悟泵,也就是我喜歡稱之為“標(biāo)準(zhǔn)曲線”的東西杈笔。對(duì)于小c繪制方法指令而言,起點(diǎn)和終點(diǎn)控制點(diǎn)的相對(duì)坐標(biāo)最簡(jiǎn)單糕非,就是0,0蒙具,但最后一組相對(duì)坐標(biāo)則要經(jīng)過(guò)計(jì)算,B相對(duì)于A的移動(dòng)距離朽肥,也就是終點(diǎn)B的絕對(duì)坐標(biāo)與起點(diǎn)A的相對(duì)坐標(biāo)差禁筏。
當(dāng)然了,如果你懶得看原理衡招,覺(jué)得很煩的話篱昔,就可以直接看解決方法,即c0,0 0,0 X(終點(diǎn)-起點(diǎn)),Y(終點(diǎn)-起點(diǎn))州刽。坐標(biāo)點(diǎn)可以在AI里面直接獲得,但計(jì)算公式還是少不了的浪箭。
所以穗椅,最終我的五邊形成功的轉(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">
路徑表示方法,這里說(shuō)明一下奶栖,如果剪開(kāi)路徑時(shí)不錯(cuò)開(kāi)匹表,最后一段路徑是大C對(duì)應(yīng)的絕對(duì)路徑繪制方法,也就是CX5,Y5 X1,Y1 X1,Y1驼抹。
3.添加虛擬曲線
做完上面的工作仍然沒(méi)有算完桑孩,對(duì)于變形動(dòng)畫(huà)而言拜鹤,曲線的數(shù)量要相等才能完成框冀,而我們的這四個(gè)圖形,曲線數(shù)量分別是:圓→4敏簿,三角形→3明也,矩形→4宣虾,五邊形→5,還好温数,沒(méi)有選擇太復(fù)雜的圖形绣硝,那就給圓和矩形加1個(gè)虛擬曲線,給三角形加兩個(gè)虛擬曲線撑刺,大家全部補(bǔ)齊成5個(gè)咯鹉胖。(什么?你問(wèn)我什么是虛擬曲線够傍?打滾……上面的文章鏈接你沒(méi)看甫菠,沒(méi)看)
好啦,加過(guò)虛擬曲線冕屯,處理過(guò)的四個(gè)圖形的<path>
路徑已經(jīng)統(tǒng)一起來(lái)了寂诱,這樣就可以套用我們的變形動(dòng)畫(huà)了。
來(lái)看一下變形動(dòng)畫(huà)的定義部分:
@keyframes deform{
0% {d:path('');} /*圓形路徑*/
25% {d:path('');}/*三角形路徑*/
50% {d:path('');}/*矩形路徑*/
75% {d:path('');}/*五邊形路徑*/
100%
}
#deform {animation:deform 3s ease infinite};
然后我們的<path>
引用這個(gè)動(dòng)畫(huà)就好了安聘。就得到了變形動(dòng)畫(huà):
嗯痰洒,只是動(dòng)了,但起來(lái)看上去不是很炫浴韭,沒(méi)事丘喻,go on。
4.流光溢彩動(dòng)效
關(guān)于這種不同顏色沿著描邊路徑流動(dòng)的效果念颈,我起了個(gè)名字叫“流光溢彩”仓犬。先拿五邊形為例,看一下單色流動(dòng)動(dòng)畫(huà)的設(shè)置舍肠,之所以沒(méi)有拿圓形舉例搀继,是怕你想用旋轉(zhuǎn)來(lái)實(shí)現(xiàn)啊:
<style>
@keyframes animate {
0%{stroke-dashoffset:0}
100%{stroke-dashoffset:1356} /*五邊形的周長(zhǎng)*/
}
#animate{
animation:animate 2s linear infinite;
stroke-dasharray:678; /*五邊形1/2周長(zhǎng)*/
}
</style>
得到效果如下:
原理我簡(jiǎn)單解釋一下翠语,dashoffset為虛線偏移位置叽躯,dasharray定義了虛線的樣式,只有一個(gè)值的話肌括,則表示線長(zhǎng)和間距等長(zhǎng)点骑,如下圖示:
當(dāng)我們把stroke-dasharray定義成1/2周長(zhǎng)時(shí),相當(dāng)于讓圖形實(shí)現(xiàn)了一半描邊效果谍夭,而CSS中stroke-dashoffset的值的變化黑滴,則對(duì)應(yīng)生成了動(dòng)效,定義差值為周長(zhǎng)是為了實(shí)現(xiàn)首尾相接連綿不斷的效果紧索。注意一下袁辈,這里說(shuō)差值為周長(zhǎng),也就是說(shuō)如果初始0%對(duì)應(yīng)的 stroke-dashoffset如果不是0珠漂, 那結(jié)束時(shí)100%對(duì)應(yīng)的也要變化晚缩,這是我們下面實(shí)現(xiàn)四個(gè)顏色流動(dòng)的基礎(chǔ)尾膊。
這里如果把stroke-dashoffset的值改成等值負(fù)數(shù),會(huì)得到相反方向的動(dòng)畫(huà)效果荞彼,感興趣的話可以自己試一下冈敛。
好了,逐步推進(jìn)鸣皂,實(shí)現(xiàn)了單色流動(dòng)抓谴,那雙色怎么辦?要再定義一個(gè)動(dòng)態(tài)單色流動(dòng)動(dòng)畫(huà)寞缝,然后進(jìn)行疊加么齐邦,哎,我們這種懶人總是想方設(shè)法偷懶第租,因?yàn)槲抑灰o這個(gè)單色流動(dòng)的動(dòng)效的底層加一個(gè)相同路徑實(shí)色描邊措拇,就得到了這種效果:
嗯,雙色流動(dòng)已完成(此為懶人法慎宾,非正解丐吓,無(wú)需掌握,看過(guò)算完)趟据。
好了正式進(jìn)階開(kāi)始了券犁,上面偷懶法只能解決兩個(gè)顏色的問(wèn)題,當(dāng)我需要多個(gè)顏色汹碱,腫么辦粘衬?
嗯,乖乖的多定義幾個(gè)描邊動(dòng)畫(huà)設(shè)置咳促,去寫(xiě)CSS屬性吧稚新。因?yàn)槊總€(gè)
<path>
路徑只識(shí)別一個(gè)描邊效果,那這種多色的只能用多條相同路徑疊加來(lái)實(shí)現(xiàn)了跪腹。我用圖示來(lái)表示一下:
當(dāng)然了褂删,針對(duì)我們四個(gè)顏色,如果把相同的五邊形路徑重復(fù)四遍是慘絕人寰的冲茸,這里我們可以用<defs>
元素或者<symbol>
元素來(lái)定義需要重復(fù)的路徑屯阀,然后用<use>
元素來(lái)引用,推薦<symbol>
轴术,是由于<symbol>
支持的屬性更多难衰,雖然在這個(gè)案例中無(wú)法體現(xiàn)出來(lái),但養(yǎng)成好習(xí)慣逗栽,需要用<defs>
的都可以用<symbol>
來(lái)代替盖袭。這里因?yàn)閐asharray的定義相同,所以統(tǒng)一到了路徑內(nèi)聯(lián)屬性里。
來(lái)看看代碼部分:
<svg>
<style>
@keyframes animate1 {
0%{stroke-dashoffset:0}
100%{stroke-dashoffset:1356}/*1356是路徑的長(zhǎng)度*/
}
@keyframes animate2 {
0%{stroke-dashoffset:339}/*定義了四個(gè)顏色苍凛,所以339是1/4周長(zhǎng)*/
100%{stroke-dashoffset:1695}/*需要dashoffset變化值是一個(gè)周長(zhǎng)來(lái)實(shí)現(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來(lái)定義需要重復(fù)引用的相同路徑-->
<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>
還算是很清晰的,而且如果用五個(gè)顏色兵志,那就初始的dashoffset遞增1/5周長(zhǎng)醇蝴,然后改一下dasharray為線長(zhǎng)1/5 間距4/5 就可以了。
得到的效果如下:
5.雙效合一
獨(dú)立設(shè)計(jì)形狀之間的變形動(dòng)畫(huà)和同一形狀的不同顏色描邊的動(dòng)畫(huà)都已經(jīng)實(shí)現(xiàn)了想罕,現(xiàn)在要做的就是把這兩個(gè)效果合在一起了悠栓。在我們上面實(shí)現(xiàn)“流光溢彩”動(dòng)效時(shí)把需要重復(fù)定義的路徑用<symbol>
進(jìn)行了定義,定義的<path>
的id值不是被賦予用了某個(gè)屬性按价,而是作為標(biāo)簽存在惭适,便于被<use xlink:href="#">
反復(fù)引用,但當(dāng)這個(gè)效果運(yùn)用到變形動(dòng)畫(huà)中時(shí)楼镐,會(huì)發(fā)現(xiàn)<path>
路徑的id對(duì)應(yīng)的是繪制路徑的變形動(dòng)畫(huà)癞志,那我們來(lái)?yè)Q個(gè)思路,把這四個(gè)路徑當(dāng)做獨(dú)立的存在框产,每個(gè)路徑在進(jìn)行變形動(dòng)畫(huà)的同時(shí)也在進(jìn)行描邊動(dòng)畫(huà)凄杯,此時(shí)我們的SVG定義的變形動(dòng)畫(huà)deform的關(guān)鍵幀不變,四個(gè)不同顏色的描邊動(dòng)畫(huà)的定義animate1-4的關(guān)鍵幀也不變秉宿,需要變化的是動(dòng)畫(huà)屬性:
#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;
}
即每個(gè)路徑的動(dòng)畫(huà)屬性同時(shí)賦予了兩種動(dòng)效戒突,一個(gè)是變形的deform動(dòng)畫(huà),一個(gè)是對(duì)應(yīng)的描邊動(dòng)畫(huà)描睦。
為了盡可能的優(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}
這樣忱叭,我們的對(duì)應(yīng)的四條路徑的代碼就簡(jiǎn)化成了如下:
<path id="animate1" />
<path id="animate2" />
<path id="animate3"/>
<path id="animate4"/>
這里如果有好奇的小伙伴可能會(huì)提出疑問(wèn)隔崎,我們的描邊動(dòng)畫(huà)在定義時(shí)用的周長(zhǎng)是五邊形的周長(zhǎng),但這個(gè)動(dòng)畫(huà)里的幾個(gè)形狀并不是等長(zhǎng)韵丑,腫么辦仍稀?
其實(shí)不要理會(huì),只要選一個(gè)最長(zhǎng)的路徑進(jìn)行定義就可以了埂息。因?yàn)槲覀兊穆窂绞且粚訉盈B加的技潘,如果圖形的周長(zhǎng)比定義時(shí)選擇的短,出現(xiàn)的結(jié)果就是最頂層的路徑會(huì)略長(zhǎng)一些千康,但對(duì)于這類(lèi)動(dòng)畫(huà)而言享幽,很難看出差別。
另外這里如果對(duì)變形的效果不滿意拾弃,可以自行調(diào)整路徑的方向和起點(diǎn)位置值桩,以前的文章里都有詳細(xì)的方法,不再贅述豪椿。
當(dāng)然了奔坟,手癢癢的我還是改了一下各個(gè)參數(shù)携栋,看了一下效果,比如我定義了stroke-dasharry:100 300 (線長(zhǎng)100 間距300的虛線)咳秉,同時(shí)改了其他的stroke-dashoffset的值婉支,依次差階100,然后得到了一個(gè)效果:
即使得到了相要的動(dòng)畫(huà)效果澜建,但積極努力追求上進(jìn)的我卻依然不滿意啊向挖,因?yàn)槲蚁胱屪冃蝿?dòng)畫(huà)在完成一個(gè)變形之后略作停留之后再進(jìn)行下一個(gè)變換。而不是像現(xiàn)在這種唰唰唰一氣呵成炕舵,于是乎何之,我改進(jìn)了一下,得到了下面這種效果:
我是用了偷懶的效果咽筋,把變形動(dòng)畫(huà)的關(guā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
小伙伴們自行查看咯悼潭。