Canvas實現直線與圓形的物理運動效果

一、爲什麼要寫這篇文章

某年某月某時某種原因,我在慕課網上看到了一個大神實現了關於小球的拋物線運動的代碼,心中很是欣喜,故而寫這篇文章來向這位大神致敬,同時也爲了彌補自己在運動效果和動畫效果製作方面的不足

二、幾種簡單的直線運動

這一部分主要講解的是簡單的運動效果的實現原理,其實所有的canvas動畫效果的實現在覈心思想是一致的:都是先定義個初始的狀態,然後定義一個定時器,定時器內執行一個方法,記得在這個方法中要對當前的畫面清除,然後在這個方法中重新繪製需要變化的效果,由於人眼存在殘影,所以短時間內的中斷的變化可以看成是連續的變化,這個就是canva動畫運動原理

最簡單的要從勻速直線運動說起,然後是勻加速直線運動。

勻速直線運動

html代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas勻速直線運動</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <canvas id="canvas">你的瀏覽器不支持canvas,請跟換其他瀏覽器試一試</canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

JS代碼

window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=728;
    canvas.width=1024;
    var context=canvas.getContext('2d');
    context.fillStyle='red';
    context.beginPath();
    context.arc(800,300,30,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    setInterval(function(){
        run(context);
    }, 50);
};
var speed=0;
var startPoint=800;
function run(cxt){
    speed=-7;
    cxt.clearRect(0,0,1024,728);
    //cxt.top+=speed;    
    startPoint+=speed;
    cxt.beginPath();
    cxt.arc(startPoint,300,30,0,2*Math.PI,true);
    cxt.closePath();
    cxt.fill();
}

運行效果如下:

Canvas實現直線與圓形的物理運動效果

PS:這裏面畫面有點卡頓,是錄製的時候軟件的因素造成的,直接運行上訴代碼是可以看到正常運行的效果

重點代碼分析:

var speed=0;
var startPoint=800;
function run(cxt){
    speed=-7;
    cxt.clearRect(0,0,1024,728);
    //cxt.top+=speed;    
    startPoint+=speed;
    cxt.beginPath();
    cxt.arc(startPoint,300,30,0,2*Math.PI,true);
    cxt.closePath();
    cxt.fill();
}

先把速度定義爲0和獲取開始點,然後將canvas畫面的內容清除,接着是計算變化後的座標,然後進行重繪(座標重新計算是運動關鍵所在)

勻變速直線運動

勻變速直線運動的定義:在直線運動中,把加速度的大小和方向都不改變的運動(加速度爲正時),稱之爲勻加速直線運動。

基本公式:

速度公式: V=V0+at
位移公式: x=V0t+1/2at^2

所以我們依次需要定義這樣的幾個變量加速度a,初始速度V0,位移量x,隨時間變化的速度v,時間time,這幾個變量的初始化值均爲0,代碼如下所示:

HTML代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas勻加速直線運動</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <canvas id="canvas">你的瀏覽器不支持canvas,請跟換其他瀏覽器試一試</canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

javascript代碼:

window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=728;
    canvas.width=1024;
    var context=canvas.getContext('2d');
    context.fillStyle='red';
    context.beginPath();
    context.arc(800,300,30,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    setInterval(function(){
        run(context);
    }, 50);
};
var v0=0;//初始速度
var a=0;//加速度
var v=0;//變化的速度
var time=0;//時間
var x=0;//位移量
var startPoint=800;//起始點
// V=V0+at
// x=v0t+1/2at^2
// v^2-V^2=2ax
function run(cxt){
    time+=0.05;
    a=10;
    x=-(0.5*a*(time*time));//位移公式代入
    startPoint+=x;
    cxt.clearRect(0,0,1024,728);
    cxt.beginPath();
    cxt.arc(startPoint,300,30,0,2*Math.PI,true);
    cxt.closePath();
    cxt.fill();
}

運行的效果如下:

Canvas實現直線與圓形的物理運動效果

基本上直線運動比較典型的也就是這兩種,如有遺漏其他運動或者是需要博主講解其他運動的製作的,請在留言板上留言

三、簡單的曲線運動的實現

