繁体   English   中英

如何在 Html5 中将矩形坐标从一个画布传输到另一个画布?

[英]How to transfer rectangle coordinates from one canvas to another in Html5?

我正在使用一些代码将画布矩形的坐标动态传输另一个画布,以在 canvas2 上的确切位置绘制矩形。

我经历了不同的类似问题和解决方案,例如:stackoverflow 上的link1link2 ,但在我的情况下,它们都不起作用。

sendimg()函数正在将矩形坐标从 canvas1 传输到 canvas2。 由于 canvas2 图像被缩放到大尺寸,矩形没有在正确的位置绘制。

请检查附加的代码段或codepen 链接以获取详细信息。

 var canvas = document.getElementById("canvas1"); var canvas2 = document.getElementById("canvas2"); var ctx = canvas.getContext("2d"); var ctx2 = canvas2.getContext("2d"); var img = new Image(); var rect = {}; var scale = 0; var scale2 = 0; var x = 0; var y = 0; var x2 = 0; var y2 = 0; var drag = false; img.onload = function () { //Setting dpi for canvas1 var dpi = window.devicePixelRatio || 1; canvas.setAttribute('width', canvas.clientWidth * dpi); canvas.setAttribute('height', canvas.clientHeight * dpi); //Setting dpi for canvas2 var dpi = window.devicePixelRatio || 1; canvas2.setAttribute('width', canvas2.clientWidth * dpi); canvas2.setAttribute('height', canvas2.clientHeight * dpi); ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); ctx.save(); ctx2.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); ctx2.save(); //fitting image to canvas fill scale = Math.max(canvas.clientWidth / img.width, canvas.clientHeight / img.height); //canvas1 scale scale2 = Math.max(canvas2.clientWidth / img.width, canvas2.clientHeight / img.height); //canvas2 scale x = (canvas.clientWidth / 2) - (img.width / 2) * scale; //canvas1 x y = (canvas.clientHeight / 2) - (img.height / 2) * scale; //canvas1 y x2 = (canvas2.clientWidth / 2) - (img.width / 2) * scale2; //canvas2 x y2 = (canvas2.clientHeight / 2) - (img.height / 2) * scale2; //canvas2 y ctx.drawImage(img, x, y, img.width * scale, img.height * scale); ctx2.drawImage(img, x2, y2, img.width * scale2, img.height * scale2); canvas.addEventListener('mousedown', mouseDown, false); canvas.addEventListener('mouseup', mouseUp, false); canvas.addEventListener('mousemove', mouseMove, false); } img.crossOrigin = "Anonymous"; img.src = 'https://i.imgur.com/1n8sbrF.jpg'; function mouseDown(e) { rect.startX = e.clientX - this.offsetLeft; rect.startY = e.clientY - this.offsetTop; drag = true; } function mouseUp() { drag = false; console.log(rect); } function mouseMove(e) { if (drag) { ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); ctx.save(); ctx.drawImage(img, x, y, img.width * scale, img.height * scale); rect.w = (e.clientX - this.offsetLeft) - rect.startX; rect.h = (e.clientY - this.offsetTop) - rect.startY; ctx.lineWidth = 2; ctx.strokeStyle = 'red'; ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h); } } function sendimg() { if(rect.startX === undefined) { alert("draw any rectangle first on canvas1"); return false; } ctx2.lineWidth = 2; ctx2.strokeStyle = "yellow"; //Following code is not drawing rectangle on correct position ctx2.strokeRect(rect.startX * scale2 + x2 , rect.startY* scale2 + y2, rect.w * scale2 , rect.h * scale2); }
 html, body{ width: 90%; height: 90%; } #div1 { margin: 10px; width: 800px; height: 600px; border: 2px solid red; } #div2 { position: absolute; top: 20px; left: 900px; margin: 10px; width: 1200px; height: 800px; border: 2px solid red; } canvas { width: 100%; height: 100%; }
 <button type="button" onclick="sendimg();">Send Rectangle on Canvas2</button> <div id="div1"> <canvas id="canvas1"></canvas> </div> <div id="div2"> <canvas id="canvas2"></canvas> </div>

一些大问题和小问题

  • 重要当您调用ctx.save您将当前的 2D 画布状态推送到堆栈。 如果您不调用ctx.restore ,则调用ctx.save毫无意义。 更糟糕的是,每次调用 save 都在消耗内存。
  • window是全局 this。 你不需要使用它。 例如window.devicePixelRatiodevicePixelRatio相同
  • 您不需要使用setAttribute 仅当您希望标记反映属性状态并且该属性未由 DOM 定义时才使用setAttribute
  • 当您设置画布widthheight ,画布将被清除,状态将重置为默认值。 设置大小后无需清除画布,也无需保存其状态。
  • 简化数学。 当您计算原点时,您会得到类似(a / 2) - (b / 2) * c的结果。 它有一个公约数2 ,因此变成(a - b) * c / 2

坐标系

使用公共坐标系并在渲染到任一画布时进行转换。 由于两个画布渲染相同的图像(并且它接缝是相关坐标系的图像)使用图像坐标系。

您有 4 个坐标系,图像、缩放图像 canvas1、缩放图像 canvas2 和作为 canvas1 像素的rect 问题是您正在从 canvas1 像素坐标( rect )转换为 canvas2 图像坐标

要修复缩放和定位,请将 canvas1 像素坐标转换为图像坐标(请参阅示例函数mouseEvents )。 然后在您的渲染函数中将图像坐标转换回画布像素坐标(参见示例函数updateCanvas )。

例子

我删除了很多不需要和重复的代码,以降低复杂性并使代码更具可读性和可维护性。 使用现代 JS 并添加了一些 UI

  • 设置画布的单个函数
  • 一个处理鼠标事件的函数
  • 渲染矩形的单个函数
  • 使用光标“十字准线”和“无”提供反馈

例如,我删除了按钮(与问题无关),减小了两个画布的大小以适应非常小的片段窗口,并在绘制第一个矩形时实时更新第二个矩形。

请注意, rect在图像坐标中。

 const COLOR1 = "#F00", COLOR2 = "#FF0", LINE_WIDTH = 2; const ctx1 = canvas1.getContext("2d"), ctx2 = canvas2.getContext("2d"); const img = new Image; const rect = {}; var drag = false; img.src = "https://i.imgur.com/1n8sbrF.jpg"; img.addEventListener("load",() => { setupCanvas(ctx1); setupCanvas(ctx2); canvas1.addEventListener("mousedown", mouseEvent); canvas1.addEventListener("mouseup", mouseEvent); canvas1.addEventListener("mousemove", mouseEvent); }, {once: true}); function setupCanvas(ctx, coords = {}) { const dPR = devicePixelRatio || 1; const w = ctx.canvas.width = ctx.canvas.clientWidth * dPR; const h = ctx.canvas.height = ctx.canvas.clientHeight * dPR; const scale = coords.scale = Math.max(w / img.width, h / img.height); const x = coords.x = (w - img.width * scale) / 2; const y = coords.y = (h - img.height * scale) / 2; ctx.drawImage(img, x, y, img.width * scale, img.height * scale); ctx.canvas.coords = coords; } function mouseEvent(e) { var cursor = "crosshair"; const co = canvas1.coords; const x = (e.clientX - this.offsetLeft - co.x) / co.scale; const y = (e.clientY - this.offsetTop - co.y) / co.scale; if (e.type === "mousedown") { rect.x = x; rect.y = y; drag = true; canvas1.title = ""; } else if (e.type === "mouseup") { drag = false } if (drag) { cursor = "none"; rect.w = x - rect.x; rect.h = y - rect.y; updateCanvas(ctx1, COLOR1, co); updateCanvas(ctx2, COLOR2, canvas2.coords); } canvas1.style.cursor = cursor; } function updateCanvas(ctx, color, {x, y, scale}) { ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.drawImage(img, x, y, img.width * scale, img.height * scale); ctx.lineWidth = LINE_WIDTH; ctx.strokeStyle = color; ctx.strokeRect(x + rect.x * scale, y + rect.y * scale, rect.w * scale, rect.h * scale); }
 html, body{ width: 90%; height: 90%; } #div1 { margin: 10px; width: 200px; height: 300px; border: 2px solid red; } #div2 { position: absolute; top: 10px; left: 240px; width: 300px; height: 200px; border: 2px solid #FF0; } canvas { width: 100%; height: 100%; }
 <div id="div1"> <canvas id="canvas1" title="Click and drag to select area."></canvas> </div> <div id="div2"> <canvas id="canvas2"></canvas> </div>

在应用第二个画布的转换和缩放因子之前,您需要反转第一个画布的缩放和平移。 这就像那里的 90%,Y 轴是正确的,但 X 轴平移错误。 查看我对ctx2.strokeRect调用以及它如何反转缩放和平移。 我的代码可能被劫持,因为画布原点在左上角,而平移和缩放公式假定它在左下角。

编辑:修复了代码 - 现在应该是正确的。

 var canvas = document.getElementById("canvas1"); var canvas2 = document.getElementById("canvas2"); var ctx = canvas.getContext("2d"); var ctx2 = canvas2.getContext("2d"); var img = new Image(); var rect = {}; var scale = 0; var scale2 = 0; var x = 0; var y = 0; var x2 = 0; var y2 = 0; var drag = false; img.onload = function () { //Setting dpi for canvas1 var dpi = window.devicePixelRatio || 1; canvas.setAttribute('width', canvas.clientWidth * dpi); canvas.setAttribute('height', canvas.clientHeight * dpi); //Setting dpi for canvas2 var dpi = window.devicePixelRatio || 1; canvas2.setAttribute('width', canvas2.clientWidth * dpi); canvas2.setAttribute('height', canvas2.clientHeight * dpi); ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); ctx.save(); ctx2.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); ctx2.save(); //fitting image to canvas fill scale = Math.max(canvas.clientWidth / img.width, canvas.clientHeight / img.height); //canvas1 scale scale2 = Math.max(canvas2.clientWidth / img.width, canvas2.clientHeight / img.height); //canvas2 scale x = (canvas.clientWidth / 2) - (img.width / 2) * scale; //canvas1 x y = (canvas.clientHeight / 2) - (img.height / 2) * scale; //canvas1 y x2 = (canvas2.clientWidth / 2) - (img.width / 2) * scale2; //canvas2 x y2 = (canvas2.clientHeight / 2) - (img.height / 2) * scale2; //canvas2 y ctx.drawImage(img, x, y, img.width * scale, img.height * scale); ctx2.drawImage(img, x2, y2, img.width * scale2, img.height * scale2); canvas.addEventListener('mousedown', mouseDown, false); canvas.addEventListener('mouseup', mouseUp, false); canvas.addEventListener('mousemove', mouseMove, false); console.log(scale) console.log(scale2) } img.crossOrigin = "Anonymous"; img.src = 'https://i.imgur.com/1n8sbrF.jpg'; function mouseDown(e) { rect.startX = e.clientX - this.offsetLeft; rect.startY = e.clientY - this.offsetTop; drag = true; } function mouseUp() { drag = false; console.log(rect); } function mouseMove(e) { if (drag) { ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight); ctx.save(); ctx.drawImage(img, x, y, img.width * scale, img.height * scale); rect.w = (e.clientX - this.offsetLeft) - rect.startX; rect.h = (e.clientY - this.offsetTop) - rect.startY; ctx.lineWidth = 2; ctx.strokeStyle = 'red'; ctx.strokeRect(rect.startX, rect.startY, rect.w, rect.h); } } function sendimg() { if(rect.startX === undefined) { alert("draw any rectangle first on canvas1"); return false; } ctx2.lineWidth = 2; ctx2.strokeStyle = "yellow"; //Following code is not drawing rectangle on correct position ctx2.strokeRect( ((rect.startX - x )/scale) * scale2 + x2, ((rect.startY - y )/scale) * scale2 + y2, (rect.w/scale) * (scale2), (rect.h/scale) * (scale2)); }
 html, body{ width: 90%; height: 90%; } #div1 { margin: 10px; width: 800px; height: 600px; border: 2px solid red; } #div2 { position: absolute; top: 20px; left: 900px; margin: 10px; width: 1200px; height: 800px; border: 2px solid red; } canvas { width: 100%; height: 100%; }
 <button type="button" onclick="sendimg();">Send Rectangle on Canvas2</button> <div id="div1"> <canvas id="canvas1"></canvas> </div> <div id="div2"> <canvas id="canvas2"></canvas> </div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM