我正在努力寻找一种方法/策略来处理带有存储的坐标的图形以及Web应用程序在各种设备和屏幕尺寸上画布尺寸的变化。
基本上我想在画布上显示图像。用户将在图像区域上标记两个点,并且应用程序会记录这些标记的放置位置。这个想法是,用户将在每个奇异的日子使用该应用程序,能够看到绘制了X数量的先前点,并且能够将两个新的先前点添加到上述区域中,而先前标记尚未标记的地方。该画布当前设置为height = window.innerHeight和width = window.innerWidth / 2。
我最初的想法是记录每对点的坐标,并根据需要检索它们,以便可以重画它们。但是,如果画布更改尺寸,这些坐标将不匹配,正如我在其他设备上测试网页时所发现的那样。无论画布尺寸如何,如何记录先前的坐标并使用它们来标记图像的相同区域?
答案 0 :(得分:1)
使用百分比!示例:
因此,假设设备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只需抓住画布的大小,就可以每次确定标记的位置。
答案 1 :(得分:0)
您必须定义一个虚拟画布。这是具有预定义大小的理想画布,所有坐标都相对于此画布。该虚拟画布的中心是坐标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);
要将点从设备坐标转换为虚拟坐标,您需要将逆矩阵应用于该点。例如,您可以从鼠标中获取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.");
}