說到簡單的曲線運動,我們就從最簡單的也是我認爲最基礎的運動圓周運動說起

1、勻速圓周運動

圓周運動的定義:質點沿圓周運動,如果在任意相等的時間裏通過的圓弧長度都相等,這種運動就叫做“勻速圓周運動”,亦稱“勻速率圓周運動”。因爲物體作圓周運動時速率不變,但速度方向隨時發生變化。所以勻速圓周運動的線速度是每時每刻都在發生變化的。

勻速圓周運動的實現與分析

勻速圓周運動的實現第一反應我們會選擇通過勻速圓周運動的物理公式進行計算得到,但是物理公式中沒有哪條明確的公式是可以把單位時間的變化量和所在點的具體座標相關聯的,顯然這樣的一條思路是行不通的,從物理公式上面是來說是不具有可行性的,所以我們應該要換另外的一種方法來實現,這個時候我們應該要看透勻速圓周運動的本質,本質上來說,勻速圓周運動的實現其實就是通過一個原點,然後在這個原點的基礎之上對一個物體進行360度的旋轉。好的,相信對canvas api熟悉的小夥伴已經想到了,是的,我們可以通過旋轉或者是矩陣來實現

Canvas實現直線與圓形的物理運動效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas實現圓周運動</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <canvas id="canvas">你的瀏覽器不支持canvas,請跟換其他瀏覽器試一試</canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

相關的JavaScript代碼:

window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=728;
    canvas.width=1024;
    var context=canvas.getContext('2d');
    drawNotChange(context);
    context.fillStyle='blue';
    context.beginPath();
    context.arc(500,550,30,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    setInterval(function(){
        run(context);
    }, 50);
};
var time=0;//定義運動的執行次數
function run(cxt){

    cxt.clearRect(0,0,1024,728);   
    drawNotChange(cxt);
    cxt.save();//將當前以左上角座標爲(0,0)的上下文環境進行保存,這樣是爲了在接下來中要進行畫布偏移後,可以進行還原當前的環境
    cxt.translate(500,400);
    cxt.rotate(time*8*Math.PI/180);//設定每次旋轉的度數
    cxt.fillStyle='blue';
    cxt.beginPath();
    cxt.arc(0,150,30,0,2*Math.PI,false);
    cxt.closePath();
    cxt.fill();
    cxt.restore();//將當前爲(500,400)的點還原爲(0,0),其實在save中就是將上下文環境保存到棧中,在restore下面對其進行還原
    time+=1;
}

//繪製不變因素
function drawNotChange(context){
    context.fillStyle='red';
    context.beginPath();
    context.arc(500,400,30,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    context.beginPath();
    context.arc(500,400,150,0,2*Math.PI,true);
    context.closePath();
    context.stroke();
}

運行的結果如下:

Canvas實現直線與圓形的物理運動效果

爲了讓讀者能夠明白其中的原理,我會在註釋中儘量將代碼註釋清楚

2、橢圓運動

可能有些小夥伴們對於高中的知識都已經遺忘了,但是這個不妨礙,因爲在接下來我們會通過一步一步的複習相關數學知識最後纔來實現效果,但是如果對高中知識比較熟悉的小夥伴,建議跳過這個階段,直接看代碼就行了,以免浪費時間

橢圓的定義:橢圓(Ellipse)是平面內到定點F1、F2的距離之和等於常數(大於|F1F2|)的動點P的軌跡,F1、F2稱爲橢圓的兩個焦點。其數學表表達式爲:|PF1|+|PF2|=2a(2a>|F1F2|)

橢圓圖解:

Canvas實現直線與圓形的物理運動效果

長軸長我們用2a表示,短軸長我們用2b表示

假設橢圓的長軸與X軸平行,那麼表達式如下所示:

Canvas實現直線與圓形的物理運動效果

根據三角函數之間的關係我們可以推導出:

x=a+cos(t)

y=b+sin(t)

這裏面的t代表的是單位是單位時間內旋轉的弧度

具體的推導會在以後有時間,爲大家專門寫一篇博文來講解一些公式的推導過程

關於橢圓的數學知識已經講完了,還是不太清楚的同學請執行去複習高中的知識,在這裏就不再累贅了。

我們還開始代碼實現之前還有先假設好一些參數,我們假設原點O(500,300),繞橢圓運動的物體爲圓形半徑爲30,其中長半軸長a=200,短半軸長爲b=100,每次重新獲取物體的運動後的移動位置的時候,x都會變化一個單位,旋轉爲順時針旋轉,開始位置爲原點的正左邊的端點,最後原點我們以一個黑色且半徑爲10的小球表示。注意:上述的數據可以讀者自行定義,但是要注意這個前提是必須保證a>b,如果a<b那麼就不是這個公式了

我們先來實現橢圓的軌跡效果:

HTML代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>canvas實現橢圓運動</title>
    <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <canvas id="canvas">你的瀏覽器不支持canvas,請跟換其他瀏覽器試一試</canvas>
    <script type="text/javascript" src="script.js"></script>
</body>
</html>

JavaScript代碼:

var a=200,
    b=100,
    radius=30;
window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=668;
    canvas.width=1024;
    var cxt=canvas.getContext('2d');
    cxt.beginPath();
    cxt.arc(300,300,10,0,2*Math.PI,true)
    cxt.closePath();
    cxt.fill();
    route(cxt,300,300,200,100);
};

