鼠标事件
,触摸事件
和键盘事件
。但是,到目前为止,我们除了键盘事件,对于另外两个事件几乎没有做过任何相应的应用。那么,在这一章,我们将要学习如何让canvas中的物体对用户的动作做出反应,并且学习如何拖拽和抛扔
一个物体。不用担心,用到的都是我们学过的知识。本节主要内容:
物体捕获
捕获验证
物体的拖拽
鼠标是个简单,却效率极高的装置。它简单来说只做两件事:移动监测和按钮点击。然后,电脑会根据鼠标得到的信息来做很多事情,比如:位置追踪,点击事件发生时光标的位置,决定鼠标移动的速度,识别双击事件等等。仔细想想,所有的这些是不是都可以归结于鼠标的点击和移动。
现在,好好回忆一下,我们平时玩游戏的时候是怎样移动一个物体的。当然,如果没玩过的话,就别想了。它的基本流程如下:
click -> move -> release
整个流程对应的事件如下:
mousedown:当按下鼠标按钮时发生
mouseup:当鼠标按钮抬起时发生
mousemove:当你移动鼠标时发生
OK!现在我们知道了,移动一个物体分为哪几步(是不是和想起了把大象装进冰箱分为那几步)。接下来还有一个重要的事情:怎样捕获一个物体呢。我们已经知道了如何移动一个物体,可是如果你都没有要移动的对象,那不是白搭嘛!简单来说就是怎样判定我们的鼠标真的点击到了物体上,你知道了不行,必须得让我们的电脑知道才行。
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上。所以,如果你要检测的物体是矩形,或者趋近于矩形都可以使用这种方法。
对于球体,还有另一种方法,那就是判定鼠标距离球心的距离。如果距离小于球体半径就可以判断鼠标落在了球体上,反之则没有。所以,如果你要检测的物体趋近于圆,就可以使用这种方法。
鼠标距球心的距离
dx = mouse.x - ball.x;
dy = mouse.y - ball.y;
dist = Math.sqrt(dx*dx + dy*dy)
if(dist < ball.radius){
//碰上了
}
要是我们要捕获的对象是不规则的,那就比较难了,我们需要用到分离轴定理(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: 鼠标坐标
为了防止步子迈的太大扯着蛋!我们先做个简单的效果:
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>
mouseup
,mousemove
定义在了mousedown
事件内部。也就是说如果mousedown事件不触发,就无法触发另外两个事件。想想我们移动一个物体的基本流程,也的确合乎情理。
touch
事件与鼠标点击事件很相似,这里我就不给代码了,具体的文件可以在文章开头看到。
有了前面的基础,要移动一个物体算什么难事,我们先上效果图:
具体代码如下:
- <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添加了mousemove
和mouseup
事件,mousemove
事件的触发是在鼠标点击在了ball之后,移动鼠标才触发的,他执行的函数是onMouseMove
。mouseup
是当鼠标抬起是触发的,执行函数是onMouseUp
。
在onMouseMove
函数中我们重新设置了ball的位置。onMouseUp
函数中移除监听事件。
注意,这里有两个变量w
和h
,这两个变量的作用是修正ball的位置的。为什么这样说呢?因为当我们点击球体的时候,不一定点击的就是球体的球心。这时移动物体,如果没有这个修正值,小球就会出现一个快速的偏移,让鼠标位于小球的球心,感觉很别扭。所以,在点击时,我们就把鼠标与球心之间的坐标差值计算出来,在移动的时候给个修正就不会出现快速偏移的现象了。