canvas动画包教不包会:用户交互

2018-06-19 15:15 更新

没有用户交互的动画就跟电视上的动画片一样,不管谁看,都是一个样,千年不变。显然,这不是我们想要的,很多时候,我们需要用户参与进来,这样才能产生丰富的动画效果,这就是专门用一章来了解用户交互的原因。


用户交互是基于用户事件的,这些事件通常包括鼠标事件键盘事件以及触摸事件

1、事件绑定和事件取消
在这里,并不会对JavaScript事件做过多的解析,如需详细了解,可看这里:《JavaScript学习笔记整理(10):Event事件》。

由于我们无法获取到canvas上绘制的线和形状,所以只能在canvas上绑定事件,然后根据鼠标相对canvas的位置而确定在哪个线或形状上,比如要为canvas绑定鼠标点击(mousedown)事件:

function MClick(event){

  console.log('鼠标点击了canvas');

};


canvas.addEventListener('mousedown',MClick,false);

当鼠标在canvas上点击时,每次都会在控制台打印出"鼠标点击了canvas"。

我们使用removeEventListener()还可以取消鼠标点击事件:

canvas.removeEventListener('mousedown',MClick,false);

上面的代码就取消了canvas上的鼠标点击事件,不过要注意的是,这里传入的只能是函数名,而不能传入函数体,而且只是取消了鼠标点击事件中的MClick事件处理函数,如果还绑定了其他的鼠标点击事件,依然有效。

2、鼠标事件
鼠标事件有很多种,常见的有下面这些:

mousedown

mouseup

click

dblclick

mousewheel

mousemove

mouseover

mouseout

当为元素注册一个鼠标事件处理函数时,它还会为函数传入一个MouseEvent对象,这个对象包含了多个属性,比如我们接下来要用的pageX和pageY:

pageX 和 pageY 分别是触点相对HTML文档左边沿的X坐标和触点相对HTML文档上边沿的Y坐标。只读属性。 当存在滚动的偏移时,pageX包含了水平滚动的偏移,pageY包含了垂直滚动的偏移。

显然,通过pageX和pageY获取到的只是相对于HTML文档的鼠标位置,并不是我们想要的,我们需要的是相对于canvas的鼠标位置,如何得到呢?

只需用pageX和pageY分别减去canvas元素的左偏移和上偏移距离就可得到相对canvas的鼠标位置:

canvas.addEventListener('mousedown',function(event){

  var x = (event.pageX || event.clientX + document.body.scrollLeft +document.documentElement.scrollLeft) - canvas.offsetLeft;

  var y = (event.pageY || event.clientY + document.body.scrollTop +document.documentElement.scrollTop) - canvas.offsetTop;

},false);

在上面的代码中,还使用了clientX和clientY,这是为了兼容不同的浏览器。

注意:这里的canvas偏移位置是相对HTML文档的。

为了避免后面重复写代码,我们先来造个轮子,创建一个tool.js文件(后续都会用到),在里面创建一个全局对象tool,然后将需要的方法传入进去:

window.tool = {};   

window.tool.captureMouse = function(element,mousedown,mousemove,mouseup){

 

  /*传入Event对象*/

  function getPoint(event){

    event = event || window.event; /*为了兼容IE*/

     /*将当前的鼠标坐标值减去元素的偏移位置,返回鼠标相对于element的坐标值*/

    var x = (event.pageX || event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft);

    x -= element.offsetLeft;

    var y = (event.pageY || event.clientY + document.body.scrollTop + document.documentElement.scrollTop);

    y -= element.offsetTop;

    return {x:x,y:y};

  };

   

  if(!element) return;

  

  /*为element元素绑定mousedown事件*/ 

  element.addEventListener('mousedown',function(event){   

    event.point = getPoint(event);   

    mousedown && mousedown.call(this,event);   

  },false);


  /*为element元素绑定mousemove事件*/   

  element.addEventListener('mousemove',function(event){   

    event.point = getPoint(event);   

    mousemove && mousemove.call(this,event);   

  },false);


  /*为element元素绑定mouseup事件*/   

  element.addEventListener('mouseup',function(event){   

    event.point = getPoint(event);   

    mouseup && mouseup.call(this,event);   

  },false);

   

};

轮子已经造好了,使用方法也很简单:

/*回调函数会传入一个event对象,event.point中包含了x和y属性,分别对应鼠标相对element的X坐标和Y坐标,函数内的this指向绑定元素element*/

function mousedown(event) {   

  console.log(event.point.x,event.ponit.y);   

  console.log(this); 

  document.querySelector('.pointX').innerHTML = event.point.x;   

  document.querySelector('.pointY').innerHTML = event.point.y;

};    


