先看一個動畫效果峰锁,這種小飛機沿路徑飛行(路徑部分線段變成綠色是錄屏軟件出了問題)虹蒋。
這種動畫效果最常見于發(fā)送信息后,兩個不同位置之間的導(dǎo)航指向等等塘辅,總之使用場景還是很多的扣墩。對于SVG動畫來說荆责,這種效果是最最簡單不過做院,只需要一個路徑外加幾個屬性的簡單設(shè)置就能完成键耕,簡單到不算飛機圖形的話郁竟,兩句代碼棚亩,而整個SVG文件只有1K左右大小讥蟆,我們由淺入深修然,從基礎(chǔ)開始愕宋,開啟SVG路徑動畫之門中贝。
我們把動畫元素拆解一下邻寿,由兩個部分組成绣否,一個是路徑蒜撮,一個是沿路徑運動的圖形元素段磨。
1.path路徑獲得
關(guān)于path路徑薇溃,SVG官方的定義如下:
- M = moveto
- L = lineto
- H = horizontal lineto
- V = vertical lineto
- C = curveto
- S = smooth curveto
- Q = quadratic Bézier curve
- T = smooth quadratic Bézier curveto
- A = elliptical Arc
- Z = closepath
這些還不是最恐怖的沐序,最恐怖的莫過于下面的三次貝塞爾曲線和二次貝塞爾曲線Q策幼。
各位UI設(shè)計師們特姐,線性代數(shù)可還會唐含?完全不記得了捷枯?很好淮捆,因為上面這些嚇唬人的知識在這個路徑動畫中統(tǒng)統(tǒng)用不著(你特么是在逗我攀痊?(ˉ﹃ˉ))(了解貝塞爾曲線的繪圖原理多用于曲線非簡單規(guī)律變化的復(fù)雜動畫苟径,不過現(xiàn)在有Airb
nb的Lottie神器了涩笤,用AE來做復(fù)雜的動畫轉(zhuǎn)成json文件是捷徑蹬碧,數(shù)學大神們除外恩沽。)
我們說過了罗心,AI里面的路徑在導(dǎo)出SVG時同樣會生成對應(yīng)的<path>
標簽渤闷。如果你是第一次看我的關(guān)于SVG動畫的文章也沒有關(guān)系飒箭,我們一步步分解弦蹂。
** 第一步:AI繪制一條由起點A到終點B的曲線,隨便描個邊翅溺。**
AI導(dǎo)出SVG的文件中,僅保留
<path>
標簽部分启昧。我的如下:
<path fill="none" stroke="#78EADF" stroke-width="30" stroke-linecap="round" stroke-miterlimit="10" d="M66.9,517
c0,0-37.8-194,125.1-161.9S427.6,371.5,350,205.7S439.3,23.4,544,62.2"/>
關(guān)于路徑描邊的各個屬性值不再贅述密末,注意標簽中d=""部分严里,后面我們的路徑動畫時要調(diào)用的就是這個路徑刹碾。
path路徑的參數(shù)由AI導(dǎo)出的SVG路徑中的d生成迷帜。
2.路徑動畫<animateMotion>
關(guān)于路徑動畫最基礎(chǔ)的語法簡單到如下:
<animateMotion path="" dur=""/>
<!--dur定義動畫時間-->
其中path即為我們在AI中繪制路徑自動生成的d="" 所包含的定義路徑曲線的部分戏锹。
現(xiàn)在锦针,如果我定義一個最簡單的圓形circle奈搜,加上路徑動畫屬性馋吗,已經(jīng)可以實現(xiàn)動畫效果了,代碼如下:
<circle fill="#F8B62D" r="20">
<animateMotion path="M66.9,517
c0,0-37.8-194,125.1-161.9S427.6,371.5,350,205.7S439.3,23.4,544,62.2" dur="3s"/>
</circle>
動畫效果如下:
這里還有一個方法商架,就是我們給藍色路徑定義一個id蛇摸,然后路徑動畫來引用這個id赶袄,代碼如下:
<!--road為我們定義的路徑id-->
<path id="road" fill="none" stroke="#78EADF" stroke-width="30" stroke-linecap="round" stroke-miterlimit="10" d="M66.9,517
c0,0-37.8-194,125.1-161.9S427.6,371.5,350,205.7S439.3,23.4,544,62.2"/>
<circle fill="#F8B62D" r="20">
<animateMotion dur="3s">
<mpath xlink:href="#road"/> <!--通過路徑鏈接屬性調(diào)用定義的路徑-->
</animateMotion>
</circle>
這兩種方法均可蒋困,重點是第二種方法雪标,后面將要揭秘村刨。
3.任意圖形的路徑動畫
看了上面的是不是感覺so easy,但這肯定不是我們的需求龄糊,開始拋磚引玉炫惩,比如诡必,我準備做一個沿路徑奮力爬行的小瓢蟲的動畫爸舒。先準備素材扭勉,在AI中繪制一只小瓢蟲,比如圖層我命名beatles
然后得到一堆<g id="beatles">……</g>
的SVG代碼苛聘。
接下來就很簡單了涂炎,用甲蟲的圖形來替換上面動畫中的圓形,其他不變设哗。得到代碼如下:
<g id="beatles">
……<!--此處省略繪制甲蟲的代碼若干-->
<animateMotion path="M66.9,517
c0,0-37.8-194,125.1-161.9S427.6,371.5,350,205.7S439.3,23.4,544,62.2" dur="3s"/>
</g>
期待已久的時刻來了唱捣,正常思路,我們的小瓢蟲可以沿著路徑爬行了网梢。各位看官先不要激動震缭,此時战虏,你看到的動畫應(yīng)該是這個樣子的(為了方便觀察膛堤,我把畫布調(diào)整了大小次企,留出足夠的空間)堵第。
小甲蟲针余,你要去哪里行您?你快回來。再看我們的代碼橡疼,沒毛病历帚,甲蟲的位置明明就是擺在路徑的起點上禽拔。不過我們?nèi)阅馨l(fā)現(xiàn)的一個規(guī)律是:雖然小瓢蟲跑偏了茧痕,但跑的姿勢似乎還是對的舀患。為什么圓形沒事,換個復(fù)雜的圖形就不可以了呢?我們上面圓形按照路徑移動時,并沒有定義圓形的圓心位置(cx和cy值)灸姊,僅定義了半徑r父晶。好了茎匠,接下來要放大招了,這是你以后制作SVG路徑動畫的關(guān)鍵圈盔。
4.任意圖形的位置校正
先來看一句話: 定義了路徑動畫的圖形會把路徑的起點作為原點娩梨。讀起來拗口且難懂,炒個栗子吧流礁。我繪制的路徑起點坐標為X=66.9霎桅,Y=517著角,對于了解SVG路徑的小伙伴們都知道囚痴,其實就是path的起點M值。那對于甲蟲來說,(66.9,517)才是它坐標系的原點员淫,而不是(0,0)刃宵。如下圖所示坦袍,整個坐標系向右偏移了66.9px缝其,向下偏移了517px嘴高。
所以最終的效果就是甲蟲沿著偏移后的灰色路徑移動喇闸。
知道了原因就可以對癥下藥了唆樊。
4.1 方法1——把甲蟲拉回它應(yīng)該在的位置
既然知道偏移的值,再把它放回去刻蟹,放回去的話就很簡單了逗旁,我們只要把甲蟲移動到畫布的左上角原點的位置(即x=0,y=0)
此時再導(dǎo)出SVG舆瘪,除了甲蟲圖形代碼替換片效,得到下面的效果:
小瓢蟲強勢回歸!
4.2 方法2——使用SVG的defs元素
這個方法相當于方法1的擴展英古,你可以使用defs來定義圖形淀衣,use標簽來調(diào)用,同樣需要圖形位于畫布原點召调,代碼如下:
<defs>
<g id="beatles">
……<!--此處為繪制瓢蟲的代碼-->
</g>
</defs>
<use xlink:href="#beatles" x="0" y="0">
<animateMotion dur="3s">
<mpath xlink:href="#road"/> <!--方法2和方法1由于路徑是同一條膨桥,所以都適合用調(diào)用路徑的方法-->
</animateMotion>
</use>
用defs來定義圖形的好處是,你可以使用use標簽在不同位置重復(fù)調(diào)用這個圖形唠叛,比如我下面說明運動速率時擺放的5只小甲蟲只嚣。
由方法1和方法2我們得出一個結(jié)論,圖形繪制的時候無所謂位置艺沼,只要最后在畫布的左上角原點就可以了册舞。
4.3 方法3——重新定義移動路徑
上面的那個方法雖然可以實現(xiàn)正常路徑運動,但實際中存在一個問題(我的動畫播放時使用了無限循環(huán)障般,所以看不出)调鲸。當動畫設(shè)置了延遲開始(eg. begin="2s")時盛杰,甲蟲在動畫開始前并不是乖乖的待在起點A,而是移動后的位置线得,畫布左上角饶唤,2s后動畫開始播放徐伐,甲蟲才回到A點贯钩。來看下面這種解決方案,重新定義圖形的移動路徑办素。
跟著我左右右手一個慢動作角雷,右手左手慢動作重播……我們用相同的方法來移動,不過性穿,這次我們移動的是路徑勺三,記得選擇“復(fù)制”,或者干脆點需曾,直接把起點拖到畫布的原點吗坚。
然后為了方便區(qū)分,建議你給移動后的新路徑區(qū)分一下描邊方法呆万。
好了商源,甲蟲按兵不動,導(dǎo)出SVG時谋减,復(fù)制后的路徑那一堆代碼我們只需要d值牡彻,我的路徑動畫代碼也就變成了下面的樣子:
<animateMotion path="M0,0
c0,0-37.8-194,125.1-161.9s235.7,16.5,158.1-149.3s89.2-182.3,194-143.5" dur="3s"/>
現(xiàn)在,所有都恢復(fù)正常出爹,我們的小甲蟲又可以乖乖的從A爬到B了庄吼,而且無論何時,起點和終點都是我們預(yù)期值严就。(但這個方法有個很大的bug总寻,后面再說)
作為有追求的設(shè)計師,這個動畫效果是不是感覺low到爆梢为?首先渐行,瓢蟲移動速度不符合物理規(guī)律,勻速運動抖誉,其次殊轴,身體沒有相應(yīng)的轉(zhuǎn)動,生硬不生動袒炉。好了旁理,來,加上這幾個屬性我磁,改善一下孽文。
5. 運動速率的設(shè)定
先給animateMotion加上下面的代碼:
calcMode="spline" keySplines="" keyTimes="0;1"
calcMode屬性定義動畫的類型驻襟,一共四個屬性值,其他不說了芋哭,"spline"要搭配后面的keySplines和keyTimes屬性一起使用沉衣,以上代碼的意思是宣告“我要來隨心所欲的控制運動速率了!”
keySplines值的設(shè)定與CSS的貝塞爾曲線相同减牺。
關(guān)于運動速率的貝塞爾曲線(說好了不提它豌习,又來!)有個在線工具可以借助:http://cubic-bezier.com/
這里你可以自定義運動速率曲線并查看效果拔疚,不過如果不是需要一些特別的效果肥隆,建議使用以下幾個固定值:
"慢-快-慢 ease":".25,.1,.25,1"
"線性 linear":"0,0,1,1"這個是默認值,可以不用定義稚失。
"慢開始 ease-in":".42,0,1,1"
"慢結(jié)束 ease-out":"0,0,.58,1"
"慢-正常-慢 ease-in-out":".42,0,.58,1"
我做一個甲蟲水平移動的動畫栋艳,來對比一下這五種不同的速率曲線的效果。
同時出發(fā)句各,但中間速度有差吸占,殊途同歸,又同時到達終點凿宾。
這里我搞了個事情出來矾屯,把速率曲線畫成了下面這個樣子:
所以我的keySplines=".28,1.89,.56,-1.32"。現(xiàn)在我的甲蟲移動方式是這樣的:
怎么樣菌湃,是不是還算有趣问拘。花樣可以很多惧所,自行嘗試骤坐。
keyTimes值(0;1)是最簡單的一種,沒有對運動過程進行分割下愈,如果你想玩出更深的套路纽绍,可以把路徑截成好幾段,然后定義不同的keySplines值势似。再炒個栗子拌夏。
keyTimes="0;0.66;1" keySplines=".42,0,1,1;0,0,1,1;" 就是路徑的前2/3,我希望用ease-in函數(shù)定義速度履因,路徑的后1/3線性速度障簿,但在銜接處會抖一下,因為實在使用場景很少栅迄,所以keyTimes值就用最基礎(chǔ)的就好站故。
6. 跟隨路徑曲度的旋轉(zhuǎn)方向
這個簡單,只有一句代碼:rotate="auto",此時的路徑動畫已經(jīng)實現(xiàn)的有模有樣了西篓。
前面提到過愈腾,4.3方法3——重新定義移動路徑,有個bug的問題岂津,就是這里虱黄,因為旋轉(zhuǎn)方向是依據(jù)路徑的走向,如果使用移動過后的路徑吮成,那旋轉(zhuǎn)的效果簡直不忍直視橱乱。
這里有一個悖論,方法1和方法2赁豆,正常旋轉(zhuǎn)仅醇,但初始的甲蟲的位置不能在起點,方法3魔种,初始位置在起點,但不能正常旋轉(zhuǎn)粉洼。(抓狂ing……)使用過程中選哪個選哪個节预,好糾結(jié)。
好了属韧,設(shè)計師們安拟,不用糾結(jié),選1和2宵喂,因為……
……
因為……
我們可以用js定義動畫開始的時間糠赦,哈哈,根本不需要begin屬性锅棕。
好了拙泽,開始放可以復(fù)用的代碼并注釋,一切不能復(fù)用的SVG動畫代碼都是耍流氓裸燎!
<svg width="" height="">
<path id="road" fill="none" d=""/><!--路徑全部由AI直接生成-->
<g>
……
<!--此處為若干圖形代碼-->
<animateMotion dur="" repeatCount="" rotate="auto" calcMode="spline" keyTimes="0;1" keySplines="" ><mpath xlink:href="#road"/></animateMotion>
</g>
</svg>
嗯顾瞻,over,就是介么簡單德绿。剩下的只是填空荷荤。
比如dur定義全部動畫需要的時間,keySplines定義運動速率類型移稳,repeatCount定義播放次數(shù)蕴纳,上面都有一一解釋。
那么利用路徑動畫个粱,都能實現(xiàn)什么效果呢古毛,來繼續(xù)看:
7.1 路徑動畫功能擴展——伴隨圖形變化
現(xiàn)在我要實現(xiàn)一個小甲蟲漸行漸遠的效果,為了讓效果看起來更逼真几蜻,我把底圖描邊路徑重新做了一下喇潘,然后把keySplines="0,0,.58,1"即運動速率為ease-out慢結(jié)束体斩,如下:
這個效果實現(xiàn)也不過再加一句縮放動畫代碼
<animateTransform dur="" attributeName="transform" fill="freeze" type="scale" from="1 1" to="0.5 0.5" />
時間與路徑動畫保持一致,type="scale" from="1 1" to="0.5 0.5"表示寬高縮小到原來1/2颖低。
你可以搞得很復(fù)雜絮吵,比如伴隨甲蟲腿的擺動,但涉及到復(fù)合動畫忱屑,好麻煩蹬敲,這也是我為什么沒有用一只帶腿的甲蟲的原因,哈哈莺戒。
7.2 路徑動畫功能擴展——搭配描邊動畫+蒙版
結(jié)合描邊動畫和蒙版伴嗡,我們可以實現(xiàn)下面這種效果:
一架一路辛苦播撒小豆的飛機。
動畫拆解:先繪制一個點狀組成的螺旋線路徑从铲,然后給這個螺旋線加一個蒙版瘪校,把描邊動畫賦給蒙版,描邊路徑即為螺旋線路徑名段,顏色為白色阱扬,相當于通過蒙版動態(tài)畫一根白色螺旋線來實現(xiàn)點狀螺旋線路徑同步顯示出來。小飛機就按我們上面介紹過的的路徑動畫來定義伸辟。
感興趣的話可以把下邊的代碼拿去復(fù)用:
<svg width="600" height="600" >
<style>
@keyframes dash {
to {
stroke-dashoffset: 0;
}
}
#helix{
stroke-dasharray:2006; /*2006為螺旋線的長度*/
stroke-dashoffset:2006;
animation: dash 4s linear forwards; /*蒙版動畫的速率和時間與飛機路徑動畫保持一致*/
animation-delay:0.2s; /*為了讓飛機稍微領(lǐng)先麻惶,設(shè)置了蒙版動畫0.2秒的延遲*/
}
</style>
<mask id="helix">
<!--蒙版調(diào)用定義的描邊動畫helix,d=""包含部分為螺旋線的路徑-->
<path fill="none" stroke="#fff" stroke-width="16" stroke-linecap="round" d=""/>
</mask>
<path mask="url(#helix)" id="road" fill="none" stroke="#78EADF" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="0,30" d=""/>
<g id="plane">
…<!--此處飛機代碼若干-->
<animateMotion fill="freeze" dur="4s" rotate="auto" calcMode="spline" keyTimes="0;1" keySplines="0,0,1,1" ><!--keySplines值的設(shè)定要與描邊動畫保持一致信夫,如果上面animation定義了ease窃蹋,則這里也要對應(yīng)改成".25,.1,.25,1"-->
<mpath xlink:href="#road"/>
</animateMotion>
</g>
</svg>
知識點總結(jié):
①動畫的路徑直接通過AI繪制后導(dǎo)出的SVG的d值獲得。
②圖形元素移動到畫布原點后再生成對應(yīng)的SVG代碼静稻。
③通過定義calcMode警没、keyTimes以及 keySplines來修改運動速度。
④定義rotate屬性來實現(xiàn)跟隨路徑曲率的旋轉(zhuǎn)效果姊扔。
⑤與其他動畫的組合惠奸,需要多多的創(chuàng)意。