//橢圓路線繪製
function route(context,x,y,a,b){
    //max是等於1除以長軸值a和b中的較大者
   //i每次循環增加1/max,表示度數的增加
   //這樣可以使得每次循環所繪製的路徑(弧線)接近1像素
   var step = (a > b) ? 1 / a : 1 / b;
   context.beginPath();
   context.moveTo(x + a, y); //從橢圓的左端點開始繪製
   for (var i = 0; i < 2 * Math.PI; i += step)
   {
      //參數方程爲x = a * cos(i), y = b * sin(i),
      //參數爲i,表示度數(弧度)
      context.lineTo(x + a * Math.cos(i), y + b * Math.sin(i));
   }
   context.closePath();
   context.stroke();
}

 橢圓的軌跡的思路是:通過循環,將極小的線段首尾相連,繪製了一個類似於橢圓的一個圖像,但是由於線段太過細小導致了我們肉眼看上去就成了一個橢圓

運行的效果是:

Canvas實現直線與圓形的物理運動效果

橢圓上小球的運動實現

這個的製作思路跟上面的思路是一樣的,所以這裏就不再分析

這次我們就先看一看效果如何:

Canvas實現直線與圓形的物理運動效果

HTML代碼和上面的例子相同

JavaScript代碼如下:

var a=200,
    b=100,
    radius=30;
    time=0;//循環的次數
window.onload=function(){
    var canvas=document.getElementById('canvas');
    canvas.height=768;
    canvas.width=1024;
    var cxt=canvas.getContext('2d');
    centerPoint(cxt);
    arcRoute(cxt,300,300,a,b,radius);
    setInterval(function(){
        arcRoute(cxt,300,300,a,b,radius);
        }, 70);
};

//繪製原點
function centerPoint(cxt){
    cxt.fillStyle="black";
    cxt.beginPath();
    cxt.arc(300,300,10,0,2*Math.PI,true)
    cxt.closePath();
    cxt.fill();
}
//橢圓路線繪製
function route(context,x,y,a,b){
   var step = (a > b) ? 1 / a : 1 / b;
   context.beginPath();
   context.moveTo(x + a, y); //從橢圓的左端點開始繪製
   for (var i = 0; i < 2 * Math.PI; i += step)
   {
      context.lineTo(x + a * Math.cos(i), y + b * Math.sin(i));
   }
   context.closePath();
   context.stroke();
}

//橢圓上小球運動的實現
function arcRoute(context,x,y,a,b,r){
    context.clearRect(0,0,1024,768);
    route(context,x,x,a,b);
    centerPoint(context);
    var step = (a > b) ? 1 / a : 1 / b;
    context.fillStyle="red";
    if(time==0){
        context.beginPath();
        context.arc(x,y,r,0,2*Math.PI,true);
        context.closePath();
        context.fill();
    }else{
    context.beginPath();
    context.arc(x+a*Math.cos(time),y+b*Math.sin(time),r,0,2*Math.PI,true);
    context.closePath();
    context.fill();
    }
    time+=1;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章