1. 程式人生 > >奇妙的canvas:彈跳小球

奇妙的canvas:彈跳小球

前言

關於canvas的入門知識,網上有很多成熟的資料,我就不多做介紹啦。

彈跳小球算是一個比較常見的效果,接下來就講講如何在canvas裡實現彈跳小球吧~

首先慣例先看效果圖:

由於視訊轉碼問題,可能有點稍卡,但是在瀏覽器裡看是流暢的噢(。ì _ í。)

1.勻減速直線運動

為了方便理解之後的彈跳運動,我們先看看如何在canvas裡實現勻減速直線運動。

我希望達到的目的是:給小球一個初速度,讓小球以這個初速度做直線運動,剛好到達指定的位置時,停止運動。

(1)幾個相關概念

requestAnimationFrame()

做過動畫的人都知道,動畫中經常會用到這個方法。它使用一個回撥函式作為引數,這個回撥函式會在瀏覽器重繪之前呼叫。回撥的次數通常是每秒60次。類比於動畫中的每一幀,每秒60幀,每一幀的時候,都要對canvas的畫布進行擦除與重繪。

結合到勻減速運動,也就是說,我要知道每一幀,這個小球的座標位置。

另外值得一提的是,canvas的座標長介個樣子:

幾個物理概念

在這裡,我把單位時間定為每一幀,單位距離定為每一個畫素;

  • 速度v表示的是每單位時間內,移動的距離;

  • 加速度a表示每單位時間內,速度v的改變值;

我先指定這個小球只沿著x軸運動,運動距離是canvas的寬度,所以我們的問題是:

(1)如何讓小球剛好在到達canvas邊界時,速度遞減到0?
​ 那就要求加速度a,由物理公式我們可以求出a=v^2/2s。

(2)在每一幀怎麼更新小球位置?

​ 在每一幀都要更新速度v = v + a,和小球的x軸座標x = x+v;

(2)程式碼片段

    //勻減速小球;
    function Ball(radius, x, y, v){
        this.radius = radius;
        this.x = x;
        this.y = y;
        this.v = v;
        this.a = ((Math.pow(v,2)/(2*(canvas.width-x))))*(-1);
    }
    //繪製勻減速小球;
    function drawBall(){
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = '#fff'
; ctx.beginPath(); //更新 ball.v += ball.a; ball.x += ball.v; ball.x = (ball.x + ball.radius > canvas.width) ? (canvas.width - ball.radius) : ball.x; ctx.arc(ball.x, canvas.height/2, ball.radius, 0, 2 * Math.PI, true); ctx.fill(); stop = ball.x + ball.radius >= canvas.width ? cancelAnimationFrame(stop) : requestAnimationFrame(drawBall); } 複製程式碼

2. 彈跳運動

我們知道,小球在重力的作用下,掉到地面,如果沒有除去重力的其他外力作用,速度方向會變成相反,大小不變,但是由於產生形變和摩擦力等因素,對小球的速度造成一定損失,其速度方向變成相反,大小會比原來小,最後趨於0。

其實彈跳運動的實現也很簡單,給他一個重力加速度,在它觸碰到底部時,可以通過設定一個 -1~0的damping 值,讓小球每次接觸到底部時,v = v * damping,這樣經過幾個彈跳,v就會逐漸趨於0。

程式碼片段

     //彈跳小球;
    function BcBall(radius, x, y, v){
        this.radius = radius;
        this.x = x;
        this.y = y;
        this.v = v;
        //設定重力加速度與損失比例
        this.gravity = 0.5;
        this.damping = -0.8;
    }
 //繪製彈性小球;
    var preV = 0;   //記錄前一次速度

    function drawBcBall() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = '#fff';
        ctx.beginPath();

        ball.v += ball.gravity;
        ball.y += ball.v;
        preV = ball.v;

        ctx.arc(canvas.width/2, ball.y, ball.radius, 0, 2 * Math.PI, true);
        ctx.fill();
        stop = requestAnimationFrame(drawBcBall);

        if(ball.y + ball.radius >= canvas.height){
            ball.y = canvas.height - ball.radius;
            ball.v *= ball.damping;
            
            if(Math.abs(preV - ball.v) < 0.5){
                cancelAnimationFrame(stop);
            }
        }
    }
複製程式碼

Last

完整的程式碼在我的GitHub裡面,有興趣者可以查閱~

GitHub連線:github.com/dy21335/Pra…