当前位置: 首页 > 知识库问答 >
问题:

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

简滨海
2023-03-14

我正在努力寻找一种方法/策略来处理存储坐标的绘图,以及我的web应用程序在不同设备和屏幕大小上画布尺寸的变化。

基本上我想在画布上显示一个图像。用户将在图像区域上标记两个点,应用程序记录这些标记的放置位置。其想法是,用户将每隔一天使用该应用程序,能够看到之前绘制的X个点的位置,并能够在之前没有标记的地方添加两个新点。画布当前设置为高度=窗口。内部高度和宽度=窗口。内部宽度/2。

我最初的想法是记录每对点的坐标,并根据需要检索它们,以便重新绘制。但如果画布改变大小,这些坐标就不匹配,正如我在不同设备上测试网页时发现的那样。如何记录之前的坐标并使用它们标记图像的同一区域,而不考虑画布尺寸?

共有2个答案

方嘉志
2023-03-14

您必须定义一个虚拟画布。这是具有预定义大小的理想画布,所有坐标都相对于此画布。此虚拟画布的中心是坐标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.");
}
  • 以上假设画布与屏幕对齐
  • 一些设备将被放大(缩小比例)。您需要检查设备像素比例以获得最佳效果
  • 最好将虚拟画布大小设置为预期的最大屏幕分辨率
  • 始终在虚拟坐标中工作,仅在需要渲染时转换为设备坐标
印晋
2023-03-14

使用百分比!示例:

因此,假设在设备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%

现在你有了百分比,你可以根据这个百分比设置标记的位置
示例:

let markerX = (canvasWidth * percent) / 100; //24.999
canvasWidth = 400; //Lets change the canvas size!
markerX = (canvasWidth * percent) / 100; //66.664;

瞧,只要抓取画布的大小,你就可以每次确定标记的位置。

 类似资料:
  • 我在FrameLayout中的SurfaceView画布上画线。我从相机预览中接收图像,对其进行处理,获取矩形的坐标并在画布上绘制线条。绘制时,我得到这些线在y轴上的位移,线越低,位移越大(见下面的照片): 用红线标记(使用Paint程序,而不是实际的应用程序)下线坐标坐标在位图上的大致位置,矩形的下部绿线(由实际的应用程序放置,在画布上绘制)以及红线显示,坐标移位了多少。顶部的坐标不能穿过屏幕,

  • 我有一个包含坐标的.csv文件,我需要在我做过的地图上使用python绘制它们。有人能帮我吗?

  • 在画布上画画是非常好的。即使橡皮擦也能工作得很好。问题是当画布保存为图像时,它画的是黑线,而不是橡皮擦。 为了更好地理解,我添加了屏幕、镜头和代码。 1.在擦除图的同时- 我不明白为什么橡皮移动被替换为黑色,而保存画布作为一个图像。

  • 因此,我正在创建一个cordova应用程序,在该应用程序中,我从iphone库中拍摄一张照片,将其绘制到画布上,并向其添加另一张图像,以便将其保存为一张照片。到目前为止,我从iphone照片库中绘制的照片可以毫无问题地绘制到画布上,但是第二张图片没有。 当我加载第二张图像时,它首先被添加到具有绝对定位的div中,以便将其移动到我想要的任何位置。之后,我得到了实际的图像,它的来源和位置,并尝试将其绘

  • 实际上,我可以使用函数来完成。我从“HTML5画布-如何在图像背景上画一条线?”中得到的东西。但是我需要在不使用from函数的情况下绘制图像,如下所示:

  • 我有以下代码应该在图像中绘制线条。我的代码是: 如果我编写,它实际上可以工作。但是我的数组很长并且来自一个输入。