转载

《每周一点canvas动画》——边界检测与摩擦力

本章已经是《每周一点canvas动画》动画的第七篇了,我保证这一章不会再有难的数学公式和物理概念。鉴于有的同学并不是从第一章开始看这个系列,我将会把本文所用到的一些 类文件工具函数文件 放到文章的开头。关于前面每一章中的示例demo都可以在 每周一点canvas动画代码文件 中找到。下面是前面章节中我们用到的类文件和工具函数文件,有兴趣的同学可去下载。

  • ball.js

  • arrow.js

  • spaceShip.js

  • utils.js

ok!回归正题,本章的主要内容有:

  • 环境边界检测

  • 摩擦力的应用

同样我们在本章的第一部分介绍前一节 环境边界检测 的主要内容:

  1. 边界设置

  2. 超出边界移除

  3. 超出边界重新形成

  4. 边界环绕

  5. 边界反弹

1.设置边界

在上一章速度与加速度(2)中我们在重力加速度那一部分时其实就用到了边界检测的内容,在本章中我们将系统的讲述关于边界检测方面的内容!在大多数的情况下,一个简单的矩形就是一个物体运动的环境边界。比如我们的canvas画布,我们做的所有动画都是在canvas中运行的,那么canvas画布就是我们动画的运行环境。那么,如何设置canvas的边界呢?答案很简单:

    var left = 0,         top = 0,         right = canvas.width,         bottom = canvas.height;

这里我们假设你使用整个canvas画布。当然,如果你只是用一部分也可以设定:top = 120, bottom = 300, left = 50, right = 300 这些数值的大小都随你设置,只要最终的效果是一个矩形的环境就是ok的(这里我们先讲最简单的矩形环境).

