姜泰——前端架構工程師
14年以上開發經驗,對client和server開發都有著深刻認知,現在依然每周都在學習數學。
最近京東二手拍拍團隊製作了一個小程序,叫「附近有閒」
發布求購信息叫「紙飛機」,發送信息完畢之後,屏幕會出現一個紙飛機的飛行軌跡。
動畫設計需求為:
1、紙飛機需要平滑的運動
2、有多種飛行方式
>>>> 紙飛機飛翔動畫>>>> css3動畫動畫的實現方式有很多種,大部分人想到jq的amination,css3的amination和transition
1、position vs transform
對比一下,左邊是布局方式的left和top,右邊是transform的translate,尤其是繪畫和渲染簡直天差地別。看了chrome的專業介紹,transform會走硬體加速,就是機器的顯卡越好,這個差距越明顯。
2、運動動畫
回過來繼續說飛機,我們寫個飛機飛行的css3,讓飛機從0px飛到1000px
@keyframes move {
from {transform: translateX(0px)}
to {transform: translateX(1000px)}
}
然後給標籤應用動畫,讓標籤播放動畫,10秒播放完畢。
#plane {
animation: move 10s linear;
width: 100px;
height: 500px;
}
動畫雖然播放了,可是設計的要求肯定不是讓你這麼直直的飛,效果太差了。但是css3隻能這麼弄,我們加上translateY就可以斜著飛了,貌似這是極限了。
@keyframes move {
from{transform: translate(0px, 0px)}
to{transform: translate(1000px, 500px)}
}
3、運動合成
實際上,我們的思維不能這麼局限,回想一下中學的物理和數學。
速度是可以分解的,v等於v1和v2的合成。
再想想著名的平拋運動——x軸做勻速直線運動,y軸做自由落體運動(重力加速運動),因此,我們也可以分兩個div,一個大div套著小div。
<div id="plane">
<div id="entity"></div>
</div>
設置一個y的運行0px到500px
@keyframesfalling {
from{transform: translateY(0px)}
to{transform: translateY(500px)}
}
注意動畫這裡,雖然還是10秒完成動畫,但是用了淡入,就是先慢後快。
#entity {
animation: falling 10s ease-in;
width: 50px;
height: 50px;
}
我們也可以改變一下這個動畫方式,比如改成5秒執行兩次並且反彈。
animation: falling 5s ease-in alternate 2;
其他的方式就更多了,總之,就看你怎麼組合了。
>>>> canvas動畫但是我們的紙飛機不能就這麼一種飛法啊!
設計小姐姐給我們提出了要多幾種飛行方式,可是每一種方式要調整到設計滿意的樣子都很難,更別說是那麼多種飛行方式了。如果設計妥協,那我們的產品精緻程度就要大打折扣。
這時候,我們重新捋一遍,設計做的效果肯定是有各種軟體,其實軟體也是把數據做了渲染。我們是否可以把設計做好的東西裡面直接把數據應用到我們的開發中呢?這樣肯定是還原了設計,並且修改成本非常低。
其實,設計就是畫了一條線,然後拉成曲線,就是我們常說的貝塞爾曲線。
是不是聽了很懵逼?
不用想那麼多了!我封裝了一個game.class.js類,你只需要在引用就行了。以後我們也多多這種類,別人初始化用一下就行了,根本不用去理解你怎麼完成的。(這個類是小程序專用,h5需要修改)
var context = wx.createCanvasContext('aeroplane', this);//構造畫布
var game = new Game.main(context);//構造game類
game.setPlane(plane);
game.launch(bezier);//啟動
Context就是canvas的context,你可以隨意創建一個;Plane是飛機的數據有地址和寬高。
plane: {
url:"/static/images/3.png",
width:80,
height:77.6
}
Bezier就是一個數組,裡面放三個坐標就是二次貝塞爾,四個坐標就是三次貝塞爾。
cube: [ //三次貝塞爾
{
x: -20,
y:600,
z:1
},
{
x:200,
y:500,
z:1
},
{
x:250,
y:300,
z:.5
},
{
x:300,
y:50,
z:.5
}
],
square: [ //二次貝塞爾
{
x:0,
y:200
},
{
x:100,
y:0
},
{
x:200,
y:200
}
],
有了這些,就可以隨意的生成小飛機動畫了。
>>>> 動畫解析但是有些童鞋,表示「我就喜歡刨根問底,告訴我怎麼做的!」好,我們一步步來。
>>>> 二次貝塞爾曲線其實,就是給起始點和結束點中間加了一個控制點。
公式為:
>>>> 三次貝塞爾曲線其實,就是給起始點和結束點中間加了兩個控制點。
公式為:
由於有了兩個控制點,可以更隨意的繪製路徑。
>>>> 函數解析所謂的公式其實就是函數(function),比如這個公式,就是一個叫B的函數,參數為t,t的取值範圍是0~1,p0~3這裡其實是常數,因為在變化過程中他是不變的。
我們拿個實際例子吧:
開始位置為(-20,600)
第一個控制點為(200,500)
第二個控制點為(250,300)
結束點為(300,50)
我們用公式算出點
t=0.1
x = -20*(1-0.1)3 + 3*200 * 0.1*(1-0.1)2 +3* 250*0.12*(1-0.1)+300*0.13
y = 600*(1-0.1)3 + 3*500 * 0.1*(1-0.1)2 +3* 300*0.12*(1-0.1)+50*0.13
t=0.5
x = -20*(1-0.5)3 + 3*200 * 0.1*(1-0.5)2 +3* 250*0.12*(1-0.5)+300*0.13
y = 600*(1-0.5)3 + 3*500 * 0.1*(1-0.5)2 +3* 300*0.12*(1-0.5)+50*0.13
……
這樣,所有點的集合為這樣的一條貝塞爾曲線。
既然畫線知道了,我們把所有點做為飛機的做標來設置就可以了,並且t與時間相匹配,比如setinterval,設置300毫秒執行一次飛機定位,並且t+=0.1,於是就完成了一個動畫,如果想讓動畫更連貫,最好用requestAnimationFrame,並且分段更細一點。
這樣設計只要做出滿意的動畫軌跡,然後把關鍵點的做標給技術,馬上動畫就完成了。並且t最好也用緩動來完成,因為飛機開始起飛慢,中間快,最後停下又慢。
>>>> 反三角函數但是飛機飛行的時候不是一直水平的,它是按照曲線的切線運動的。這個怎麼實現呢?就要用我們的反三角函數了。
不對,是反→
我們知道當前的坐標,又知道上一個坐標,然後yt-y0和xt-x0就是三角函數的值,把這個用反三角函數就可以求出角度(其實是弧度)了,然後讓其旋轉。
let r = Math.atan2(p.y - yt, p.x - xt);
_context.rotate(r);
記住,一定要用atan2不要用atan,那個只能算出π/2之內的值。
>>>> 總結總的來說,我們的思維要開闊一些。其實自己深度不夠沒事,我們可以增加我們的深度;最怕的是都不知道可以往某個方面去想,所以我們要多增加點交流,技術儲備就會越來越多。
點擊閱讀原文,下載紙飛機demo
現京東商城-拍拍二手團隊誠招:高級前端研發工程師,web、app方向均可
坐標:京東集團總部,地鐵旁邊,全程班車
要求:只要你足夠NB,夠膽你就來!
簡歷投遞郵箱:dingxiufu@jd.com
-END-
下面的內容同樣精彩
點擊圖片即可閱讀
京東技術 ∣關注技術的公眾號