我正在努力寻找一种方法/策略来处理使用存储坐标的绘图以及我的网络应用程序的各种设备和屏幕尺寸的画布尺寸的变化。
基本上我想在画布上显示图像。用户将在图像区域上标记两个点,并且应用程序记录这些标记的放置位置。这个想法是用户将在每个奇数日使用该应用程序,能够看到绘制先前点数的X数量,并且能够将两个新的数据添加到前面标记未标记的区域中提到的区域。画布当前设置为height = window.innerHeight和width = window.innerWidth / 2。
我最初的想法是记录每对点的坐标并根据需要检索它们,以便重绘它们。但是如果画布改变大小,这些坐标不匹配,就像我在不同设备上测试网页时发现的那样。如何记录先前的坐标并使用它们来标记图像的相同区域,而不管画布尺寸如何?
使用百分比!例:
因此,在设备1上说,画布大小是150x200
,
用户将标记放在像素25x30上。你可以做一些数学来获得百分比。
然后你节省了这个百分比,而不是位置,
例:
let userX = 25; //where the user placed a marker
let canvasWidth = 150;
//Use a calculator to verify :D
let percent = 100 / (canvasWidth / userX); //16.666%
现在您有了percent
,您可以根据该百分比设置标记的位置。
例:
let markerX = (canvasWidth * percent) / 100; //24.999
canvasWidth = 400; //Lets change the canvas size!
markerX = (canvasWidth * percent) / 100; //66.664;
瞧:D只是抓住画布尺寸,你可以每次确定标记的位置。
您必须定义虚拟画布。这是具有预定义大小的理想画布,所有坐标都相对于此画布。该虚拟画布的中心是坐标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);
Point被定义为虚拟画布上的位置。但是,变换还将缩放线宽和特征尺寸,这些尺寸在极低或高分辨率设备上是您不需要的。
要保持相同像素大小但仍以像素大小呈现要素,请使用反比例,并在笔划之前重置变换,如下所示(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);
要将点从设备坐标转换为虚拟坐标,您需要将逆矩阵应用于该点。例如,您从鼠标获取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.");
}