9、《每周一点canvas动画》——移动物体(1)

本系列文章代码文件

《每周一点canvas动画》——用户交互这一章我们介绍了用户交互事件,包括:鼠标事件触摸事件键盘事件。但是,到目前为止,我们除了键盘事件,对于另外两个事件几乎没有做过任何相应的应用。那么,在这一章,我们将要学习如何让canvas中的物体对用户的动作做出反应,并且学习如何拖拽和抛扔一个物体。不用担心,用到的都是我们学过的知识。本节主要内容:

  1. 物体捕获

  2. 捕获验证

  3. 物体的拖拽

1.物体捕获

鼠标是个简单,却效率极高的装置。它简单来说只做两件事:移动监测和按钮点击。然后,电脑会根据鼠标得到的信息来做很多事情,比如:位置追踪,点击事件发生时光标的位置,决定鼠标移动的速度,识别双击事件等等。仔细想想,所有的这些是不是都可以归结于鼠标的点击和移动。
现在,好好回忆一下,我们平时玩游戏的时候是怎样移动一个物体的。当然,如果没玩过的话,就别想了。它的基本流程如下:

click ->  move -> release

整个流程对应的事件如下:

  • mousedown:当按下鼠标按钮时发生

  • mouseup:当鼠标按钮抬起时发生

  • mousemove:当你移动鼠标时发生

OK!现在我们知道了,移动一个物体分为哪几步(是不是和想起了把大象装进冰箱分为那几步)。接下来还有一个重要的事情:怎样捕获一个物体呢。我们已经知道了如何移动一个物体,可是如果你都没有要移动的对象,那不是白搭嘛!简单来说就是怎样判定我们的鼠标真的点击到了物体上,你知道了不行,必须得让我们的电脑知道才行。

1.1 外接矩形判别法

这里为了便于理解,我做了一个倍详细的图。灰色区域代表canvas, 蓝色的代表rect, 我们已知的是rect的坐标(这里以左上角作为坐标基准)和宽高。当鼠标点击在蓝色区域,即rect上就代表我们捕获这个物体。那么怎样算是鼠标落在这块区域上呢?看下面的几个条件:

1.mouse.x > rect.x
2.mouse.x < rect.x + rect.width
3.mouse.y > rect.y
4.mouse.y < rect.y + rect.height

我们列出了四个条件,仔细对比图片看看。是不是只要所有条件同时满足就可以判定我们的鼠标落在了rect上。所以,如果你要检测的物体是矩形,或者趋近于矩形都可以使用这种方法。

1.2 外接圆判别法

我们可以看到,如果当物体变成圆形,再使用上面的方法去判定鼠标是否落在ball上,就不会那么精确了。当然,不是不行,就是不够精确,当球体很小的时候还是可以接受的。
对于球体,还有另一种方法,那就是判定鼠标距离球心的距离。如果距离小于球体半径就可以判断鼠标落在了球体上,反之则没有。所以,如果你要检测的物体趋近于圆,就可以使用这种方法。

鼠标距球心的距离
dx = mouse.x - ball.x;
dy = mouse.y - ball.y;
dist = Math.sqrt(dx*dx + dy*dy)
if(dist < ball.radius){
   //碰上了
}
1.3 多边形和不规则图形

要是我们要捕获的对象是不规则的,那就比较难了,我们需要用到分离轴定理(SAT)和最小平移向量(MTV),听起来就很高深啊。自己去了解一下吧!其实挺容易的,这里就不介绍了。

在本章中为了简单,我们的球体也是使用外接矩形法。我们需要在ball.js文件中增加如下代码:

Ball.prototype.getBounds = function(){
    return {
        x: this.x - this.radius,
        y: this.y - this.radius,
        width: this.radius*2,
        height: this.radius*2
    };
}

这里定义了一个方法getBounds(),该方法返回一个对象,包含物体的坐标(以左上角为基准)和宽高,就跟上图中表示的一样。同时我们也需要在工具函数文件utils.js中增加一个新方法:

utils.containsPoint = function(rect, x, y){
    return !(x<rect.x || x>rect.x + rect.width ||
             y<rect.y || y>rect.y + rect.height);
}

注意,该方法返回的是个布尔值,如果落在区域内,那么返回的是!(0 || 0 || 0 || 0)也就是true,如果有任意一个条件不满足,那么久返回false,表示不在区域内。该方法传入三个参数,
rect: 表示要检测的物体
x,y: 鼠标坐标

2.捕获验证

为了防止步子迈的太大扯着蛋!我们先做个简单的效果:

当在鼠标没有点击在ball上时,打印出canvas和当前的活动状态,当鼠标点击在球体上时打印出ball和鼠标的运动状态。具体代码如下:

    <canvas id="canvas" width="400" height="400" style="background:#000;">
       your browser not support canvas
   </canvas>
   <textarea name="textarea" id="txt" cols="30" rows="10"></textarea>
   <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('txt');

           //传入canvas,获取鼠标坐标
           var mouse = utils.captureMouse(canvas);

           var ball = new Ball(20,"red");
               ball.x = canvas.width/2;
               ball.y = canvas.height/2;
               ball.draw(context);

           //定义状态函数
           function state(wrd){
               /*注意这里用到了我们前面新加的代码
                 ball.getBounds(): 返回一个对象包含球的坐标,宽高.并作为参数传入
                 utils.containsPoint(): 判断点击区域,返回布尔值
               */
               if(utils.containsPoint(ball.getBounds(), mouse.x, mouse.y)){
                   log.value = "in ball : "+ wrd;
               }else{
                   log.value = "canvas : " + wrd;
               }
           }
           // 为canvas添加mousedown事件
           canvas.addEventListener('mousedown', function(event){
               state("mousedown");
               //mouseup
               canvas.addEventListener('mouseup', function(event){
                    state("mouseup");
               },false);
               //mousemove
               canvas.addEventListener('mousemove', function(event){
                    state("mousemove");
               }, false);
           }, false);
       }
    </script>