设置完边界我们就可以开始做些事情了,假设我们的canvas里有个小球:

    if(ball.x > right){         // do something     }else if(ball.x < left){         //do something     }      if(ball.y > bottom){         //do something     }else if(ball.y < top){         //do something     }

我们通过判断小球的位置是否超出canvas的边界来做一些事情,那能做哪些事情呢?

2.超出边界移除

我们第一个要做的效果是超出边界移除,用到的是 ball.js 文件,先看看效果图吧!

《每周一点canvas动画》——边界检测与摩擦力

上代码:

       <canvas id="canvas" width="400" height="400" style="background:#000;">            your browser not support canvas        </canvas>        <p id="log"></p>         <script src="../js/utils.js"></script>         <script src="../js/ball.js"></script>         <script>             window.onload = function(){                 var canvas = document.getElementById("canvas"),                     context = canvas.getContext("2d"),                     log = document.getElementById("log");                  var balls = [],                     numBall = 10,                     canWid = canvas.width,                     canHei = canvas.height;                  //定义10个小球                 for(var i=0; i<numBall; i++){                     var size = Math.random()*20 + 5,   //颜色                         color = Math.random()*(0xffffff),   //大小                         ball = new Ball(size, color);                      ball.id = "ball_" + i;   //给每个小球一个id                     ball.radius = Math.random()*30+10;                     ball.x = Math.random()*canWid;                     ball.y = Math.random()*canHei;                     ball.vx = Math.random()*2 - 1;                     ball.vy = Math.random()*2 - 1;                      balls.push(ball); //push进数组                 }                  //定义draw函数                 function draw(ball, pos){                       //让小球加上它的速度值                       ball.x += ball.vx;                       ball.y += ball.vy;                        //判断是否超出边界,不论超出哪一边                       if(ball.x - ball.radius > canvas.width ||                          ball.radius+ball.x <0 ||                          ball.y - ball.radius > canvas.height ||                          ball.y+ball.radius < 0){                          //将超出边界的小球从数组中删除                           balls.splice(pos, 1);                           if(balls.length > 0){                               //将超出的小球的id值输出                               log.innerHTML += "移除" + ball.id + "<br/>";                           }else{                               log.innerHTML = "全部移除";                           }                       }                      ball.draw(context);                   }                  //动画循环                 (function drawFrame(){                     window.requestAnimationFrame(drawFrame, canvas);                     context.clearRect(0, 0, canvas.width, canvas.height);                      //定义初始变量 i 为小球的数量, 并且会随着小球的移除减小                     var i = balls.length;                     while(i--){                         draw(balls[i], i);                     }                 }());             }         </script>

我们定义了十个小球,并把它们push进一个数组balls中,每当有小球超出边界(即完全消失在canvas中),就从balls中删除小球,并且打印出小球的id值。以横坐标为例,这里我们并没有使用 ball.x > canvas.width ,而是使用 ball.x - ball.radius > canvas.width ,这里做一点小的解释。因为 ball.x 是小球的球心,所以要让小球完全移除必须是小球的最左边(或最右边)完全超出canvas的边界,这才叫完全移除。图示如下:

《每周一点canvas动画》——边界检测与摩擦力

图示以canvas左侧边界为例,小球最右边的坐标为 ball.x + ball.raidus 。所以,只要它超过了canvas的左侧边界,小球就被判定为完全移除。

3.超出边界重新形成

这个效果的思想比较简单: 当小球超出了边界,重置他的位置就ok了 。效果图:

《每周一点canvas动画》——边界检测与摩擦力

这部分代码与上一部分代码区别不是很大,这里我只列出改变的部分:

     function draw(ball, pos){                       ...                       if(边界超出判定){                           //超出了重置速度与坐标                           ball.x = canvas.width/2;                           ball.y = canvas.height;                           ball.vx = Math.random()*(2) - 1;                           ball.vy = Math.random()*(-2) - 1;                       }                    ...                   }      (function drawFrame(){                     window.requestAnimationFrame(drawFrame, canvas);                     context.clearRect(0, 0, canvas.width, canvas.height);                     balls.forEach(draw);                 }()); 

当小球超出了边界,我们就重置它的坐标与速度。注意,这里在重置速度代码中,我们将小球的竖直方向的速度(ball.vy)设置为负值,就达到了小球从下到上喷涌的效果。

3.边界环绕

边界环绕其实是上一部分的一个子类型。它想要实现的效果是: 当物体从一个边界消失,会从对立的边界出现 。简单来说,就是如果物体从左边界消失,就会从右边界出现。

《每周一点canvas动画》——边界检测与摩擦力

如图所示,我们将canvas画布左右边界对接。当物体从左边界消失,立即又从右边界出现,形成一种环绕效果。其实,没有想象中的那么神秘,我们也不会真的把canvas画布的左右边界对接。先上上效果图:

《每周一点canvas动画》——边界检测与摩擦力

上代码:

    <canvas id="canvas" width="500" height="500" style="background:#000;">            your browser not support canvas!        </canvas>        <script src="../js/utils.js"></script>        <script src="../js/spaceship.js"></script>        <script>            window.onload = function(){                var canvas = document.getElementById("canvas"),                    context = canvas.getContext("2d");                 var ship = new SpaceShip();                    ship.x = canvas.width/2;                    ship.y = canvas.height/2;                 var vr = 0,                    vx = 0,                    vy = 0,                    ax = 0,                    ay = 0,                    angle = 0,                    thrust = 0;                 window.addEventListener("keydown", function(event){                    switch (event.keyCode){                        case 37:                            vr = -3;                            break;                        case 39:                            vr = 3;                            break;                        case 38:                            ship.showFlame = true;                            thrust = 0.05;                            break;                        case 40:                            thrust -= 0.02;                            break;                          }                }, false);                window.addEventListener("keyup", function(event){                    vr = 0;                    thrust = 0;                    ship.showFlame = false;                }, false);                 (function drawFrame(){                    window.requestAnimationFrame(drawFrame, canvas);                    context.clearRect(0, 0, canvas.width, canvas.height);                     angle += vr * Math.PI/180                    ship.rotation = angle;                    ax = Math.cos(angle)*thrust;                    ay = Math.sin(angle)*thrust;                     vx += ax;                    vy += ay;                     ship.x += vx;                    ship.y += vy                                        //核心部分                    if(ship.x - ship.width/2 > canvas.width){                        ship.x = 0;                    }                    if(ship.x < 0){                        ship.x = canvas.width;                    }                    if(ship.y - ship.height/2> canvas.height){                        ship.y = 0;                    }                    if(ship.y <0){                        ship.y = canvas.height;                    }                     ship.draw(context);                }())            }        </script>

为了方便演示,在动态图中我只让飞船沿着水平方向移动,当飞船消失在右边界的时候,立即会出现在左边界。这段代码中,你只需要关注核心部分,其他部分使用的是上一章 《每周一点canvas动画》——速度与加速度(2) 结尾太空船的代码!相信理解起来很容易,在这我就不做过多解释了。

4.边界反弹

ok!本章的重头戏来了,标题的意思已经很明白了,我们要让物体触碰到边界的时候反弹回来。就像真实的世界中一样,球体撞到墙上反弹回来。有了前面的铺垫,要实现这个效果那还不是分分钟的事情!一样的套路,先上效果图:

《每周一点canvas动画》——边界检测与摩擦力

上代码:

       <canvas id="canvas" width="400" height="300" style="background:#000;">            your browser not support canvas!        </canvas>        <script src="../js/utils.js"></script>        <script src="../js/ball.js"></script>        <script>          window.onload = function(){              var canvas = document.getElementById("canvas"),                  context = canvas.getContext("2d");               var vx = Math.random()*10 - 5;              var vy = Math.random()*10 - 5;               var ball = new Ball(20, "#ff0000");                  ball.x = canvas.width/2;                  ball.y = canvas.height/2;                              //动画循环              (function drawFrame(){                  window.requestAnimationFrame(drawFrame, canvas);                  context.clearRect(0, 0, canvas.width, canvas.height);                   ball.x += vx;                  ball.y += vy;                                   //核心部分                  if(ball.x + ball.radius > canvas.width){                      ball.x = canvas.width - ball.radius;                      vx *= -1;                  }else if(ball.x - ball.radius < 0){                      ball.x = ball.radius;                      vx *= -1;                  }                  if(ball.y + ball.radius > canvas.height){                      ball.y = canvas.height - ball.radius;                      vy *= -1;                  }else if(ball.y - ball.radius < 0){                      ball.y = ball.radius;                      vy *= -1;                  }                   ball.draw(context);              }())          }

代码很简洁,你只需要关注核心部分。这里同样我们判断小球是否超出边界,但不同的是,我们这次判断的是小球的最右边的位置,即坐标为 ball.x + ball.radius 的位置,当这个位置大于canvas的宽度的时候,在if执行语句中我们让小球的位置等于 canvas.width - ball.radius 即刚好球体的最右边的位置靠在canvas画布的右边界上,并且最重要的是:我们让速度乘以 -1 ,相当于将速度方向旋转180度。

更好的写法,我们定义一个变量bounce,它的值除了-1你还可以取-0.5, -1.5....,不同之处在于,小球的速度会衰减或增强。如果你想要模仿现实中小球在经过几次反弹后速度逐渐减小最后停止,那么你可以将bounce的值设为[-1, 0)之间的值试一试。

   var bounce = -1;     if(ball.x + ball.radius > canvas.width){            ball.x = canvas.width - ball.radius;            vx *= bounce;     }else if(ball.x - ball.radius < 0){            ball.x = ball.radius;            vx *= bounce;     }     if(ball.y + ball.radius > canvas.height){           ball.y = canvas.height - ball.radius;           vy *= bounce;     }else if(ball.y - ball.radius < 0){           ball.y = ball.radius;           vy *= bounce;     }    

好了,本节的内容到这就结束了,敬请期待下一章————摩擦力。

原文  https://segmentfault.com/a/1190000005081879
正文到此结束
Loading...