Home >Web Front-end >HTML Tutorial >Introduction to event handling of HTML5 Canvas

Introduction to event handling of HTML5 Canvas

不言
不言Original
2018-06-05 10:45:101190browse

This article mainly introduces the event processing of HTML5 Canvas. This article explains the limitations of Canvas, binding events to Canvas elements, isPointInPath method, loop redrawing and event bubbling, etc. Friends who need it can refer to it

DOM is a very important part of the Web front-end field. DOM is used not only when processing HTML elements, but also in graphic programming. For example, in SVG drawing, various graphics are inserted into the page in the form of DOM nodes, which means that graphics can be manipulated using DOM methods. For example, if there is a b0df1a4bdae36d21726841c97f474466 element, you can directly use jquery to add the click event $('#p1').click(function(){...})". However, this DOM processing method is not suitable for HTML5 It is no longer applicable in Canvas. Canvas uses another set of mechanisms. No matter how many graphics are drawn on Canvas, Canvas is a whole. The graphics themselves are actually part of Canvas and cannot be obtained separately, so it cannot be directly given to someone. Graphics add JavaScript events.

Limitations of Canvas

In Canvas, all graphics are drawn on the frame, and the drawing method will not treat the drawn graphic elements as a Even if the return value is output, js cannot obtain the graphic elements that have been drawn. For example:

Copy code

The code is as follows:

cvs = document.getElementById('mycanvas');
ctx = canvas.getContext('2d');
theRect = ctx.rect(10, 10, 100, 100);
ctx.stroke();
console.log(theRect);     //undefined

This code draws a rectangle in the canvas tag. First of all, you can see that the rect method for drawing graphics has no return value. If you open the developer tools of the browser, you can also see that no content is added inside the canvas tag, and in js The canvas element and the current context obtained in it do not have any content indicating the new graphics.

Therefore, the DOM methods commonly used on the front end are not applicable in canvas. For example, click on the rectangle in the Canvas above , the actual click is the entire Canvas element.

Bind events to the Canvas element

Since the event can only reach the Canvas element level, if you want to go further , to identify which graphic inside the Canvas the click occurred on, you need to add code for processing. The basic idea is: bind an event to the Canvas element, when the event occurs, check the position of the event object, and then check which graphics cover the Position. For example, in the above example, a rectangle was drawn, which covers the range of 10-110 on the x-axis and 10-110 on the y-axis. As long as the mouse clicks within this range, it can be regarded as clicking on the rectangle, and you can manually The click event that needs to be processed by triggering the rectangle. The idea is actually relatively simple, but the implementation is still a little complicated. Not only must the efficiency of this judgment process be considered, but also the event type needs to be re-judged in some places, and the capture and risk settings inside the Canvas must be redefined. Bubble mechanism.

The first thing to do is to bind events to the Canvas element. For example, if you want to bind a click event to a graphic inside the Canvas, you need to proxy the event through the Canvas element:

Copy code

The code is as follows:

cvs = document.getElementById('mycanvas');
cvs.addEventListener('click', function(e){
  //...
}, false);

Next, you need to determine the location where the event object occurs. The layerX and layerY attributes of the event object e represent the inside of the Canvas Coordinates in a coordinate system. However, Opera does not support this attribute, and Safari plans to remove it, so we need to make some compatible writing methods:

Copy code

The code is as follows:

function getEventPosition(ev){
  var x, y;
  if (ev.layerX || ev.layerX == 0) {
    x = ev.layerX;
    y = ev.layerY;
  } else if (ev.offsetX || ev.offsetX == 0) { // Opera
    x = ev.offsetX;
    y = ev.offsetY;
  }
  return {x: x, y: y};
}

//Note: To use the above function, you need to set the position of the Canvas element to absolute.

Now that we have the coordinate position of the event object, we need to determine which graphics in the Canvas cover this coordinate.

isPointInPath method

The isPointInPath method of Canvas can determine whether the current context graphics covers a certain coordinate, such as:

Copy code

The code is as follows:

cvs = document.getElementById('mycanvas');
ctx = canvas.getContext('2d');
ctx.rect(10, 10, 100, 100);
ctx.stroke();
ctx.isPointInPath(50, 50);     //true
ctx.isPointInPath(5, 5);     //false

Next, add an event judgment to determine whether a click event occurs on the rectangle:

Copy code

The code is as follows:

cvs.addEventListener('click', function(e){
 p = getEventPosition(e);
 if(ctx.isPointInPath(p.x, p.y)){
   //点击了矩形
 }
}, false);

The above is the basic method of handling Canvas events, but the above code still has limitations, due to the isPointInPath method Only the path in the current context is judged, so when multiple graphics have been drawn in the Canvas, the event can only be judged based on the context of the last graphic, for example:

Copy code

The code is as follows:

cvs = document.getElementById('mycanvas');
ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.rect(10, 10, 100, 100);
ctx.stroke();
ctx.isPointInPath(20, 20);     //true
ctx.beginPath();
ctx.rect(110, 110, 100, 100);
ctx.stroke();
ctx.isPointInPath(150, 150);     //true
ctx.isPointInPath(20, 20);     //false

As you can see from the above code, the isPointInPath method can only identify the graphics path in the current context environment, and the previously drawn path, There is no way to judge retrospectively. The solution to this problem is: when a click event occurs, redraw all graphics, and use the isPointInPath method for each drawing to determine whether the event coordinates are within the coverage of the graphics.

Loop redrawing and event bubbling

In order to achieve loop redrawing, the basic parameters of the graphics must be saved in advance:

复制代码

代码如下:

arr = [
 {x:10, y:10, width:100, height:100},
 {x:110, y:110, width:100, height:100}
];

cvs = document.getElementById('mycanvas');
ctx = canvas.getContext('2d');

draw();

function draw(){
 ctx.clearRech(0, 0, cvs.width, cvs.height);
 arr.forEach(function(v){
   ctx.beginPath();
   ctx.rect(v.x, v.y, v.width, v.height);
   ctx.stroke();
 });
}

上面的代码事先将两个矩形的基本参数保存下来,每次调用draw方法,就会循环调用这些基本参数,用于绘制两个矩形。这里还使用了clearRect方法,用于在重绘时清空画布。接下来要做的是增加事件代理,以及在重绘时对每一个上下文环境使用isPointInPath方法:

复制代码

代码如下:

cvs.addEventListener('click', function(e){
 p = getEventPosition(e);
 draw(p);
}, false);

事件发生时,将事件对象的坐标传给draw方法处理。这里还需要对draw方法做一些小改动:

复制代码

代码如下:

function draw(p){
 var who = [];
 ctx.clearRech(0, 0, cvs.width, cvs.height);
 arr.forEach(function(v, i){
   ctx.beginPath();
   ctx.rect(v.x, v.y, v.width, v.height);
   ctx.stroke();
   if(p && ctx.isPointInPath(p.x, p.y)){
     //如果传入了事件坐标,就用isPointInPath判断一下
     //如果当前环境覆盖了该坐标,就将当前环境的index值放到数组里
     who.push(i);
   }
 });
 //根据数组中的index值,可以到arr数组中找到相应的元素。
 return who;
}

在上面代码中,点击事件发生时draw方法会执行一次重绘,并在重绘过程中检查每一个图形是否覆盖了事件坐标,如果判断为真,则视为点击了该图形,并将该图形的index值放入数组,最后将数组作为draw方法的返回值。在这种处理机制下,如果Canvas里有N个图形,它们有一部分是重叠的,而点击事件恰巧发生在这个重叠区域上,那么draw方法的返回数组里会有N个成员。这时就有点类似事件冒泡的情况,数组的最后一个成员处于Canvas最上层,而第一个成员则在最下层,我们可以视为最上层的成员是e.target,而其他成员则是冒泡过程中传递到的节点。当然这只是最简单的一种处理方法,如果真要模拟DOM处理,还要给图形设置父子级关系。

以上就是Canvas事件处理的基本方法。在实际运用时,如何缓存图形参数,如何进行循环重绘,以及如何处理事件冒泡,都还需要根据实际情况花一些心思去处理。另外,click是一个比较好处理的事件,相对麻烦的是mouseover、mouseout和mousemove这些事件,由于鼠标一旦进入Canvas元素,始终发生的都是mousemove事件,所以如果要给某个图形单独设置mouseover或mouseout,还需要记录鼠标移动的路线,给图形设置进出状态。由于处理的步骤变得复杂起来,必须对性能问题提高关注。

The above is the detailed content of Introduction to event handling of HTML5 Canvas. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn