奇妙的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…

猜你喜欢

转载自juejin.im/post/5bdd393c6fb9a04a0163c14e