16、《每周一点canvas动画》——坐标旋转

每周一点canvas动画代码文件

在上一节中我们介绍了一些碰撞检测的方法。这一节本来打算讲解一个基于距离碰撞检测的小游戏。但是,因为最近比较忙,一直没来的及把游戏的整个过程完整的写出来。所以,这一节我们继续介绍下一项新技术——坐标旋转,它可能相对枯燥一些,而且有一些大家十分讨厌的数学公式。但是,它是我们后面高级动画的基石。所以,看的时候还请耐心一点,关于碰撞检测的游戏示例,我会在本周发出。

本章主要内容:

  1. 简单的坐标旋转

  2. 高级的坐标旋转

坐标旋转是一个非常有用的技术,它主要是让坐标围绕某个点旋转。通过它我们能实现很对有意思的效果,比如,一个物体与一个非水平的表面发生碰撞后,物体的反弹方向,反弹速度等。现在有没有注意到,我们的整个系列文章其实是一个循序渐进的过程,上一章我们介绍如何判断两个物体发生碰撞,而这一节我们介绍的就是两个物体发生碰撞后的事情。

1.简单的坐标旋转

《每周一点canvas动画》——三角函数(1)这一节中我们介绍了关于三角函数的使用。不知道你是否还能回忆起让一个物体做圆周运动的条件是什么?

首先我们得有一个中心点(center point),一个物体(object),然后是半径(radius),角度(angle)。通过增加或减少angle的值,使用基本的三角函数我们就可以让物体围绕某个点做圆周运动。这里我们回忆一下:

    object
    vr = 0.1
    angle = 0
    radius = 100
    centerX = 0
    centerY = 0

    object.x = centerX + Math.sin(angle)*radius
    object.y = centerY + Math.cos(angle)*radius
    angle += vr

上述代码只是展示了我们以前让物体做圆周运动的方法,并没有把它放在动画循环中。so, 看看我们让一个物体做圆周运动需要多少条件吧!如果你知道半径(radius)和角度(angle),上面的方法应该说是相当不错。

但是,如果你只知道中心点(center point)和物体的位置,还想要物体做圆周运动?该怎么做呢?

当然,如果你还是想要使用上面的方法,那也不难,你还是可以通过这两个已知量来计算我们需要的条件:角度(angle)和半径(radius)都是没问题的。

var dx = objext.x - center.x,
    dy = object.y - center.y,
    angle = Math.atan2(dy, dx),
    radius = Math.sqrt(dx*dx + dy*dy);

对于单个物体的旋转,使用这种方法非常不错,尤其是半径(radius)和角度(angle)这两变量,只需要设定一次的情况下。但是,在大多数的情况下,你可能有很多物体需要做旋转,而且他们距离中心点的相对位置也可能发生变化。so,你需要去计算距离,角度,半径,然后让角度在每一帧都加上vr,最终才能在每一帧得到物体的新坐标。

这个方法怎么说呢?既不高效,也不优雅。所以,我们需要一个新方法。

2.高级的坐标旋转

一提到高级,肯定离不开数学这货。

好吧,我只是给你打个预防针。要想让方法既优雅,又高效,我们必须调整思路。怎样用最少的条件得到我们想要的结果,这时候数学就要上场了。

这里我们不会用到多么高深的大学数学知识,只是简单的中学三角函数变换。你会发现原来老师整天唠叨的公式尽然会如此有用。

这个公式只需要物体的x,y坐标,和每一帧物体旋转的角度(角速度)。

newX = x * cos(rotation) - y * sin(rotation);
newY = y * cos(rotation) + x * sin(rotation);

如果你设置了一个中心点,上述公式可以变为

newX = (x - centerX) * cos(rotation) - (y - centerY) * sin(rotation);
newY = (y - centerY) * cos(rotation) + (x - centerX) * sin(rotation);

明白其中的原理了吗?学霸就直接跳过,没明白的听我细细讲来。

上图展示了该公式基于的原理图。物体旋转了一个很小的角度rotation, 那么它的位置该如何计算呢?

//物体的原始位置,距离中心点的距离radius
x = radius * cos(angle);
y = radius * sin(angle);

newX = radius * cos(angle + rotation);
newY = raidus * sin(angle + rotation);

我们知道

cos(a + b) = cos(a) * cos(b) - sin(a) * sin(b);
sin(a + b) = sin(a) * cos(b) + cos(a) * sin(b);

所以面的公式可以化简成如下形式

newX = radius * cos(angle) * cos(rotation) - raidus * sin(angle) * sin(rotation);
newY = raidus * sin(angle) * cos(rotation) + raidus * cos(angle) * sin(rotation);

又因为 x = radius cos(angle); y = radius sin(angle); 将其带入得

newX = x * cos(rotation) - y * sin(rotation);
newY = y * cos(rotation) + x * sin(rotation);

也就是说我们只需要知道物体的位置,设置它每秒钟要旋转的角度(角速度vr),就可以完成以前圆周运动的效果,至于中心点就看你自己的设置了。是不是很优雅,感谢一下你的数学老师吧!下面,我们就运用这个公式来完成一个简单的效果。

2.1 单物体旋转

也就是我们用它来代替以前的圆周运动

辅助线条是我加上去的,你不用太在意,具体看源代码文件:高级坐标旋转.html我们只关心核心代码

<canvas id="canvas" width="400" height="400" style="background:#000;"></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'),
               ball = new Ball(20, "red"),
               vr = 0.05, //每一帧转动的弧度值
               cos = Math.cos(vr), // 得到cos值
               sin = Math.sin(vr), // 得到sin值
               centerX = canvas.width/2,
               centerY = canvas.height/2,
               canvasWidth = canvas.width,
               canvasHeight = canvas.height;

              ball.x = Math.random()*canvasWidth;
              ball.y = Math.random()*canvasHeight;

              (function drawFrame(){
               window.requestAnimationFrame(drawFrame, canvas);
               context.clearRect(0, 0, canvas.width, canvas.height);

               var x1 = ball.x - centerX; //相对中心点的位置
               var y1 = ball.y - centerY;

               var newX = x1*cos - y1*sin; //旋转一定角度后的位置
               var newY = y1*cos + x1*sin;

               ball.x = centerX + newX; //更新球的位置
               ball.y = centerY + newY;
               ball.draw(context);
           }());
       }
    <script>
2.2 多物体旋转

那么怎样将该公式运用到多个物体上呢?这个其实在以前的例子中我们已经讲过很多遍了

...
var cos = Math.cos(vr),
    sin = math.sin(vr);

balls.forEash(function(ball){
    var x1 = ball.x - center.x,
        y1 = ball.y - center.y;

    var newX = x1 * cos - y1 * sin,
        newY = y1 * cos + x1 * sin;

    ball.x = centerX + newX;
    ball.y = centerY + newY;
})
...

下一节,我们讲与之紧密相关的角度反弹,敬请期待!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值