简体   繁体   English

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

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

I'm using some codes to dynamically transfer co-ordinates of a rectangle on canvas to the another canvas to draw rectangle on the exact position on canvas2.我正在使用一些代码将画布矩形的坐标动态传输另一个画布,以在 canvas2 上的确切位置绘制矩形。

I've gone through different similar questions and solutions like: link1 and link2 on stackoverflow, but none of them is working in my case.我经历了不同的类似问题和解决方案,例如:stackoverflow 上的link1link2 ,但在我的情况下,它们都不起作用。

sendimg() function is transferring rectangle co-ordinates from canvas1 to the canvas2. sendimg()函数正在将矩形坐标从 canvas1 传输到 canvas2。 As the canvas2 image is scaled to large size, rectangles are not drawing on the correct position.由于 canvas2 图像被缩放到大尺寸,矩形没有在正确的位置绘制。

Please check the attached snippet or codepen link for detail.请检查附加的代码段或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>

Some major and minor issues一些大问题和小问题

  • IMPORTANT When you call ctx.save you push the current 2D canvas state to a stack.重要当您调用ctx.save您将当前的 2D 画布状态推送到堆栈。 If you don't call ctx.restore there is no point calling ctx.save .如果您不调用ctx.restore ,则调用ctx.save毫无意义。 Worse is that every call to save is chewing up memory.更糟糕的是,每次调用 save 都在消耗内存。
  • window is the global this. window是全局 this。 You dont need to use it.你不需要使用它。 eg window.devicePixelRatio is the same as devicePixelRatio例如window.devicePixelRatiodevicePixelRatio相同
  • You don't need to use setAttribute .您不需要使用setAttribute Only use setAttribute when you want the Markup to reflect a properties state and the property is not defined by the DOM.仅当您希望标记反映属性状态并且该属性未由 DOM 定义时才使用setAttribute
  • When you set the canvas width or height the canvas is cleared and the state is reset to default.当您设置画布widthheight ,画布将被清除,状态将重置为默认值。 There is no need to clear the canvas after setting its size nor to save its state.设置大小后无需清除画布,也无需保存其状态。
  • Simplify the math.简化数学。 You have something like (a / 2) - (b / 2) * c when you calculate the origin.当您计算原点时,您会得到类似(a / 2) - (b / 2) * c的结果。 It has a common divisor 2 and thus becomes (a - b) * c / 2它有一个公约数2 ,因此变成(a - b) * c / 2

Coordinate system坐标系

Use a common coordinate system and convert when rendering to either canvas.使用公共坐标系并在渲染到任一画布时进行转换。 As both canvas render the same image (and it seams that its the image that is the relevant coord system) use the image coordinate system.由于两个画布渲染相同的图像(并且它接缝是相关坐标系的图像)使用图像坐标系。

You have 4 coordinate systems, the image, the scaled image canvas1, the scaled image canvas2 and rect as canvas1 pixels.您有 4 个坐标系,图像、缩放图像 canvas1、缩放图像 canvas2 和作为 canvas1 像素的rect The problem is that you are converting from canvas1 pixel coordinates ( rect ) to canvas2 image coordinates问题是您正在从 canvas1 像素坐标( rect )转换为 canvas2 图像坐标

To fix your scaling and positioning convert canvas1 pixel coordinates to the image coordinates (see example function mouseEvents ).要修复缩放和定位,请将 canvas1 像素坐标转换为图像坐标(请参阅示例函数mouseEvents )。 Then in your render function convert from image coords back to canvas pixels coords (see example function updateCanvas ).然后在您的渲染函数中将图像坐标转换回画布像素坐标(参见示例函数updateCanvas )。

Example例子

I removed a lot of the unneeded and repeated code to reduce complexity and make the code more readable and maintainable.我删除了很多不需要和重复的代码,以降低复杂性并使代码更具可读性和可维护性。 Used modern JS and added some UI使用现代 JS 并添加了一些 UI

  • A single function to setup the canvas设置画布的单个函数
  • A single function to handle the mouse events一个处理鼠标事件的函数
  • A single function to render rectangle渲染矩形的单个函数
  • Give feedback using cursor "crosshair" and "none"使用光标“十字准线”和“无”提供反馈

For the example I removed the button (not relevant to the question), reduced the two canvas size to fit the very small snippet window, and have the second rectangle update live as the first rectangle is being plotted.例如,我删除了按钮(与问题无关),减小了两个画布的大小以适应非常小的片段窗口,并在绘制第一个矩形时实时更新第二个矩形。

Note that rect is in image coordinates.请注意, 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>

You need to reverse your scaling and translation of the first canvas before applying your transformation and scaling factors of the second canvas.在应用第二个画布的转换和缩放因子之前,您需要反转第一个画布的缩放和平移。 This is like 90% of the way there, the Y axis is correct, but the X axis is translated wrong.这就像那里的 90%,Y 轴是正确的,但 X 轴平移错误。 See my call to ctx2.strokeRect and how it reverses the scaling and translation.查看我对ctx2.strokeRect调用以及它如何反转缩放和平移。 My code is probably jacked because the canvas origin is in the top left and the formulas for translation and scaling assume it's the bottom left.我的代码可能被劫持,因为画布原点在左上角,而平移和缩放公式假定它在左下角。

Edit: Fixed the code - it should be correct now.编辑:修复了代码 - 现在应该是正确的。

 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