canvas绘图总结——拾取、Text的特殊处理方式

在实现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();
发布了28 篇原创文章 · 获赞 4 · 访问量 7397

猜你喜欢

转载自blog.csdn.net/JuicyActiveGilbert/article/details/90229961