在具有存储坐标的图像上绘制时考虑画布大小差异

时间:2019-03-14 23:43:07

标签: javascript html canvas screen-size

我正在努力寻找一种方法/策略来处理带有存储的坐标的图形以及Web应用程序在各种设备和屏幕尺寸上画布尺寸的变化。

基本上我想在画布上显示图像。用户将在图像区域上标记两个点,并且应用程序会记录这些标记的放置位置。这个想法是,用户将在每个奇异的日子使用该应用程序,能够看到绘制了X数量的先前点,并且能够将两个新的先前点添加到上述区域中,而先前标记尚未标记的地方。该画布当前设置为height = window.innerHeight和width = window.innerWidth / 2。

我最初的想法是记录每对点的坐标,并根据需要检索它们,以便可以重画它们。但是,如果画布更改尺寸,这些坐标将不匹配,正如我在其他设备上测试网页时所发现的那样。无论画布尺寸如何,如何记录先前的坐标并使用它们来标记图像的相同区域?

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);

通过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.");
}

加分

  • 以上假设画布与屏幕对齐。
  • 某些设备将被缩放(缩小比例)。您需要检查设备像素比例以获得最佳结果。
  • 最好将虚拟画布大小设置为所需的最大屏幕分辨率。
  • 始终在虚拟坐标中工作,仅在需要渲染时才转换为设备坐标。