function mousemove(event) {   

  console.log(event.point);  

  document.querySelector('.pointX1').innerHTML = event.point.x;   

  document.querySelector('.pointY1').innerHTML = event.point.y;   

  var x = event.point.x;   

  var y = event.point.y;   

  var radius = 5;   

  /*清除整个canvas画布*/

  ctx.clearRect(0,0,canvas.width,canvas.height);   

  ctx.fillStyle = 'red';   

  ctx.beginPath();

  /*绘制一个跟随鼠标的圆*/   

  ctx.arc(x,y,radius,0,2*Math.PI,true);   

  ctx.fill();   

  ctx.closePath();

};    


function mouseup(event) {   

  console.log(event.point);  

  document.querySelector('.pointX2').innerHTML = event.point.x;   

  document.querySelector('.pointY2').innerHTML = event.point.y;

};

/*传入canvas元素,后面是传入三个函数,分别对应mousedown、mousemove和mouseup事件的事件处理函数*/

tool.captureMouse(canvas, mousedown, mousemove, mouseup);

上面代码中的mousedown、mousemove和mouseup三个方法都是自定义的,三个回调函数都会传入一个event对象(element当前绑定事件的event对象), event.point 是我定义的,它包含了 x 和 y 属性,分别对应鼠标相对element(也就是元素左上角)的X坐标和Y坐标,函数内的 this 指向绑定元素element。

实例(获取鼠标坐标):


3、触摸事件
常用触摸事件:

touchstart

touchmove

touchend

触摸事件和鼠标事件最大的区别在于,触摸有可能会在同一时间有多个触摸点,而鼠标永远都是只有一个触摸点。

要获取触摸点相对canvas的坐标,同样是根据event对象中的pageX和pageY,还有canvas相对于HTML文档的偏移位置来确定,不过由于触摸点可能有多个,它传递给事件处理函数的是TouchEvent对象,使用方法稍微有一点区别:

canvas.addEventListener('touchstart',function(event){

  var touchEvnet = event.changedTouches[0];

  var x = (touchEvent.pageX || touchEvent.clientX + document.body.scrollLeft+ document.documentElement.scrollLeft );

  x -= canvas.offsetLeft;

  

  var y = (touchEvent.pageY || touchEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop );

  y -= canvas.offsetTop;

});

注意上面代码中的 event.changedTouches[0] ,这是获取当前触摸事件引发的所有Touch对象中第一个触摸点的Touch对象,当然还有 event.touches[0] 也可以获取到,它是获取所有仍然处于活动状态的触摸点中的第一个。

对于触摸事件,我们也可以在tool这个轮子里添加一个captureTouch方法,你可以用手机或者打开浏览器控制台模拟手机模式看看这个例子:触摸例子

window.tool.captureTouch = function(element,touchstart,touchmove,touchend){

 

  /*传入Event对象*/

  function getPoint(event){

    event = event || window.event;

    var touchEvent = event.changedTouches[0];

     /*将当前的鼠标坐标值减去元素的偏移位置,返回鼠标相对于element的坐标值*/

    var x = (touchEvent.pageX || touchEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft);

    x -= element.offsetLeft;

    var y = (touchEvent.pageY || touchEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop);

    y -= element.offsetTop;

    return {x:x,y:y};

  };

   

  if(!element) return;

  

  /*为element元素绑定touchstart事件*/ 

  element.addEventListener('touchstart',function(event){   

    event.point = getPoint(event);   

    touchstart && touchstart.call(this,event);   

  },false);


  /*为element元素绑定touchmove事件*/   

  element.addEventListener('touchmove',function(event){   

    event.point = getPoint(event);   

    touchmove && touchmove.call(this,event);   

  },false);


  /*为element元素绑定touchend事件*/   

  element.addEventListener('touchend',function(event){   

    event.point = getPoint(event);   

    touchend && touchend.call(this,event);   

  },false);

   

};


下面我会将鼠标事件和触摸事件结合在一起,添加一个captureMT方法:

window.tool.captureMT = function(element, touchStartEvent, touchMoveEvent, touchEndEvent) {   

  'use strict';   

  var isTouch = ('ontouchend' in document);   

  var touchstart = null;   

  var touchmove = null   

  var touchend = null;   

  if(isTouch){   

    touchstart = 'touchstart';   

    touchmove = 'touchmove';   

    touchend = 'touchend';   

  }else{   

    touchstart = 'mousedown';   

    touchmove = 'mousemove';   

    touchend = 'mouseup';   

  };   

  /*传入Event对象*/   

  function getPoint(event) {   

    /*将当前的触摸点坐标值减去元素的偏移位置,返回触摸点相对于element的坐标值*/     event = event || window.event;

    var touchEvent = isTouch ? event.changedTouches[0]:event;

    var x = (touchEvent.pageX || touchEvent.clientX + document.body.scrollLeft + document.documentElement.scrollLeft);   

    x -= element.offsetLeft;   

    

    var y = (touchEvent.pageY || touchEvent.clientY + document.body.scrollTop + document.documentElement.scrollTop);   

    y -= element.offsetTop;   

    return {x: x,y: y};

  };

  if(!element) return;   

  /*为element元素绑定touchstart事件*/   

  element.addEventListener(touchstart, function(event) {   

    event.point = getPoint(event);   

    touchStartEvent && touchStartEvent.call(this, event);   

  }, false);    


  /*为element元素绑定touchmove事件*/   

  element.addEventListener(touchmove, function(event) {   

    event.point = getPoint(event);   

    touchMoveEvent && touchMoveEvent.call(this, event);   

  }, false);    


  /*为element元素绑定touchend事件*/   

  element.addEventListener(touchend, function(event) {   

    event.point = getPoint(event);   

    touchEndEvent && touchEndEvent.call(this, event);   

  }, false);   

};


在上面的代码中,我们先检测是移动还是PC('ontouchend' in document),然后将布尔值传给变量 isTouch ,然后定义使用mouse事件还是touch事件,获取坐标点时,也是根据 isTouch的值来绝对直接用 event还是用 event.changedTouches[0]


实例(移动PC都可以使用):



4、键盘事件


键盘事件只有三个:

keydown  按下键盘时触发该事件。

keyup   松开键盘时触发该事件。

keypress  只要按下的键并非Ctrl、Alt、Shift和Meta,就接着触发keypress事件(较少用到)。

只要用户一直按键不松开,就会连续触发键盘事件,触发顺序如下:

keydown  

keypress  

keydown  

keypress  

(重复以上过程)  

keyup

在这里,我们只来了解keydown和keyup就足够了。

我们会将事件绑定到window上,而不是canvas上,因为如果将键盘事件绑定到某个元素上,那么该元素只有在获取到焦点时才会触发键盘事件,而绑定到window上时,我们可以任何时候监听到。


一般来说,我们比较关心键盘上的箭头按钮(上下左右):

function keyEvent(event){   

  switch (event.keyCode){   

    case 38:   

      keybox.innerHTML = '你点击了向上箭头(↑)';   

      break;   

    case 40:   

      keybox.innerHTML = '你点击了向下箭头(↓)';   

      break;   

    case 37:   

      keybox.innerHTML = '你点击了向左箭头(←)';   

      break;   

    case 39:   

      keybox.innerHTML = '你点击了向右箭头(→)';   

      break;   

    default:   

      keybox.innerHTML = '你点击了其他按钮';   

  };   

};   

window.addEventListener('keydown',keyEvent,false);

为了便利,我将keydown和keyup事件封装成这样:

window.tool.captureKeyDown = function(params) {   

  function keyEvent(event) {   

    params[event.keyCode]();   

  };   

  window.addEventListener('keydown', keyEvent, false);  

};


window.tool.captureKeyUp = function(params) {

  function keyEvent(event) {

   params[event.keyCode]();   

  };   

  window.addEventListener('keyup', keyEvent, false);  

};


需要时只需如下调用:

function keyLeft(){   

  keybox.innerHTML = '你点击了向左箭头(←)';

};

function keyRight(){   

  keybox.innerHTML = '你点击了向右箭头(→)'; 

};

window.tool.captureKeyEvent({"37":keyLeft,"39":keyRight});

传入一个键值对形式的对象,键名是键码,键值是调用函数。


实例(先点击下面,让其获取到焦点):




在后面会有个附录,有完整的键码值对应表。不过,我们不需要去死记硬背,你可以使用到再去查或者使用插件 keycode.js(可到这里下载:https://github.com/lamberta/html5-animation):

<script src="keycode.js"></script>


<script>

function keyEvent(event){   

  switch (event.keyCode){   

    case keycode.UP:   

      keybox.innerHTML = '你点击了向上箭头(↑)';   

      break;                 

  };   

};   

window.addEventListener('keydown',keyEvent,false);

</script>

其实keycode.js里定义了一个全局变量keycode,然后以键值对的形式定义键名和键名值。


总结

用户交互在游戏动画中是很重要的一步,所以掌握用户交互的各种事件是必须的,而且特别强调一点是,要学会制造轮子,避免重复的编写相同的代码,不过,初学者建议多敲 。


如有问题,欢迎指正!


附录:






以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号