什么是動(dòng)畫牌芋?
就像思考哲學(xué)問(wèn)題無(wú)法回避思維和存在的關(guān)系一樣迂猴,制作動(dòng)畫同樣無(wú)法逃避的問(wèn)題是動(dòng)畫的原理是什么佑菩?這里提一句題外話扁凛,任何原理的東西通常難以讓你短期拾掇成果忍疾,但在隱約的未來(lái)會(huì)起到難以置信的效果,不信就看接下來(lái)小羊的一些學(xué)習(xí)成果分享令漂。
動(dòng)畫本質(zhì)上是圖像按照事先設(shè)定好的順序在一定的時(shí)間內(nèi)的圖像序列變化運(yùn)動(dòng)膝昆。
這種圖像序列的變化運(yùn)動(dòng)給我們最為直觀的感受就是圖像仿佛真實(shí)的在運(yùn)動(dòng)一般,由此產(chǎn)生動(dòng)畫效果叠必。
然后荚孵,事實(shí)并非如此,真相往往難以用肉眼觀察得到纬朝,除非你是上帝~~~
動(dòng)畫的特性在于:
- 每一張圖像的內(nèi)容是事先設(shè)定好的收叶,內(nèi)容是不變的,變化的是圖像序列按照規(guī)定的順序在變動(dòng)共苛;
- 構(gòu)成動(dòng)畫特效需要在單位時(shí)間內(nèi)渲染一定量的圖像判没,每張圖像稱之為幀(Frame)蜓萄,通常電影只需要24FPS就足夠流暢,而游戲則需要60FPS澄峰,我們?cè)O(shè)計(jì)動(dòng)畫時(shí)通常選用60FPS嫉沽;
總之,你所看到的動(dòng)畫無(wú)非是你的眼睛在欺騙你的大腦俏竞,本質(zhì)上每一張圖像還是那張圖像绸硕,只不過(guò)它們?cè)趩挝粫r(shí)間內(nèi)按照一定順序在快速移動(dòng)罷了~~~
Canvas API的簡(jiǎn)介
這一部分為了兼顧之前未接觸canvas元素的看官以及重溫canvas的目的,幫助小羊和各位快速過(guò)一遍canvas的API魂毁。
//demo.html
<canvas id='canvas' width='500' height='500'></canvas>
[注]
設(shè)置canvas的寬高要在元素或在其API玻佩,canvas.width || canvas.height,在CSS上設(shè)置為得到意想不到的結(jié)果席楚,不信試試看哈···
//demo.js
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
//路徑或圖形
context.fillRect();//填充矩形
context.strokeRect();//勾勒矩形輪廓
context.arc(x,y,r,anglestart,angleend,clockwise);//畫弧形
context.beginPath();//開(kāi)始路徑
context.moveTo();//定義路徑起始點(diǎn)
context.lineTo();//路徑的去向
context.closePath();//畫完后咬崔,關(guān)閉路徑
context.fill() || context.stroke();//最后畫出由路徑構(gòu)成的圖形
[注]
本質(zhì)上,所有的多邊形都可以由路徑畫出烦秩;
context.save();//保存save以上context對(duì)象設(shè)置的屬性值
context.restore();//恢復(fù)到先前保存在context對(duì)象上的所有屬性值
這里在介紹一下實(shí)現(xiàn)動(dòng)畫效果的非常重要的API:
window.requestAnimationFrame(callback)
先前我已經(jīng)說(shuō)過(guò)垮斯,動(dòng)畫是在單位時(shí)間內(nèi)按照一定順序的圖像序列的變化形成的;
這個(gè)API的功能就是闻镶,你可以在回調(diào)函數(shù)里面寫一個(gè)腳本改變圖形的寬高甚脉,然后這一API就會(huì)根據(jù)瀏覽器的刷新頻率而在一定時(shí)間內(nèi)調(diào)用callback;
然后铆农,根據(jù)遞歸的思想,實(shí)現(xiàn)callback的反復(fù)調(diào)用狡耻,最終實(shí)現(xiàn)動(dòng)畫效果墩剖;
不明白,上代碼
(function drawFrame(){
window.requestAnimationFrame(drawFrame);
//some code for animation effect here
})();
上面的代碼意思是立即執(zhí)行drawFrame這個(gè)函數(shù)夷狰,發(fā)現(xiàn) window.requestAnimationFrame(drawFrame)岭皂,okay根據(jù)瀏覽器的刷新頻率,在一定時(shí)間之后執(zhí)行沼头;
接下來(lái)執(zhí)行你所編寫的改變圖像內(nèi)容(圖像的位置爷绘、寬高、顏色等等)的腳本进倍,執(zhí)行回調(diào)土至;
循環(huán)反復(fù),形成動(dòng)畫效果
由此也可知道:
window.requestAnimationFrame這個(gè)API你可以理解為window.setTimeout(callback,time)
事實(shí)上猾昆,當(dāng)部分瀏覽器不兼容這個(gè)API時(shí)陶因,我們也可以寫成以下形式:
if(!window.requestAnimationFrame){
window.requestAnimationFrame = (
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRquestAniamtionFrame ||
window.oRequestAnimationFrame ||
function (callback){
return setTimeout(callback,Math.floor(1000/60))
}
)
}
Okay,有了這么幾個(gè)基本的canvasAPI就足以應(yīng)對(duì)接下來(lái)的知識(shí)點(diǎn)了垂蜗,如有不懂或深入了解楷扬,詳見(jiàn)Canvas教程-MDN解幽。
【注】
以下所有代碼托管到【github】
動(dòng)畫的數(shù)理分析
有了前面的基礎(chǔ)知識(shí),現(xiàn)在我們就會(huì)想:如果我們能夠在每16ms(1秒60幀烘苹,1000/60)內(nèi)渲染1張圖像躲株,并且每一張圖像的內(nèi)容發(fā)生微調(diào),那么在1秒鐘整個(gè)畫面就會(huì)產(chǎn)生動(dòng)畫效果了镣衡。
內(nèi)容的微調(diào)可以是圖形的移動(dòng)的距離霜定、轉(zhuǎn)動(dòng)的方向以及縮放的比例等等,而“捕獲”這些數(shù)據(jù)的方法就用使用到我們以前忽視的解析幾何的知識(shí)了捆探。
移動(dòng)的距離
-
線性運(yùn)動(dòng)
線性運(yùn)動(dòng)就是物體朝特定方向的運(yùn)動(dòng)然爆,運(yùn)動(dòng)過(guò)程中速度不發(fā)生改變;
<canvas id="canvas" width="500" height="500" style="background:#000"></canvas>
<script src='../js/utils.js'></script>
<script src='../js/ball.js'></script>
<script>
//這個(gè)腳本中黍图,勻速運(yùn)動(dòng)的原理是通過(guò)連續(xù)改變?cè)c(diǎn)在x軸上的坐標(biāo)曾雕,從而實(shí)現(xiàn)勻速運(yùn)動(dòng);
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var ball = new Ball();
var xspeed = 1;//定義每渲染1幀助被,圖形在x軸移動(dòng)的距離(移動(dòng)原點(diǎn))
(function drawFrame(){
window.requestAnimationFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
ball.x += xspeed;
ball.y = canvas.height/2;
if(ball.x>canvas.width+ball.radius){
ball.x = -ball.radius;
}
ball.draw(context);
})();
</script>
這段代碼涉及部分封裝的函數(shù)剖张,這里就不講,具體源碼可以參考【github】揩环。
這里主要講解一下思路搔弄,如果我們需要圓在x軸上移動(dòng),那么一個(gè)思路是改變圓的圓心丰滑,使圓心在x軸上不斷變化顾犹,最終形成動(dòng)畫的效果;
上面的ball.x = xpeed就是每執(zhí)行一次RAF(window.requestAnimationFrame)褒墨,圓心就向右移動(dòng)1像素炫刷;
同理可以實(shí)現(xiàn)在圓在y軸上的移動(dòng),甚至是x和y軸的同時(shí)移動(dòng)郁妈,這樣涉及向量的合成知識(shí)了浑玛。
現(xiàn)在大伙是不是深刻理解高中和大學(xué)時(shí)學(xué)的看似無(wú)用的解析幾何的妙用啦,自然界物體的運(yùn)動(dòng)規(guī)律不正是遵循著這些迷人的數(shù)學(xué)等式嗎噩咪?
好吧顾彰,扯遠(yuǎn)了,言歸正傳~~~
小結(jié)一下物體的勻速運(yùn)動(dòng):
1.物體的勻速運(yùn)動(dòng)無(wú)非是改變其在坐標(biāo)軸的值胃碾,但是**每次的改變量是不變的**涨享,也就是單位時(shí)間內(nèi)的移動(dòng)距離是不變的,這樣才符合勻速书在;
2.通過(guò)向量的合成原理灰伟,我們可以在canvas畫布上實(shí)現(xiàn)任意方向的勻速運(yùn)動(dòng)
-
變速運(yùn)動(dòng)
如同你的知識(shí)積淀一樣,當(dāng)你學(xué)的越多,運(yùn)用的越靈活栏账,你再去get一門新的技能的時(shí)候帖族,學(xué)習(xí)的速度就不在是勻速運(yùn)動(dòng),而是變速運(yùn)動(dòng)了挡爵。
變速運(yùn)動(dòng)竖般,本質(zhì)上是物體運(yùn)動(dòng)過(guò)程中速度在變化,也就是以前學(xué)過(guò)的加速度的概念茶鹃,也就是說(shuō)要想實(shí)現(xiàn)物體變速運(yùn)動(dòng)涣雕,只需要改變坐標(biāo)軸的值,每次的改變是變化的
<canvas id="canvas" width="500" height="500" style="background:#000"></canvas>
<script src='../js/utils.js'></script>
<script src='../js/ball.js'></script>
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var ball = new Ball();
var xspeed = 1;//定義每渲染1幀闭翩,圖形在x軸移動(dòng)的距離(移動(dòng)原點(diǎn))
var ax = 0.5;//設(shè)置x軸上的每渲染1幀xspeed增加0.05;
(function drawFrame(){
window.requestAnimationFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
ball.x += xspeed;
xspeed += ax;
ball.y = canvas.height/2;
//ball.y += 1;
if(ball.x>canvas.width+ball.radius){
ball.x = -ball.radius;
}
ball.draw(context);
})();
</script>
【nonlinear-motion】
看完上面的代碼有沒(méi)有感到很神奇挣郭,同樣一段代碼,只需要添加var vx = 0.5
和xspeed+=vx
就可以使物體實(shí)現(xiàn)加速運(yùn)動(dòng)疗韵;
看完demo后兑障,你有沒(méi)有發(fā)現(xiàn),當(dāng)速度達(dá)到一定程度的時(shí)候蕉汪,物體給人的感覺(jué)好像是靜止一樣流译;
【注】
這里給大伙講一個(gè)上面非線性運(yùn)動(dòng)日常例子,也就是籃球的自由落體運(yùn)動(dòng)者疤,先給大伙看看演示的效果:
中學(xué)物理都有學(xué)過(guò)福澡,物體在自由落體過(guò)程中受萬(wàn)有引力和空氣摩擦力的合力——重力的影響而向下運(yùn)動(dòng),球體在落地地面給了物體一個(gè)作用力導(dǎo)致物理受向上的力和萬(wàn)有引力的影響而向上運(yùn)動(dòng)驹马,循環(huán)反復(fù)革砸,由此出現(xiàn)上面的運(yùn)動(dòng)效果;
數(shù)理分析上糯累,物體先是做向下的加速運(yùn)動(dòng)业岁,落到地面后(有一個(gè)落到地面的速度)再做向上的減速運(yùn)動(dòng)知道速度為0時(shí),再做向下的加速運(yùn)動(dòng)寇蚊,循環(huán)反復(fù),知道小球落到地面棍好;
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var ball = new Ball(20,'white');
//設(shè)置小球初始降落的位置
ball.x = canvas.width/2;
ball.y = canvas.height/5;
var vy = 0;
var gravity = 0.05;//定義重力加速度仗岸;
var bounce = -0.8;//定義反彈系數(shù);
//碰撞測(cè)試
function checkGround(ball){
if(ball.y+ball.radius>canvas.height){
//小球碰到地面時(shí)借笙,讓球的位置暫時(shí)設(shè)置為在地面上
ball.y = canvas.height - ball.radius;
//此時(shí)設(shè)置小球落到地面時(shí)的速度為反向扒怖,大小為原來(lái)的0.8;
vy *= bounce;
}
}
(function drawFrame(){
window.requestAnimationFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
//小球首先做向下的加速運(yùn)動(dòng)
vy += gravity;
ball.y += vy;
//碰撞測(cè)試业稼,當(dāng)小球下落到地面時(shí)盗痒,vy *= bounce;
//此時(shí)小球在地面時(shí)的初始速度為vy *= bounce(vy此時(shí)是負(fù)值),接著繼續(xù)向上運(yùn)動(dòng),每渲染1幀,vy+=gravity俯邓,注意此時(shí)小球做向上的減速運(yùn)動(dòng)骡楼,直到速度為0時(shí);
//接著小球繼續(xù)做向下加速運(yùn)動(dòng)稽鞭,循環(huán)往復(fù)鸟整,直到小球停止;
checkGround(ball)
ball.draw(context);
})();
</script>
各位可以嘗試去修改gravity和bounce的參數(shù)朦蕴,你會(huì)有意想不到的結(jié)果發(fā)現(xiàn)篮条;
-
波形運(yùn)動(dòng)
波形運(yùn)動(dòng),顧名思義像波浪般的運(yùn)動(dòng)吩抓,運(yùn)動(dòng)軌跡如同下面的三角函數(shù)一般涉茧;
此時(shí),我們不禁會(huì)想:要實(shí)現(xiàn)波形運(yùn)動(dòng)的軌跡疹娶,是不是需要用到三角函數(shù)呢伴栓?
Bingo,答對(duì)了~~~可是知識(shí)已經(jīng)還給我敬愛(ài)的老師嘞蚓胸;
別緊張挣饥,其實(shí)實(shí)現(xiàn)這個(gè)軌跡so easy,聽(tīng)我娓娓道來(lái)······
先分析一下思路:
實(shí)現(xiàn)圓按照波形運(yùn)動(dòng)的動(dòng)畫原理是什么沛膳?
每16ms瀏覽器渲染1幀扔枫,每1幀圓的圓心位置發(fā)生改變,改變的路徑就是這個(gè)正弦函數(shù)锹安;
還不懂短荐?答案就是跟前面的代碼一模一樣,只不過(guò)x軸的變化值由
//勻速運(yùn)動(dòng)
ball.x = xspeed//xspeed = 1一直都是1叹哭;
//變速運(yùn)動(dòng)
var ax = 0.05;
ball.x += xspeed //xspeed = 1初始值為1忍宋;
xspeed += ax//每16ms,xspeed增加0.05风罩;
【注】
各位童鞋自己想一下曲線運(yùn)動(dòng)如何實(shí)現(xiàn)糠排?提示一下,結(jié)合勻速運(yùn)動(dòng)和變速運(yùn)動(dòng)一起思考超升;
[【curve-motion】](http://terenyeung.applinzi.com/newapp/canvas/html/curve-motion.html)
//波形運(yùn)動(dòng)
var angle = 0;//定義每次變化的角度
var swing = 100;//定義振幅入宦;
ball.x +=2;
ball.y = canvas.height/2 + Math.sin(angle)*swing;
angle += 0.1;
完整代碼如下:
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var ball = new Ball();
var angle = 0;
var swing = 100;
(function drawFrame(){
window.requestAnimationFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
ball.x += 2;
ball.y = canvas.height/2+Math.sin(angle)*swing;
//ball.y += 1;
angle += 0.1;
if(ball.x>canvas.width+ball.radius){
ball.x = -ball.radius;
}
ball.draw(context);
})();
</script>
細(xì)心的童鞋可能已經(jīng)發(fā)現(xiàn)這么一個(gè)規(guī)律:物體的運(yùn)動(dòng)軌跡無(wú)非就是通過(guò)改變物體在canvas坐標(biāo)軸上的值+RAF這個(gè)API而產(chǎn)生運(yùn)動(dòng)的;
勻速運(yùn)動(dòng)設(shè)置ball.x += 1室琢,每頻次圖形的x軸右移1px乾闰;
變速運(yùn)動(dòng)設(shè)置ball.x += vx, vx += ax,每頻次圖形x軸右移vx后盈滴,vx加ax涯肩,下一次圖形將移動(dòng)vx+ax從而實(shí)現(xiàn)變速;
波形運(yùn)動(dòng)則設(shè)置ball.y = centerY + Mathsin(angle)*swing,由于正弦函數(shù)的值區(qū)間為[-1,1]病苗,所以圖形會(huì)永遠(yuǎn)在[centerY-swing,centerY+swing]上下移動(dòng)疗垛;
這一種思想將會(huì)對(duì)后面的圖形運(yùn)動(dòng)的思考同樣奏效;
-
圓形運(yùn)動(dòng)
現(xiàn)在我們?cè)傧胍幌虑ο纾绾巫寛A圍繞一個(gè)點(diǎn)做圓周運(yùn)動(dòng)继谚?
我們學(xué)到的解析幾何有什么是可以表示圓的?相信各位童鞋已經(jīng)學(xué)會(huì)開(kāi)始搶答了阵幸,對(duì)啦就是
x*x+y*y = r*r//這是一原點(diǎn)為圓心花履,半徑為r的圓;
或許有童鞋會(huì)問(wèn)候我尼瑪挚赊,你剛才不是告訴我實(shí)現(xiàn)物體運(yùn)動(dòng)诡壁,只要按照RAF改變物體坐標(biāo)軸的值就行了嗎,你給我上面這么一個(gè)等式荠割,那我怎么樣去給ball.x和ball.y賦值妹卿;
人類一思考,上帝就發(fā)笑蔑鹦,這是小羊?qū)戇@篇文章時(shí)新鮮看到的一句話夺克,我一開(kāi)始的理解為嘲諷人類的自作聰明,后來(lái)想一下我更加愿意理解為上帝是在對(duì)人類不斷追求真理這一行為的勉勵(lì)把嚎朽;
如果有看官想到這一層面铺纽,我會(huì)覺(jué)得你很牛X锦爵,因?yàn)槲沂鞘潞髲?fù)習(xí)才想到這一點(diǎn)的电湘。不賣關(guān)子,大家應(yīng)該聽(tīng)說(shuō)過(guò)極坐標(biāo)把(再一次驗(yàn)證原理的有效性)
//圓的極坐標(biāo)表達(dá)式為
x = rcosθ
y = rsinθ
也就是說(shuō)給我一個(gè)圓的半徑和每次旋轉(zhuǎn)的角度搀捷,我就可以用x和y的方式描繪圓的路徑
二話不說(shuō)上代碼:
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var ball = new Ball();
var angle = 0.1;
var scope = 100;
(function drawFrame(){
window.requestAnimationFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
ball.x = canvas.width/2+Math.cos(angle)*scope;
ball.y = canvas.height/2+Math.sin(angle)*scope;
//ball.y += 1;
angle += 0.1;
// if(ball.x>canvas.width+ball.radius){
// ball.x = -ball.radius;
// }
ball.draw(context);
})();
</script>
有了圓形運(yùn)動(dòng)锅很,再講一下橢圓運(yùn)動(dòng)其馏,思考過(guò)程和上面基本一樣,數(shù)學(xué)表達(dá)式為:
(x/a)*(x/a)+(y/b)*(y/b)=1
//極坐標(biāo)
x = a*cosθ
y = b*sinθ
有了這兩個(gè)坐標(biāo)爆安,圖形的橢圓路徑還不出來(lái)嗎叛复,相信你已經(jīng)躍躍欲試了,我這里就直接給demo啦扔仓。
其實(shí)致扯,圓形運(yùn)動(dòng)本質(zhì)上就是特殊的橢圓運(yùn)動(dòng),各位可以看一下二者的聯(lián)系與區(qū)別:
//圓形運(yùn)動(dòng)
var angle = 0,scope = 100;
x = canvas.width/2 + scope*Math.cos(angle)
y = canvas.height/2 + scope*Math.sin(angle)
angle += 0.1;
//橢圓運(yùn)動(dòng)
var angle = 0,scopeX = 150 , scopeY = 80;
x = canvas.width/2 + scopeX*Math.cos(angle)
y = canvas.height/2 + scopeY*Math.sin(angle)
angle += 0.1;
轉(zhuǎn)動(dòng)的方向
動(dòng)畫特效當(dāng)中其中有一個(gè)很重要的點(diǎn)就是物體的轉(zhuǎn)動(dòng)方向問(wèn)題当辐,以自然界的實(shí)例來(lái)看,你會(huì)看到地球自轉(zhuǎn)及其圍繞太陽(yáng)公轉(zhuǎn)鲤看;
這里先給上一段實(shí)現(xiàn)封裝好的Arrow類缘揪,用于后面的講解所用;
//arrow.js
function Arrow(){
this.x = 0;
this.y = 0;
this.rotation = 0;
this.color = '#ff0';
};
Arrow.prototype.draw = function(context){
context.save();
context.translate(this.x,this.y);
context.rotate(this.rotation);
context.lineWidth = 5;
context.beginPath();
context.moveTo(-50,-25);
context.lineTo(0,-25);
context.lineTo(0,-50);
context.lineTo(50,0);
context.lineTo(0,50);
context.lineTo(0,25);
context.lineTo(-50,25);
context.closePath();
context.stroke();
context.fill();
context.restore();
};
小羊在轉(zhuǎn)動(dòng)的方向這一部分要使用一個(gè)canvas的新API——context.rotate(angle)來(lái)控制物體的轉(zhuǎn)動(dòng);
到現(xiàn)在只要你掌握前面所講的動(dòng)畫原理的話找筝,那么就不難推理出自轉(zhuǎn)和公轉(zhuǎn)的動(dòng)畫來(lái)蹈垢;
自轉(zhuǎn):每16ms變化一次angle,那么angle作為參數(shù)每傳遞1次袖裕,物體就會(huì)轉(zhuǎn)動(dòng)1次曹抬,最終形成自轉(zhuǎn)
window.onload = function(){
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var arrow = new Arrow();
var angle = 0;
arrow.x = canvas.width/2;
arrow.y = canvas.height/2;
(function drawFrame(){
window.requestAnimationFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
arrow.rotation = angle;
angle +=0.1;
arrow.draw(context);
})();
}
公轉(zhuǎn):使用圓周運(yùn)動(dòng)的方法實(shí)現(xiàn)公轉(zhuǎn);
【resolution】
上面的angle的賦值是機(jī)械式急鳄,如果我們想要鼠標(biāo)轉(zhuǎn)到哪里谤民,箭頭就指到哪里,會(huì)不會(huì)更加具有交互性疾宏;
物體轉(zhuǎn)動(dòng)的角度和鼠標(biāo)的指向有關(guān)张足,那么如何建立二者之間的聯(lián)系呢?
上圖給出了答案:先是獲取到鼠標(biāo)在canvas上的坐標(biāo)坎藐,然后獲取到物體中心的坐標(biāo)为牍,根據(jù)二點(diǎn)間的距離公式,可以測(cè)算出鼠標(biāo)距離中心點(diǎn)在x軸和y軸的分量dx和dy岩馍,然后通過(guò)一個(gè)很牛掰的三角函數(shù)碉咆,
object.rotation = Math.atan2(dy,dx);
這個(gè)三角函數(shù)作用是給它兩個(gè)x和y軸的距離分量,就可以測(cè)算出鼠標(biāo)與x軸的夾角來(lái)蛀恩;
有同學(xué)會(huì)問(wèn):?jiǎn)柺裁纯梢赃@樣疫铜?這個(gè)暫時(shí)無(wú)法回答,這個(gè)問(wèn)題深究下去就不屬于本筆記范圍之內(nèi)了赦肋,知道有這么一個(gè)方法就okay啦块攒;
測(cè)算出角度,就可以給context.rotation(angle)傳參啦佃乘,此時(shí)箭頭將會(huì)跟著鼠標(biāo)轉(zhuǎn)動(dòng)囱井;
window.onload = function(){
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var mouse = utils.captureMouse(canvas);
var arrow = new Arrow();
var angle = 0;
arrow.x = canvas.width/2;
arrow.y = canvas.height/2;
(function drawFrame(){
window.requestAnimationFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
var dx = mouse.x - arrow.x,
dy = mouse.y - arrow.y;
angle = Math.atan2(dy,dx);
arrow.rotation = angle;
arrow.draw(context);
})();
}
okay,現(xiàn)在有了轉(zhuǎn)動(dòng)趣避,再加入先前的物體運(yùn)動(dòng)就可以讓"讓子彈飛"啦~~~
【cursor-follow】
這個(gè)demo是一個(gè)小飛機(jī)庞呕,你按下啥鍵它就會(huì)飛向哪,真正實(shí)現(xiàn)和用戶交互程帕;
【spaceShip】
【注】
Math.atan2(dy,dx)函數(shù)很重要W×贰!愁拭!讲逛,這意味著這要你能夠測(cè)算出鼠標(biāo)與指定點(diǎn)之間的x軸和y軸的分量,那么你就可以獲取到鼠標(biāo)與指定點(diǎn)的連線與x軸所形成的的夾角岭埠,由此就可以去改變物體的運(yùn)動(dòng)或是轉(zhuǎn)向盏混;
縮放的比例
canvas提供縮放功能的API可以讓我們對(duì)物體進(jìn)行縮放大小蔚鸥,如果結(jié)合我們之前學(xué)的一些解析幾何的知識(shí),那么就可以創(chuàng)作出千變?nèi)f化的縮放特效出來(lái)许赃;
<script>
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var ball = new Ball();
ball.x = canvas.width/2;
ball.y = canvas.height/2;
var angle = 0,
centerScale = 1;
swing = 0.5;
(function drawFrame(){
window.requestAnimationFrame(drawFrame,canvas);
context.clearRect(0,0,canvas.width,canvas.height);
angle += 0.05
//plusing-effect
ball.scaleX = ball.scaleY = centerScale + Math.sin(angle)*swing;
//bigger and bigger effect
//ball.scaleX = ball.scaleY = centerScale + angle
ball.draw(context);
})();
</script>
總結(jié)
本篇文章題目是《基于Canvas的動(dòng)畫基本原理與數(shù)理分析》止喷,因此只介紹了一些在使用canvas元素繪制動(dòng)畫時(shí)運(yùn)用到的一些常用的解析幾何原理和相關(guān)的物理知識(shí),例如勻速運(yùn)動(dòng)混聊、變速運(yùn)動(dòng)弹谁、圓周運(yùn)動(dòng)、波形運(yùn)動(dòng)句喜、脈沖運(yùn)動(dòng)预愤,這些運(yùn)動(dòng)過(guò)程中可涉及到的概念又包括向量的分解(力的分解)、重力藤滥、摩擦力鳖粟、加速度、三角函數(shù)等等······
物體的屬性——在canvas上的位置拙绊、方向和比例的變化萬(wàn)千無(wú)非是使用上述的數(shù)學(xué)等式通過(guò)RAF從而形成動(dòng)畫效果的向图;
當(dāng)然,僅僅掌握這些并不足以讓你設(shè)計(jì)出相當(dāng)出彩的動(dòng)畫特效标沪,但是就像開(kāi)頭所說(shuō)的這部分的知識(shí)點(diǎn)相當(dāng)于是動(dòng)畫的基本原理榄攀,它相對(duì)于直接使用CSS3做動(dòng)畫來(lái)的艱深復(fù)雜些,短期可能會(huì)花我們一些時(shí)間去理解金句,但是往后保不準(zhǔn)會(huì)排上用場(chǎng)檩赢,反正我在學(xué)習(xí)的過(guò)程中就充滿力量感——因?yàn)槟岈敻咧袑W(xué)的物理和大學(xué)學(xué)的高數(shù)終于排上用場(chǎng)啦!Nツ贞瞒!
canvas動(dòng)畫的知識(shí)點(diǎn)還包括一些具體的應(yīng)用和晉升到3D的層次,這一部分的內(nèi)容就留給童鞋們自行解決趁曼,冥冥之中我有種預(yù)感军浆,你只要把上面的掌握住了,高級(jí)的就不成問(wèn)題了挡闰;
寫完這篇文章乒融,感覺(jué)自己要吐血三升!I忝酢赞季!具體是多少字?jǐn)?shù),自己也沒(méi)數(shù)奢驯,可能會(huì)比較長(zhǎng)申钩,如果有看官耐住性子看到這里,我可能會(huì)很佩服你喲······
最后瘪阁,要感謝周余飛童鞋的技術(shù)博客的指導(dǎo)典蜕,本篇文章是學(xué)習(xí)周余飛同學(xué)【每周一點(diǎn)canvas動(dòng)畫系列】的學(xué)習(xí)心得和思考断盛,沒(méi)有作者的辛勤付出,也就沒(méi)有我的知識(shí)汲取后的再度分享愉舔,為此我在下面給出作者于【github】的地址,有興趣的同學(xué)伙菜,可進(jìn)一步學(xué)習(xí)和探討轩缤,THX。