基于Canvas的動(dòng)畫基本原理與數(shù)理分析

什么是動(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ā)生改變;
linear-motion
<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í)了浑玛。

圖片來(lái)源:周余飛

現(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)

【linear-motion】


  • 變速運(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)軸的值,每次的改變是變化的

nonlinear-motion
<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.5xspeed+=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)者疤,先給大伙看看演示的效果:

gravity-acceleration

中學(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-acceleration】

各位可以嘗試去修改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)的老師嘞蚓胸;
圖片來(lá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ù)锹安;

waving-motion

還不懂短荐?答案就是跟前面的代碼一模一樣,只不過(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>

【waving-motion】

細(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ì)把嚎朽;

circular-motion

如果有看官想到這一層面铺纽,我會(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>

【circular-motion】

有了圓形運(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啦扔仓。

ellipse-motion

【ellipse-motion】

其實(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)鲤看;

self-rotation
resolution

這里先給上一段實(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);
        })();

  }

【self-rotation】

公轉(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)系呢?

圖片來(lái)源:周余飛

上圖給出了答案:先是獲取到鼠標(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);
        })();

  }

【rotation-to-cursor】

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)许赃;

plusing-motion
   <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>

【plusing-motion】


總結(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。

參考資料
【周余飛——每周一點(diǎn)canvas動(dòng)畫系列】

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贩绕,一起剝皮案震驚了整個(gè)濱河市火的,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淑倾,老刑警劉巖馏鹤,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異娇哆,居然都是意外死亡湃累,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門碍讨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)治力,“玉大人,你說(shuō)我怎么就攤上這事勃黍∠常” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵覆获,是天一觀的道長(zhǎng)马澈。 經(jīng)常有香客問(wèn)我,道長(zhǎng)弄息,這世上最難降的妖魔是什么痊班? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮疑枯,結(jié)果婚禮上辩块,老公的妹妹穿的比我還像新娘。我一直安慰自己荆永,他們只是感情好废亭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著具钥,像睡著了一般豆村。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上骂删,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天掌动,我揣著相機(jī)與錄音四啰,去河邊找鬼。 笑死粗恢,一個(gè)胖子當(dāng)著我的面吹牛柑晒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播眷射,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼匙赞,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了妖碉?” 一聲冷哼從身側(cè)響起涌庭,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎欧宜,沒(méi)想到半個(gè)月后坐榆,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冗茸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年席镀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚀狰。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡愉昆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出麻蹋,到底是詐尸還是另有隱情跛溉,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布扮授,位于F島的核電站芳室,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏刹勃。R本人自食惡果不足惜堪侯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望荔仁。 院中可真熱鬧伍宦,春花似錦、人聲如沸乏梁。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)遇骑。三九已至卖毁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間落萎,已是汗流浹背亥啦。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工炭剪, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翔脱。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓奴拦,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親届吁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粱坤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容

  • 看了很多視頻、文章瓷产,最后卻通通忘記了,別人的知識(shí)依舊是別人的枚驻,自己卻什么都沒(méi)獲得濒旦。此系列文章旨在加深自己的印象,因...
    DCbryant閱讀 716評(píng)論 0 2
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)再登、插件尔邓、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,033評(píng)論 4 62
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜锉矢,今天將帶大家一窺ios動(dòng)畫全貌梯嗽。在這里你可以看...
    每天刷兩次牙閱讀 8,465評(píng)論 6 30
  • 日更第15篇堅(jiān)果姐姐在345會(huì)上分享了結(jié)構(gòu)化寫作之美,她上臺(tái)的第一句話就是沽损,我是為兔子而來(lái)的灯节,為兔子來(lái)做這個(gè)分享。...
    羽青閱讀 974評(píng)論 1 13
  • 感覺(jué)是一個(gè)新的開(kāi)始形入,有了許多輸入,第一次有輸出缝左,期待和緊張亿遂,還有小小的盼望,好想以后每天都跑步啊渺杉,變瘦一點(diǎn)蛇数,變成...
    aec75d286fa4閱讀 232評(píng)論 0 1