簡體   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