虚拟画布
您必须定义一个虚拟画布。这是具有预定义大小的理想画布,所有坐标都相对于该画布。这个虚拟画布的中心是坐标 0,0
输入坐标后,它会转换为虚拟坐标并存储。渲染时,它们会转换为设备屏幕坐标。
不同的设备具有不同的纵横比,即使是单个设备也可以通过倾斜来改变纵横比。这意味着虚拟画布不会完全适合所有设备。您可以做的最好的事情是确保整个虚拟画布是可见的,而无需在 x 或 y 方向上拉伸它。这称为适合的比例。
缩放以适应
要渲染到设备画布,您需要缩放坐标以适应整个虚拟画布。您使用画布变换来应用缩放。
创建设备比例矩阵
const vWidth = 1920; // virtual canvas size
const vHeight = 1080;
function scaleToFitMatrix(dWidth, dHeight) {
const scale = Math.min(dWidth / vWidth, dHeight / vHeight);
return [scale, 0, 0, scale, dWidth / 2, dHeight / 2];
}
const scaleMatrix = scaleToFitMatrix(innerWidth, innerHeight);
缩放位置不是像素
点被定义为虚拟画布上的一个位置。然而,转换也会缩放线宽和特征尺寸,这是您在非常低或高分辨率的设备上不想要的。
要保持相同的像素大小,但仍以像素大小呈现特征,请使用反向比例,并在描边之前重置变换,如下所示(以点为中心的 4 个像素框)
const point = {x : 0, y : 0}; // center of virtual canvas
const point1 = {x : -vWidth / 2, y : -vHeight / 2}; // top left of virtual canvas
const point2 = {x : vWidth / 2, y : vHeight / 2}; // bottom right of virtual canvas
function drawPoint(ctx, matrix, vX, vY, pW, pH) { // vX, vY virtual coordinate
const invScale = 1 / matrix[0]; // to scale to pixel size
ctx.setTransform(...matrix);
ctx.lineWidth = 1; // width of line
ctx.beginPath();
ctx.rect(vX - pW * 0.5 * invScale, vY - pH * 0.5 * invScale, pW * invScale, pH * invScale);
ctx.setTransform(1,0,0,1,0,0); // reset transform for line width to be correct
ctx.fill();
ctx.stroke();
}
const ctx = canvas.getContext("2d");
drawPoint(ctx, scaleMatrix, point.x, point.y, 4, 4);
通过 CPU 转换
要将点从设备坐标转换为虚拟坐标,您需要将逆矩阵应用于该点。例如,您从鼠标获取 pageX、pageY 坐标,您使用比例矩阵进行转换,如下所示
function pointToVirtual(matrix, point) {
point.x = (point.x - matrix[4]) / matrix[0];
point.y = (point.y - matrix[5]) / matrix[3];
return point;
}
从虚拟转换为设备
function virtualToPoint(matrix, point) {
point.x = (point.x * matrix[0]) + matrix[4];
point.y = (point.y * matrix[3]) + matrix[5];
return point;
}
检查边界
画布的上方/下方或左/右可能存在虚拟画布坐标之外的区域。要检查虚拟画布内部是否调用以下代码
function isInVritual(vPoint) {
return ! (vPoint.x < -vWidth / 2 ||
vPoint.y < -vHeight / 2 ||
vPoint.x >= vWidth / 2 ||
vPoint.y >= vHeight / 2);
}
const dPoint = {x: page.x, y: page.y}; // coordinate in device coords
if (isInVirtual(pointToVirtual(scaleMatrix,dPoint))) {
console.log("Point inside");
} else {
console.log("Point out of bounds.");
}
加分
- 以上假设画布与屏幕对齐。
- 某些设备将被缩放(捏合缩放)。您需要检查设备像素比例以获得最佳效果。
- 最好将虚拟画布大小设置为您期望的最大屏幕分辨率。
- 始终在虚拟坐标中工作,仅在需要渲染时转换为设备坐标。