在实现canvas双缓冲绘图、拖动以及缩放后,又遇到新的问题,那就是如何实现拾取绘制的图形?
canvas的说明文档中写有context自带有判断坐标是否在绘制路径上或者在图形中的方法,如下:
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
ctx.rect(20,20,150,100);
ctx.stroke();
ctx.isPointInPath(20,50);//判断点是否在绘制区域中
ctx.isPointInStroke(20,50);//判断是否在绘制线上
但是如此判断有个问题,context只会在最后一次绘制的图形中进行判断,也就是如果我们绘制多个图形,它只会判断是否拾取到最后一个图形。这与我们预想不符,如何解决这个问题呢?
解决思路:在绘制多个图形时,每绘制一个图形就进行一次判断,判断是否拾取到该图形。
为了节约计算资源,设置一个bool变量来判断已经拾取到了图形。示例代码如下:
var pickup = {
id: "",
text: ""
};
var canvas = document.getElementById("canvas");
canvas.width = 500;
canvas.height = 500;
var ctx = canvas.getContext("2d");
updateGraphic({
x: -100,
y: -100
})
canvas.addEventListener('mousemove', function(event) {
var bbox = canvas.getBoundingClientRect();
//使坐标的原点位于canvas的左上角计算新的坐标
let x = parseFloat((event.clientX - bbox.left * (canvas.width / bbox.width)).toFixed(2)); //
let y = parseFloat((event.clientY - bbox.top * (canvas.height / bbox.height)).toFixed(2));
updateGraphic({
x: x,
y: y
});
});
function updateGraphic(point) {
ctx.clearRect(0, 0, 500, 500); //清空canvas
var isPickup = false;
ctx.beginPath();//开始新的路径
ctx.rect(20, 20, 150, 100);
ctx.stroke();
if (!isPickup && ctx.isPointInPath(point.x, point.y)) {
pickup.id = "1";
pickup.text = "拾取到矩形";
isPickup = true;
console.log(pickup)
} else {
pickup = {
id: "",
text: ""
};
}
ctx.closePath();//关闭路径
ctx.beginPath()
ctx.moveTo(20, 200);
ctx.lineTo(50, 300);
ctx.stroke();
if (!isPickup && ctx.isPointInStroke(point.x, point.y)) {
pickup.id = "2";
pickup.text = "拾取到线";
isPickup = true;
console.log(pickup)
} else {
pickup = {
id: "",
text: ""
};
}
ctx.closePath();
}
随着鼠标的移动就会进行图形的刷新与判断是否拾取到图形。
特例:Text的拾取
在canvas绘图中几乎所有绘制的图形都可以拾取,但唯有一个不行,那就是Text。字符串在绘制完成后并不能通过上述的两个方法进行判断,但是实际工程中依然会遇到拾取字符串的需求。那么如何解决?
解决思路:在字符串上蒙一层透明的矩形,拾取矩形作为判断是否拾取到text的判断依据。
示例代码如下:
ctx.beginPath();
ctx.fillText("拾取测试", 50, 350);
if (!isPickup) {
ctx.save(); //保存绘制的参数颜色/字体等
ctx.strokeStyle = "transparent";
let w = ctx.measureText("拾取测试").width;
let h = 12; //默认字体大小
let x = 50
let y = 350 - 12 / 2; //居中判断
ctx.rect(x, y, w, h);
ctx.stroke();
if (ctx.isPointInStroke(point.x, point.y)) {
pickup.id = "3";
pickup.text = "拾取字符串";
isPickup = true;
console.log(pickup)
} else {
pickup = {
id: "",
text: ""
};
}
ctx.restore(); //还原绘制参数以免透明属性对整体造成影响
} else {
pickup = {
id: "",
text: ""
};
}
ctx.closePath();