传统的DOM的事件处理机制在Canvas里并不适应.Canvas是一个整体,无法给里面的某个图形增加Javascript事件。
由于事件只能达到Canvas元素这一层,所以,如果要识别点击发生在哪一个图形上,需要增加代码进行处理:
- 给Canvas绑定事件
- 事件发生时,检查事件对象的位置
- 检查哪些图形覆盖了该位置
给Canvas元素绑定事件
cvs = document.getElementById('canvas');
cvs.addEventListener('click,function(e){
},false);
判断事件对象发生的位置
事件对象e的.layerX和layerY属性代表Canvas内部坐标系中的坐标。但这个属性可能会被废弃,所以要写兼容方法:
//该函数需要将canvas的position设置为absolute
<canvas id="myCanvas" width="400" height="400" style="border:2px solid #eee;position:absolute"></canvas>
<script>
var cvs = document.getElementById('myCanvas');
cvs.addEventListener('click',function(e){
console.log(getEventPosition(e));
},false);
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};
}
</script>
isPointInPath方法
用来判断当前上下文的图形是否覆盖了某个坐标。
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
事件结合isPointInPath
<canvas id="myCanvas" width="400" height="400" style="border:2px solid #eee;position:absolute"></canvas>
<script>
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
ctx.rect(10,10,100,100);
ctx.stroke();
console.log(ctx.isPointInPath(50,50));
canvas.addEventListener('click',function(e){
console.log(getEventPosition(e));
var p=getEventPosition(e);
console.log(ctx.isPointInPath(p.x,p.y));
},false);
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};
}
</script>
isPointInPath方法仅判断当前上下文环境中的路径,所以当Canvas里已经绘制了多个图形时,仅能以最后一个图形的上下文环境来判断事件
解决方案:当点击事件发生时,重绘所有图形,每绘制一个就使用isPointInPath方法,判断事件坐标是否在该图形覆盖范围内。
循环重绘和事件冒泡
为了实现循环重绘,要先将图形的基本参数先保存下来。
<body>
<canvas id="mycanvas" width="150" height="150"></canvas>
<script type="text/javascript">
arr = [
{x:10, y:10, width:100, height:100},
{x:110, y:110, width:100, height:100}
];
var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext('2d');
function draw(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
arr.forEach(function(v){
ctx.beginPath();
ctx.rect(v.x, v.y, v.width, v.height);
ctx.stroke();
});
}
draw();
</script>
</body>
实现点击后重绘,并判断点击图形
<body>
<canvas id="mycanvas" width="150" height="150"></canvas>
<script type="text/javascript">
arr = [
{x:10, y:10, width:100, height:100},
{x:110, y:110, width:100, height:100}
];
var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext('2d');
canvas.addEventListener('click', function(e){
p = getEventPosition(e);
var who=draw(p);
console.log('in',who);
}, false);
function draw(p){
console.log('click',p);
var who=[];
ctx.clearRect(0, 0, canvas.width, canvas.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)){
who.push(i);
}
});
return who;
}
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};
}
draw();
</script>
</body>