《每周一点canvas动画》——角度反弹

每周一点canvas动画代码文件

在上一节我们介绍了高级的坐标旋转方法,我们只需要知道物体的位置,通过设定每一帧需要旋转的角速度,通过公式

newX = x*cos - y*sin;
newY = y*cos + x*sin;

就可以计算出做圆周运动时物体的下一个坐标位置。本节的内容与上一节的内容息息相关。所以,务必把上一节的内容弄懂了,再来看这一节你就不会那么吃力了。这也应该是本系列最难的一部分吧!请收下我的膝盖。。。

在前面的章节中我们写了很多的小动画,大部分的动画中,为了限制物体的活动范围,当物体与canvas画布边界接触的时候,我们都设定了一个反弹系数bounce,让物体有种撞上墙壁的感觉。但是现实环境中不仅仅只有水平或竖直方向的平面,更多的是不同倾斜度的表面。那么,当物体撞击上这样的表面我们该如何处理呢?物体反弹后的速度大小,还有方向该如何计算呢?下面我们就来一一讨论这些问题。

1.概念解析

如果,你对前面的文字描述不理解,没关系。看下图,我会通过图形来形象的描述。比如,现在我们有个斜平面,物体以一定的速度朝着斜面运动。

没有现成的公式可以直接让物体按照我们想象的从斜面反弹。这似乎是个很复杂的问题,那你有没有想过,既然斜面不好做,何不把它转到平面来做呢!我们最擅长的就是平面的反弹了。

思路有了,我们需要把它转化成平面来做角度反弹。那我们需要做哪些事情呢?不卖关子了,我们所要做的所有事情就是把整个系统包括物体,包括平面全部旋转的平面,做完反弹处理后,再旋转回去。这就意味着,我们需要旋转斜面,旋转物体的坐标,并且还要旋转物体的速度。

这里我随便设置了一个中心点(图中虚线与实线相交的部分),让其围绕这个中心点旋转至平面。此时速度也做了相应的旋转,图中清晰的显示出了速度的方向。接下来,你应该就很熟悉了,既然到了水平面做反弹就很容易。反弹后的速度方向如下图:

下一步,就是把整个系统旋转回去,也就是还原整个系统到初始位置

如果你对它的真实性表示怀疑,这里我们它旋转前与旋转恢复后的两幅图做个叠加

是不是跟你想象的完全一样呢?

2.代码实现

首先我们,新建一个类文件line.js,它的作用和其他的类文件一样,就是画一条线。这里我就不列出来了,你可以去代码文件中找到这个文件的代码。先上效果图

具体代码如下,首先引入类文件

<canvas id="canvas" width="500" height="500" style="background: #fff;"></canvas>
   <script src="../js/utils.js"></script>
   <script src="../js/ball.js"></script>
   <script src="../js/Line.js"></script>

然后是初始化我们需要的元素

<script>
       window.onload = function(){
           var canvas = document.getElementById('canvas'),context = canvas.getContext('2d'),ball = new Ball(20,"red"),line = new Line(0,300,0),gravity = 0.2,//重力加速度
               bounce = -0.6,//反弹系数
               angleN = 10;                           //斜面的旋转角度
           
           ball.x = 100;
           ball.y = 100;
           
           line.x = 50;
           line.y = 300;
           line.rotation = angleN * Math.PI / 180;    //角度旋转
           
           //得到系统从斜面转到平面的cos和sin值(就是我们坐标旋转中的sin,cos值)
           var cos = Math.cos(line.rotation),sin = Math.sin(line.rotation);
               
           //动画循环
           }
    </script>

小球的引入我就不解释了,Line有4个参数(x1,y1,x2,y2),表示从(x1,y1)位置开始,至(x2,y2)位置画一条线。在代码中是从(0,0)到(300,0),也就是画了一条长度为300的水平直线。然后,把它移动到(50,300)的位置,并让其倾斜了一个角度。这样你就看到我们图中的斜线了。

下一步,就是我们的核心了

(function drawFrame(){
               window.requestAnimationFrame(drawFrame,canvas);
               context.clearRect(0,canvas.width,canvas.height);
               
               //球体运动
               ball.vy += gravity;            
               ball.x += ball.vx;                //初始为0,小球竖直往下落
               ball.y += ball.vy;
               
               //获取小球体与线的相对位置
               var x1 = ball.x - line.x,y1 = ball.y - line.y,//旋转坐标
                   x2 = x1 * cos + y1 * sin,y2 = y1 * cos - x1 * sin,//旋转速度
                   vx1 = ball.vx * cos + ball.vy * sin,vy1 = ball.vy * cos - ball.vx * sin;
            
               
               //如果小球与斜面碰撞
               if(y2 > -ball.radius){
                   y2 = -ball.radius;              //重设小球的位置
                   vy1 *= bounce;                  //反弹
               }
               //
               x1 = x2 * cos - y2 * sin;           //位置旋转回去,注意公式变化
               y1 = y2 * cos + x2 * sin;
               ball.vx = vx1 * cos - vy1 * sin;    //速度旋转回去
               ball.vy = vy1 * cos + vx1 * sin;
               ball.x = line.x + x1;               //小球位置变化
               ball.y = line.y + y1;
               
               ball.draw(context);
               line.draw(context);
               
}())

注意代码中,旋转回去的坐标旋转公式公式发生了变化。

3.代码优化

注意上部分代码中,我们发生坐标旋转是在下面的条件下:

if(y2 > -ball.radius){
       //...
  }

那上面的代码就有很大的问题了,我们在每一帧都做了坐标旋转,再旋转回去。其实完全没有必要,所以代码修改如下:

...
    var x1 = ball.x - line.x,y2 = y1*cos - x1*sin;
        
   
    if(y2 > -ball.radius){                       //只有当小球与平面接触时才做旋转
           var x2 = x1*cos + y1*sin;             //旋转 x 坐标
                      
           vx1 = ball.vx*cos + ball.vy*sin;      //旋转速度
           vy1 = ball.vy*cos - ball.vx*sin;
                      
            y2 = -ball.radius;
            vy1 *= bounce;
                      
           //所有东西旋转回去
           x1 = x2*cos - y2*sin;
           y1 = y2*cos + x2*sin;
           ball.vx = vx1*cos - vy1*sin;
           ball.vy = vy1*cos + vx1*sin;
           ball.x = line.x + x1;
           ball.y = line.y + y1;
}

4.边界问题

注意到在上面的效果中,当小球超出了斜面依然保持运动。而不是我们想象的掉落到地面上。为了修正这个问题,我们需要用到前面章节介绍的两个物体之间的边界检测方法,你应该很熟悉。

//动画循环中
var bounds = line.getBounds(),if( ball.x + ball.radius > bounds.x && ball.x - ball.radius < bounds.x + bounds.width){
    if(y2 > -ball.radius){
       //....
    }
}

效果如下:

5.更多动效

1.用鼠标控制斜面的角度

2.多斜面撞击

6.总结

本章的重点公式就是一个坐标旋转

newX = x*cos - y*sin;
newY = y*cos + x*sin;

//旋转回去
newX = x*cos + y*sin;
newY = y*cos - x*sin;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


HTML5和CSS3实现3D展示商品信息的代码
利用HTML5中的Canvas绘制笑脸的代码
Html5剪切板功能的实现
如何通过HTML5触摸事件实现移动端简易进度条
Html5移动端获奖无缝滚动动画实现
关于HTML5和CSS3实现机器猫的代码
HTML5使用DOM进行自定义控制
使用HTML5 Canvas绘制阴影效果的方法
使用PHP和HTML5 FormData实现无刷新文件上传
如何解决HTML5 虚拟键盘出现挡住输入框的问题
HTML5中div和section以及article的区别分析
html5和CSS 实现禁止IOS长按复制粘贴功能
html5 touch事件实现触屏页面上下滑动
canvas 模拟实现电子彩票刮刮乐的代码
HTML5 Plus 实现手机APP拍照或相册选择图片上传的功能
Android自定义环形LoadingView效果
HTML5 canvas绘制五角星的方法
html5使用html2canvas实现浏览器截图
使用Canvas处理图片的方法介绍
利用Canvas模仿百度贴吧客户端loading小球的方法