注意这里,我们将mouseupmousemove定义在了mousedown事件内部。也就是说如果mousedown事件不触发,就无法触发另外两个事件。想想我们移动一个物体的基本流程,也的确合乎情理。

touch事件与鼠标点击事件很相似,这里我就不给代码了,具体的文件可以在文章开头看到。

3.物体的拖拽

有了前面的基础,要移动一个物体算什么难事,我们先上效果图:

具体代码如下:

<canvas id="canvas" width="400" height="400" 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'),
               mouse = utils.captureMouse(canvas),
               ball = new Ball(20, "red");

           ball.x = canvas.width/2;
           ball.y = canvas.height/2;

           var w = 0, h = 0;

           //为canvas设置监听事件
           canvas.addEventListener('mousedown', function(event){
               //判断是否点击在球体上
               if(utils.containsPoint(ball.getBounds(), mouse.x, mouse.y)){
                   w = mouse.x - ball.x;
                   h = mouse.y - ball.y;
                   canvas.addEventListener('mouseup', onMouseUp, false);
                   canvas.addEventListener('mousemove', onMouseMove, false);
               }
           }, false);

           //mouseup事件执行函数
           function onMouseUp(event){
               canvas.removeEventListener('mouseup', onMouseUp, false);
               canvas.removeEventListener('mousemove', onMouseMove, false);
           }
           //mousemove事件执行函数
           function onMouseMove(event){
                  ball.x = mouse.x - w;
                  ball.y = mouse.y - h;
           }
           //动画循环
           (function dramFrame(){
               window.requestAnimationFrame(dramFrame, canvas);
               context.clearRect(0, 0, canvas.width, canvas.height);

               ball.draw(context);
           }())
       }
    </script>

这部分代码,其实只是在对上部分代码的改进。我们在canvas上设置了mousedown事件,当鼠标点击在canvas上时,就会触发。此时注意我们的判定条件,如果为true,就会执行if中的语句。

if中,我们为canvas添加了mousemovemouseup事件,mousemove事件的触发是在鼠标点击在了ball之后,移动鼠标才触发的,他执行的函数是onMouseMovemouseup是当鼠标抬起是触发的,执行函数是onMouseUp

onMouseMove函数中我们重新设置了ball的位置。onMouseUp函数中移除监听事件。

注意,这里有两个变量wh,这两个变量的作用是修正ball的位置的。为什么这样说呢?因为当我们点击球体的时候,不一定点击的就是球体的球心。这时移动物体,如果没有这个修正值,小球就会出现一个快速的偏移,让鼠标位于小球的球心,感觉很别扭。所以,在点击时,我们就把鼠标与球心之间的坐标差值计算出来,在移动的时候给个修正就不会出现快速偏移的现象了。

我说了这么多,要想深刻体会,还是自己动手尝试一下吧!下一节我们介绍如何捕获运动的物体,敬请期待!

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Canvas是HTML5新增的一个用于绘制图形的标签,可以通过JavaScript来控制Canvas进行图形的绘制。在进行Canvas绘制时,可以通过调整画布的位置、大小和旋转角度等属性,来控制绘制出来的图形的位置和形态。 下面我们就来介绍一下Canvas画布的移动、缩放和旋转: 1. 画布移动 画布移动可以通过Canvas提供的translate方法来实现。translate方法接收两个参数,分别表示x轴和y轴方向上的偏移量。偏移量为正值表示向右或向下移动,为负值表示向左或向上移动。 例如,我们可以通过下面的代码将画布向右移动50个像素,向下移动100个像素: ``` context.translate(50, 100); ``` 2. 画布缩放 画布缩放可以通过Canvas提供的scale方法来实现。scale方法接收两个参数,分别表示x轴和y轴方向上的缩放比例。缩放比例为大于1的值表示放大,小于1的值表示缩小。 例如,我们可以通过下面的代码将画布在x轴和y轴方向上都放大2倍: ``` context.scale(2, 2); ``` 3. 画布旋转 画布旋转可以通过Canvas提供的rotate方法来实现。rotate方法接收一个参数,表示旋转的角度,单位为弧度。 例如,我们可以通过下面的代码将画布旋转45度: ``` context.rotate(Math.PI / 4); ``` 需要注意的是,Canvas绘图的坐标系原点默认在画布的左上角,而移动、缩放和旋转操作都是相对于原点进行的。因此,在进行这些操作时,需要先将画布的原点移动到需要的位置,再进行操作。例如,如果需要将画布向右移动50个像素,需要先将原点移动到(50,0)的位置,再进行移动操作: ``` context.translate(50, 0); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值