摘要:注以下所有代碼托管到動畫的數(shù)理分析有了前面的基礎(chǔ)知識,現(xiàn)在我們就會想如果我們能夠在每秒幀,內(nèi)渲染張圖像,并且每一張圖像的內(nèi)容發(fā)生微調(diào),那么在秒鐘整個畫面就會產(chǎn)生動畫效果了。
什么是動畫?
就像思考哲學(xué)問題無法回避思維和存在的關(guān)系一樣,制作動畫同樣無法逃避的問題是動畫的原理是什么?這里提一句題外話,任何原理的東西通常難以讓你短期拾掇成果,但在隱約的未來會起到難以置信的效果,不信就看接下來小羊的一些學(xué)習(xí)成果分享。
動畫本質(zhì)上是圖像按照事先設(shè)定好的順序在一定的時間內(nèi)的圖像序列變化運(yùn)動。這種圖像序列的變化運(yùn)動給我們最為直觀的感受就是圖像仿佛真實(shí)的在運(yùn)動一般,由此產(chǎn)生動畫效果。
然后,事實(shí)并非如此,真相往往難以用肉眼觀察得到,除非你是上帝~~~
動畫的特性在于:
每一張圖像的內(nèi)容是事先設(shè)定好的,內(nèi)容是不變的,變化的是圖像序列按照規(guī)定的順序在變動;
構(gòu)成動畫特效需要在單位時間內(nèi)渲染一定量的圖像,每張圖像稱之為幀(Frame),通常電影只需要24FPS就足夠流暢,而游戲則需要60FPS,我們設(shè)計動畫時通常選用60FPS;
總之,你所看到的動畫無非是你的眼睛在欺騙你的大腦,本質(zhì)上每一張圖像還是那張圖像,只不過它們在單位時間內(nèi)按照一定順序在快速移動罷了~~~
Canvas API的簡介這一部分為了兼顧之前未接觸canvas元素的看官以及重溫canvas的目的,幫助小羊和各位快速過一遍canvas的API。
//demo.html [注] 設(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();//開始路徑 context.moveTo();//定義路徑起始點(diǎn) context.lineTo();//路徑的去向 context.closePath();//畫完后,關(guān)閉路徑 context.fill() || context.stroke();//最后畫出由路徑構(gòu)成的圖形 [注] 本質(zhì)上,所有的多邊形都可以由路徑畫出; context.save();//保存save以上context對象設(shè)置的屬性值 context.restore();//恢復(fù)到先前保存在context對象上的所有屬性值
這里在介紹一下實(shí)現(xiàn)動畫效果的非常重要的API:
window.requestAnimationFrame(callback)
先前我已經(jīng)說過,動畫是在單位時間內(nèi)按照一定順序的圖像序列的變化形成的;這個API的功能就是,你可以在回調(diào)函數(shù)里面寫一個腳本改變圖形的寬高,然后這一API就會根據(jù)瀏覽器的刷新頻率而在一定時間內(nèi)調(diào)用callback;然后,根據(jù)遞歸的思想,實(shí)現(xiàn)callback的反復(fù)調(diào)用,最終實(shí)現(xiàn)動畫效果。
不明白,上代碼
(function drawFrame(){ window.requestAnimationFrame(drawFrame); //some code for animation effect here })();
上面的代碼意思是立即執(zhí)行drawFrame這個函數(shù),發(fā)現(xiàn) window.requestAnimationFrame(drawFrame),okay根據(jù)瀏覽器的刷新頻率,在一定時間之后執(zhí)行;接下來執(zhí)行你所編寫的改變圖像內(nèi)容(圖像的位置、寬高、顏色等等)的腳本,執(zhí)行回調(diào);循環(huán)反復(fù),形成動畫效果
由此也可知道:
window.requestAnimationFrame這個API你可以理解為window.setTimeout(callback,time)
事實(shí)上,當(dāng)部分瀏覽器不兼容這個API時,我們也可以寫成以下形式:
if(!window.requestAnimationFrame){ window.requestAnimationFrame = ( window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRquestAniamtionFrame || window.oRequestAnimationFrame || function (callback){ return setTimeout(callback,Math.floor(1000/60)) } ) }
Okay,有了這么幾個基本的canvasAPI就足以應(yīng)對接下來的知識點(diǎn)了,如有不懂或深入了解,詳見Canvas教程-MDN。
【注】以下所有代碼托管到【github】
動畫的數(shù)理分析有了前面的基礎(chǔ)知識,現(xiàn)在我們就會想:如果我們能夠在每16ms(1秒60幀,1000/60)內(nèi)渲染1張圖像,并且每一張圖像的內(nèi)容發(fā)生微調(diào),那么在1秒鐘整個畫面就會產(chǎn)生動畫效果了。
內(nèi)容的微調(diào)可以是圖形的移動的距離、轉(zhuǎn)動的方向以及縮放的比例等等,而“捕獲”這些數(shù)據(jù)的方法就用使用到我們以前忽視的解析幾何的知識了。
移動的距離1. 線性運(yùn)動
線性運(yùn)動就是物體朝特定方向的運(yùn)動,運(yùn)動過程中速度不發(fā)生改變;
這段代碼涉及部分封裝的函數(shù),這里就不講,具體源碼可以參考【github】。
這里主要講解一下思路,如果我們需要圓在x軸上移動,那么一個思路是改變圓的圓心,使圓心在x軸上不斷變化,最終形成動畫的效果;上面的ball.x = xpeed就是每執(zhí)行一次RAF(window.requestAnimationFrame),圓心就向右移動1像素;同理可以實(shí)現(xiàn)在圓在y軸上的移動,甚至是x和y軸的同時移動,這樣涉及向量的合成知識了。
現(xiàn)在大伙是不是深刻理解高中和大學(xué)時學(xué)的看似無用的解析幾何的妙用啦,自然界物體的運(yùn)動規(guī)律不正是遵循著這些迷人的數(shù)學(xué)等式嗎?
好吧,扯遠(yuǎn)了,言歸正傳~~~小結(jié)一下物體的勻速運(yùn)動:
物體的勻速運(yùn)動無非是改變其在坐標(biāo)軸的值,但是每次的改變量是不變的,也就是單位時間內(nèi)的移動距離是不變的,這樣才符合勻速;
通過向量的合成原理,我們可以在canvas畫布上實(shí)現(xiàn)任意方向的勻速運(yùn)動
【linear-motion】
2. 變速運(yùn)動
如同你的知識積淀一樣,當(dāng)你學(xué)的越多,運(yùn)用的越靈活,你再去get一門新的技能的時候,學(xué)習(xí)的速度就不在是勻速運(yùn)動,而是變速運(yùn)動了。
變速運(yùn)動,本質(zhì)上是物體運(yùn)動過程中速度在變化,也就是以前學(xué)過的加速度的概念,也就是說要想實(shí)現(xiàn)物體變速運(yùn)動,只需要改變坐標(biāo)軸的值,每次的改變是變化的
【nonlinear-motion】
看完上面的代碼有沒有感到很神奇,同樣一段代碼,只需要添加var vx = 0.5和xspeed+=vx就可以使物體實(shí)現(xiàn)加速運(yùn)動;看完demo后,你有沒有發(fā)現(xiàn),當(dāng)速度達(dá)到一定程度的時候,物體給人的感覺好像是靜止一樣;
【注】
這里給大伙講一個上面非線性運(yùn)動日常例子,也就是籃球的自由落體運(yùn)動,先給大伙看看演示的效果:
中學(xué)物理都有學(xué)過,物體在自由落體過程中受萬有引力和空氣摩擦力的合力——重力的影響而向下運(yùn)動,球體在落地地面給了物體一個作用力導(dǎo)致物理受向上的力和萬有引力的影響而向上運(yùn)動,循環(huán)反復(fù),由此出現(xiàn)上面的運(yùn)動效果;
數(shù)理分析上,物體先是做向下的加速運(yùn)動,落到地面后(有一個落到地面的速度)再做向上的減速運(yùn)動知道速度為0時,再做向下的加速運(yùn)動,循環(huán)反復(fù),知道小球落到地面;
【gravity-acceleration】
各位可以嘗試去修改gravity和bounce的參數(shù),你會有意想不到的結(jié)果發(fā)現(xiàn);
3. 波形運(yùn)動
波形運(yùn)動,顧名思義像波浪般的運(yùn)動,運(yùn)動軌跡如同下面的三角函數(shù)一般;
此時,我們不禁會想:要實(shí)現(xiàn)波形運(yùn)動的軌跡,是不是需要用到三角函數(shù)呢?
Bingo,答對了~~~可是知識已經(jīng)還給我敬愛的老師嘞;
別緊張,其實(shí)實(shí)現(xiàn)這個軌跡so easy,聽我娓娓道來······
先分析一下思路:
實(shí)現(xiàn)圓按照波形運(yùn)動的動畫原理是什么?
每16ms瀏覽器渲染1幀,每1幀圓的圓心位置發(fā)生改變,改變的路徑就是這個正弦函數(shù);
還不懂?答案就是跟前面的代碼一模一樣,只不過x軸的變化值由
//勻速運(yùn)動 ball.x = xspeed//xspeed = 1一直都是1; //變速運(yùn)動 var ax = 0.05; ball.x += xspeed //xspeed = 1初始值為1; xspeed += ax//每16ms,xspeed增加0.05; 【注】 各位童鞋自己想一下曲線運(yùn)動如何實(shí)現(xiàn)?提示一下,結(jié)合勻速運(yùn)動和變速運(yùn)動一起思考; [【curve-motion】](http://terenyeung.applinzi.com/newapp/canvas/html/curve-motion.html) //波形運(yùn)動 var angle = 0;//定義每次變化的角度 var swing = 100;//定義振幅; ball.x +=2; ball.y = canvas.height/2 + Math.sin(angle)*swing; angle += 0.1;
完整代碼如下:
【waving-motion】
細(xì)心的童鞋可能已經(jīng)發(fā)現(xiàn)這么一個規(guī)律:物體的運(yùn)動軌跡無非就是通過改變物體在canvas坐標(biāo)軸上的值+RAF這個API而產(chǎn)生運(yùn)動的;
勻速運(yùn)動設(shè)置ball.x += 1,每頻次圖形的x軸右移1px;
變速運(yùn)動設(shè)置ball.x += vx, vx += ax,每頻次圖形x軸右移vx后,vx加ax,下一次圖形將移動vx+ax從而實(shí)現(xiàn)變速;
波形運(yùn)動則設(shè)置ball.y = centerY + Mathsin(angle)*swing,由于正弦函數(shù)的值區(qū)間為[-1,1],所以圖形會永遠(yuǎn)在[centerY-swing,centerY+swing]上下移動;
這一種思想將會對后面的圖形運(yùn)動的思考同樣奏效;
4. 圓形運(yùn)動
現(xiàn)在我們再想一下,如何讓圓圍繞一個點(diǎn)做圓周運(yùn)動?
我們學(xué)到的解析幾何有什么是可以表示圓的?相信各位童鞋已經(jīng)學(xué)會開始搶答了,對啦就是
x*x+y*y = r*r//這是一原點(diǎn)為圓心,半徑為r的圓;
或許有童鞋會問候我尼瑪,你剛才不是告訴我實(shí)現(xiàn)物體運(yùn)動,只要按照RAF改變物體坐標(biāo)軸的值就行了嗎,你給我上面這么一個等式,那我怎么樣去給ball.x和ball.y賦值;
人類一思考,上帝就發(fā)笑,這是小羊?qū)戇@篇文章時新鮮看到的一句話,我一開始的理解為嘲諷人類的自作聰明,后來想一下我更加愿意理解為上帝是在對人類不斷追求真理這一行為的勉勵把;
如果有看官想到這一層面,我會覺得你很牛X,因?yàn)槲沂鞘潞髲?fù)習(xí)才想到這一點(diǎn)的。不賣關(guān)子,大家應(yīng)該聽說過極坐標(biāo)把(再一次驗(yàn)證原理的有效性)
//圓的極坐標(biāo)表達(dá)式為 x = rcosθ y = rsinθ 也就是說給我一個圓的半徑和每次旋轉(zhuǎn)的角度,我就可以用x和y的方式描繪圓的路徑
二話不說上代碼:
【circular-motion】
有了圓形運(yùn)動,再講一下橢圓運(yùn)動,思考過程和上面基本一樣,數(shù)學(xué)表達(dá)式為:
(x/a)*(x/a)+(y/b)*(y/b)=1 //極坐標(biāo) x = a*cosθ y = b*sinθ
有了這兩個坐標(biāo),圖形的橢圓路徑還不出來嗎,相信你已經(jīng)躍躍欲試了,我這里就直接給demo啦。
【ellipse-motion】
其實(shí),圓形運(yùn)動本質(zhì)上就是特殊的橢圓運(yùn)動,各位可以看一下二者的聯(lián)系與區(qū)別:
//圓形運(yùn)動 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)動 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)中其中有一個很重要的點(diǎn)就是物體的轉(zhuǎn)動方向問題,以自然界的實(shí)例來看,你會看到地球自轉(zhuǎn)及其圍繞太陽公轉(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)動的方向這一部分要使用一個canvas的新API——context.rotate(angle)來控制物體的轉(zhuǎn)動;
到現(xiàn)在只要你掌握前面所講的動畫原理的話,那么就不難推理出自轉(zhuǎn)和公轉(zhuǎn)的動畫來;
自轉(zhuǎn):每16ms變化一次angle,那么angle作為參數(shù)每傳遞1次,物體就會轉(zhuǎn)動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); })(); }
【self-rotation】
公轉(zhuǎn):使用圓周運(yùn)動的方法實(shí)現(xiàn)公轉(zhuǎn);
【resolution】
上面的angle的賦值是機(jī)械式,如果我們想要鼠標(biāo)轉(zhuǎn)到哪里,箭頭就指到哪里,會不會更加具有交互性;
物體轉(zhuǎn)動的角度和鼠標(biāo)的指向有關(guān),那么如何建立二者之間的聯(lián)系呢?
上圖給出了答案:先是獲取到鼠標(biāo)在canvas上的坐標(biāo),然后獲取到物體中心的坐標(biāo),根據(jù)二點(diǎn)間的距離公式,可以測算出鼠標(biāo)距離中心點(diǎn)在x軸和y軸的分量dx和dy,然后通過一個很牛掰的三角函數(shù),
object.rotation = Math.atan2(dy,dx);
這個三角函數(shù)作用是給它兩個x和y軸的距離分量,就可以測算出鼠標(biāo)與x軸的夾角來;
有同學(xué)會問:問什么可以這樣?這個暫時無法回答,這個問題深究下去就不屬于本筆記范圍之內(nèi)了,知道有這么一個方法就okay啦;
測算出角度,就可以給context.rotation(angle)傳參啦,此時箭頭將會跟著鼠標(biāo)轉(zhuǎn)動;
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); })(); }
【rotation-to-cursor】
okay,現(xiàn)在有了轉(zhuǎn)動,再加入先前的物體運(yùn)動就可以讓"讓子彈飛"啦~~~
【cursor-follow】
這個demo是一個小飛機(jī),你按下啥鍵它就會飛向哪,真正實(shí)現(xiàn)和用戶交互;
【spaceShip】
【注】Math.atan2(dy,dx)函數(shù)很重要?。?!,這意味著這要你能夠測算出鼠標(biāo)與指定點(diǎn)之間的x軸和y軸的分量,那么你就可以獲取到鼠標(biāo)與指定點(diǎn)的連線與x軸所形成的的夾角,由此就可以去改變物體的運(yùn)動或是轉(zhuǎn)向;
縮放的比例canvas提供縮放功能的API可以讓我們對物體進(jìn)行縮放大小,如果結(jié)合我們之前學(xué)的一些解析幾何的知識,那么就可以創(chuàng)作出千變?nèi)f化的縮放特效出來;
【plusing-motion】
總結(jié)本篇文章題目是《基于Canvas的動畫基本原理與數(shù)理分析》,因此只介紹了一些在使用canvas元素繪制動畫時運(yùn)用到的一些常用的解析幾何原理和相關(guān)的物理知識,例如勻速運(yùn)動、變速運(yùn)動、圓周運(yùn)動、波形運(yùn)動、脈沖運(yùn)動,這些運(yùn)動過程中可涉及到的概念又包括向量的分解(力的分解)、重力、摩擦力、加速度、三角函數(shù)等等······
物體的屬性——在canvas上的位置、方向和比例的變化萬千無非是使用上述的數(shù)學(xué)等式通過RAF從而形成動畫效果的;
當(dāng)然,僅僅掌握這些并不足以讓你設(shè)計出相當(dāng)出彩的動畫特效,但是就像開頭所說的這部分的知識點(diǎn)相當(dāng)于是動畫的基本原理,它相對于直接使用CSS3做動畫來的艱深復(fù)雜些,短期可能會花我們一些時間去理解,但是往后保不準(zhǔn)會排上用場,反正我在學(xué)習(xí)的過程中就充滿力量感——因?yàn)槟岈敻咧袑W(xué)的物理和大學(xué)學(xué)的高數(shù)終于排上用場啦?。?!
canvas動畫的知識點(diǎn)還包括一些具體的應(yīng)用和晉升到3D的層次,這一部分的內(nèi)容就留給童鞋們自行解決,冥冥之中我有種預(yù)感,你只要把上面的掌握住了,高級的就不成問題了;
寫完這篇文章,感覺自己要吐血三升!?。【唧w是多少字?jǐn)?shù),自己也沒數(shù),可能會比較長,如果有看官耐住性子看到這里,我可能會很佩服你喲······
最后,要感謝周余飛童鞋的技術(shù)博客的指導(dǎo),本篇文章是學(xué)習(xí)周余飛同學(xué)【每周一點(diǎn)canvas動畫系列】的學(xué)習(xí)心得和思考,沒有作者的辛勤付出,也就沒有我的知識汲取后的再度分享,為此我在下面給出作者于【github】的地址,有興趣的同學(xué),可進(jìn)一步學(xué)習(xí)和探討,THX。
參考資料
【周余飛——每周一點(diǎn)canvas動畫系列】
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/91374.html
摘要:目錄導(dǎo)語對象對象對象方法的應(yīng)用日歷插件小結(jié)導(dǎo)語這是標(biāo)準(zhǔn)庫系列的第二篇文章,主要討論一下對象和對象,對象在平時處理一些數(shù)學(xué)操作時能起到事半功倍的作用,目前小羊接觸到的對象的使用場景是動畫制作對象作為關(guān)于時間的接口,能夠?qū)⑵鋺?yīng)用于制作和時間相關(guān) 目錄 導(dǎo)語 1. Math對象 2. Date對象 3. Date對象方法的應(yīng)用——日歷插件; 4. 小結(jié) 導(dǎo)語 這是《JavaScript標(biāo)準(zhǔn)...
摘要:寫在最前本次分享一下在作者上一次失利即拿到畢業(yè)證第二天突然收到阿里社招面試通知失敗之后,通過分析自己的定位與實(shí)際情況,做出的未來一到兩年的規(guī)劃。在博客有一定曝光度的積累中,陸續(xù)收到了一些面試邀請,基本上是阿里的但是我知道我菜。。 寫在最前 本次分享一下在作者上一次失利即拿到畢業(yè)證第二天突然收到阿里社招面試通知失敗之后,通過分析自己的定位與實(shí)際情況,做出的未來一到兩年的規(guī)劃。以及本次社招...
閱讀 3328·2021-11-08 13:12
閱讀 2770·2021-10-15 09:41
閱讀 1461·2021-10-08 10:05
閱讀 3309·2021-10-08 10:04
閱讀 2119·2021-09-29 09:34
閱讀 2497·2019-08-30 15:55
閱讀 2989·2019-08-30 15:45
閱讀 2594·2019-08-29 14:17