我用SVG+CSS3做了一個舞動的機械人——致AI時代的到來

泱泱發表於2017-11-03

想法來源於codepen大神的一個神作,一群機械人以神一樣的步伐在進行群魔亂舞,可是,可是,開啟一看,純js實現,js!也罷,我偏要試一下用SVG+CSS3能不能搞出這個效果來,o( ̄ヘ ̄o#)。此為前提。

1.基礎圖形與基礎動畫

先來個簡易版的機械人吧,因為重點是動效,所以就簡易到不能再簡易,全部膠囊樣式的圓角矩形來搞,但為了方便以後替換個胳膊腿什麼的,我還是乖乖的把每個準備運動起來的零件都拆分到了不同的圖層,並且給關節部位打了一個螺絲釘。好吧好吧,簡易螺絲釘,打了個黑點行了吧,在AI裡繪製完成後,這貨是下面這樣滴。(為了方便疊加,我給加了透明度,後來發現有透明度居然自帶設計感,那就這樣吧)

雖然沒有科技感,且遍佈全身的詭異黑點導致它看上去像是十八銅人或者中醫鍼灸啊穴位啊的什麼東西,但是這不妨礙我們的機械人運動,來吧,給大家打個招呼,動動頭先,走起來。

CSS部分略通一二的小夥伴都知道,再簡單不過,transform:rotate屬性,這裡唯一要注意的是在定義animation時,一定要定義transform-origin,旋轉的原點,這裡就是我在關節處打了一堆黑點的意義了。因為我AI匯出的SVG中,每個關節,也就是<circle>的圓心cxcy的值,就是各個部位旋轉的原點。

很好,很聽話。既然動起來有了生命,管它矽基還是碳基,給“他”起個帥氣的名字,叫起來也方便。大白(●—●)不能叫,那就叫小冰吧。“Hi~,我是小冰”

小冰,現在命令你動一下左腿,擺幅大一些。

好的,接下來,其他部位一併動起來。


好了檢測完畢,一切正常。等等,似乎哪裡不太對,聰明如你一定發現了詭異之處,那就是——動效裡都是肢體的末節在動,小腿動大腿不動,胳膊動胳膊肘不動,咳咳,是的,這是一個被釘住只能掙扎的機械人。釘住小冰的就是那一堆“螺絲”。為什麼這麼搞呢?理由很簡單,我讓大腿動一下,看看發生了什麼

這是?各甩各的,誰都不理誰?拜託,你們可是一個整體哎。現在的問題來了,我們CSS動畫屬性定義的時候,每個可活動的形狀的旋轉原點都是給了固定值,那既然如此,怎麼才能讓末肢的原點隨著上肢的擺動而動起來呢?

2.SIML路徑動畫解決方案

凡事皆要慢慢來,先進行第一步拆解,只讓上半部分肢節動起來。

為了方便檢視(現在眼花繚亂的不能直視),小冰聽令,先暫停擺動,右大腿帶著右小腿動一個。(大腿和小腿用相同的旋轉動畫定義,包括原點,統一定義為大腿旋轉的原點)

有沒有毛病?有!不符合物理規律的樣子。我們先用圖來分析一下,然後來對症下藥。


大腿從位置A甩到位置B,我希望得到小腿的效果是始終保持下垂。相當於小腿部分沒有擺幅度的運動,也就是沿著圖中的虛線軌跡運動。等等,想起來了點什麼沒?軌跡?我們在路徑動畫中提到過是可用控制擺動的,<animateMotion rotate="auto">這是定義隨著路徑擺動,不定義的話預設不擺動,那既然圓形也可以是路徑,我們這裡用路徑動畫實現一下看看如何。關於路徑動畫的詳解我以前寫過詳解了,這裡只看效果:

似乎解決了問題的樣子。但是,新的問題已然出現,就是我要甩大腿的過程中小腿自然擺動,怎麼搞?

3.拆解SVG,利用獨立座標系解決方案

去他的路徑動畫,正解在這裡,因為一個SVG是一個座標系,也就是說我們定義旋轉原點的時候都要遵循同一個規則,在立方體旋轉動畫裡,我們曾經搞過,六個面,六個SVG,裡面各自動畫進行,互不干擾,這個思路放到我們這個動畫中,依舊可行。


簡單來說,我把需要擺動的四個末肢放到四個SVG裡,還是拿右腿為例。看圖,看圖。小腿所在的獨立的SVG先與大腿保持相同的旋轉運動,當然,用的也是大腿的旋轉原點O2,裡面組成小腿的形狀按自己座標系的旋轉原點O1運動,因為SVG運動的過程中,座標系也在運動,所以雖然相對於原始座標系小腿的運動的原點一直在變化,但在它自己的座標系裡,卻始終不變。有點拗口,但理解了動畫思路也就打通了。
貼上部分程式碼順便解釋一下
首先,最重要的是,放到html中,首先要把SVG的位置屬性定義成絕對,這裡不賣弄了,小夥伴都懂得,因為我們的SVG是互相重疊的,所以

SVG{position:absolute}

運動的CSS程式碼如下(只放了右腿的):

 @keyframes rightLeg{              /*定義右腿大腿的擺動(旋轉動畫)*/
 0%{transform:rotate(0deg); }
 100%{transform:rotate(-90deg); }
 }
 .rightLeg{
animation:rightLeg 1s ease alternate infinite; 
transform-origin:           /*旋轉原點O1*/
}

@keyframes rightLeg2{         /*定義右腿小腿的擺動(旋轉動畫)*/
0%{transform:rotate(-50deg); }
100%{transform:rotate(50deg); }
}
.rightLeg path{
animation:rightLeg2 0.4s ease alternate infinite ;
transform-origin:          /*旋轉原點O2*/
} 複製程式碼

DOM部分,大腿、胳膊肘兒、頭、身體是同一個SVG,組成小腿部分是單獨的SVG

<svg class="rightLeg">
<path d=""/> <!--這裡可能是path,也可能是rect,總之AI匯出什麼就是什麼啦。-->
</svg>複製程式碼

好了,小冰,聽口令,現在讓大家看一下正常的機械腿。


perfect!現在小腿和大腿可以無縫連線在一起,想怎麼動就怎麼動,運動速度啊,擺動幅度啊,任意定義。

剩下的又是體力活了,現在把其餘的部分按照上面的思路拆解就可以咯。我隨隨便便定義了一下(說是隨便,但每個都要定義一堆,也是頭昏腦漲啊),然後就得到了下面這段動畫。


機械舞,夠不夠魔性。
當然了,如果你有足夠的創意的話,可以隨便玩兒嘛,反正小冰非我族類,怎麼運動都可以。比如齊刷刷的左右擺動(左臂右臂定義成相同的動畫,左腿和右腿定義成相同的動畫)


總之,怎麼定義都行,一切隨意。

還記不記得我們的跳幀動畫,見這篇文章juejin.im/post/597694…
裡面做了一些效果,比如“咔噠咔噠”的時鐘,我們的小冰也可以“咔吧咔吧”的動起來。在定義動畫animation時,去掉ease啊,linear啊,或者cubic-brazier這些定義速率的值,換成steps(),就會得到缺少潤滑油的小冰,關於steps(n)裡n值的定義,可以不要太過隨意,根據旋轉動畫的時間,使咔吧的頻率保持一致,會更嚴謹一些。我做了效果可以看一下:

再比如我們來個炫酷的組裝過程。(動畫第一部分為零件組裝,也就是以transform:translate位移動畫為主)


就這樣吧,拆得再散實在招架不住了。

如果你覺得身體部分一動不動,太過死板的話,no problem,先理清楚思路,首先,所有的零件一起移動,同時,大腿在自己的座標系做旋轉運動,小腿在自己的座標系做旋轉運動。這樣的話,要對原DOM結構再加入一個容器<div>。把所有的SVG放到這個<div>中。
CSS部分原來的不變,增加一個身體移動部分的定義如下:

 @keyframes base{        
 0%{transform:translateX(-100px); }
 100%{transform:translateX(100px); }
 }
.base{
animation:base 1s ease alternate infinite;
}複製程式碼

或者再增加點擺動,就得到了下面這個效果:

在基礎的動效基礎上,可以對機械人小冰進行各種美化,去替換SVG的組成部分就可以,比如下面這種:


記得重新定義旋轉原點!!

關於動效聯動重點是相對座標系的使用,也就是說,如果一個元素需要“自動”和“公動”(源自自轉和公轉,自己造詞)方法就是放到單獨的SVG中,每個SVG擁有自己獨立的座標系,組成元素的“自動”遵循自己的座標系。

這個方法一旦掌握,再做動效時簡直為所欲為,做機械臂很簡單吧?做摩天輪很簡單吧?感興趣的小夥伴可以自行嘗試。
另外,另外,其實我知道最好的方法是用js,比如我們這種動畫,可以js去獲取關節部分的座標值,再賦值給旋轉原點,可是,這不是不會嘛~~手動(* ̄︶ ̄)

相關文章