簡體   English   中英

Javascript/HTML canvas:在動態大小的圖像上繪圖

[英]Javascript/HTML canvas: drawing on dynamically sized image

我正在嘗試創建一個界面(HTML canvas/Vanilla JS),用戶可以:

  1. 上傳圖片
  2. 在圖像上繪制
  3. 以與步驟 (1) 中的原始圖像相同的分辨率保存新圖像

我堅持(2)。 當我嘗試繪制時,x 和 y 坐標都是錯誤的。 它們的距離取決於上傳圖像的大小/形狀。 我制作了一個codepen來演示,因為下面的 Stack 代碼片段在運行時表現得很奇怪。

我是 JS/HTML 菜鳥,但我的假設是不正確的坐標是基於圖像的原始分辨率(保存結果圖像時保留),而不是 canvas 中顯示的圖像分辨率。

非常感謝任何幫助。

 const fileInput = document.querySelector(".file-input"), inputImg = document.querySelector(".input-img img"), chooseImgBtn = document.querySelector(".choose-img"), saveImgBtn = document.querySelector(".save-img"); drawOnImage(); fileInput.addEventListener("change", async(e) => { const [file] = fileInput.files; // displaying the uploaded image const image = document.createElement("img"); image.src = await fileToDataUri(file); // enbaling the brush after after the image // has been uploaded image.addEventListener("load", () => { drawOnImage(image); }); return false; }); function fileToDataUri(field) { return new Promise((resolve) => { const reader = new FileReader(); reader.addEventListener("load", () => { resolve(reader.result); }); reader.readAsDataURL(field); }); } const colorElement = document.getElementsByName("colorRadio"); let color; colorElement.forEach((c) => { if (c.checked) color = c.value; }); colorElement.forEach((c) => { c.onclick = () => { color = c.value; }; }); function drawOnImage(image = null) { const canvasElement = document.getElementById("canvas"); const context = canvasElement.getContext("2d"); // if an image is present, // the image passed as a parameter is drawn in the canvas if (image) { const imageWidth = image.width; const imageHeight = image.height; // rescaling the canvas element canvasElement.width = imageWidth; canvasElement.height = imageHeight; context.drawImage(image, 0, 0, imageWidth, imageHeight); } let isDrawing; canvasElement.onmousedown = (e) => { isDrawing = true; context.beginPath(); context.lineWidth = size; context.strokeStyle = "black"; context.lineJoin = "round"; context.lineCap = "round"; context.moveTo(e.clientX, e.clientY); }; canvasElement.onmousemove = (e) => { if (isDrawing) { context.lineTo(e.clientX, e.clientY); context.stroke(); } }; canvasElement.onmouseup = function() { isDrawing = false; context.closePath(); }; } chooseImgBtn.addEventListener("click", () => fileInput.click());
 /* Import Google font - Poppins */ @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap'); * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Poppins', sans-serif; } body { display: flex; padding: 10px; min-height: 90vh; align-items: center; justify-content: center; background: #9c9c9c; }.container { width: 1200px; padding: 30px 35px 35px; background: #fff; border-radius: 10px; box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1); }.container.editor-panel, .container.controls.reset-filter, .container.controls.save-img { opacity: 0.5; pointer-events: none; }.container h2 { margin-top: -8px; font-size: 22px; font-weight: 500; }.container.wrapper { display: flex; margin: 20px 0; min-height: 335px; }.editor-panel.options, .controls { display: flex; flex-wrap: wrap; justify-content: space-between; }.wrapper.canvas-wrapper { flex-grow: 1; display: flex; overflow: hidden; margin-left: 20px; border-radius: 5px; align-items: center; justify-content: center; } #canvas { max-width: 700px; max-height: 650px; width: 100%; height: 100%; border-radius: 5px; border: 1px solid #ccc; object-fit: contain; }.controls button { padding: 11px 20px; font-size: 14px; border-radius: 3px; outline: none; color: #fff; cursor: pointer; background: none; transition: all 0.3s ease; text-transform: uppercase; }.controls.choose-img { background: #6C757D; border: 1px solid #6C757D; }.controls.save-img { margin-left: 5px; background: #5372F0; border: 1px solid #5372F0; }
 <link rel="stylesheet" href="https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css" /> <div class="container"> <h2>Drawing</h2> <div class="wrapper"> <div class="canvas-wrapper"> <canvas id="canvas" style="border: 1px solid black;"></canvas> </div> </div> <div class="controls"> <input type="file" class="file-input" accept="image/*" hidden> <button class="choose-img">Choose Input Image</button> <div class="row"> <button class="save-img">Save</button> </div> </div> </div>

你在這里有兩個不同的問題。 首先是使用clientXclientY獲取鼠標position。 這些是相對於視口的 X 和 Y 坐標,例如 (0, 0) 是瀏覽器 window 的左上角,而不是 canvas。 您想改用offsetXoffsetY ,它們與觸發事件的元素相關。

第二個問題是使用 CSS 修改 canvas 的大小( width: 100%; height: 100% )。 這會更改屏幕上元素的物理尺寸,但不會更改您在上傳圖像時設置的 canvas 上下文的尺寸。 For example, let's say you set canvas.width and canvas.height (the size of the canvas context) to be 200x100 and you set its CSS width and height to be 400x200. 該元素在屏幕上的寬度為 400 像素,如果將鼠標放在一半,則offsetX將為 200。但是,在 canvas 上下文中繪制任何 X 為 200 的內容會將其放置在最右邊,因為上下文寬度是 200,而不是 400。

要解決這個問題,您要么根本不需要更改 CSS 中畫布的大小,要么通過將鼠標的 position 除以元素的物理大小和畫布的上下文大小之間的比率來解釋 JS 中的差異。

這是一個例子。 canvas 通過 CSS 放大 2 倍為 400x200。 黑色矩形是使用鼠標的確切 position 繪制的,而紅色矩形向下划分以獲得 canvas 上正確對應的 position。

 const mouse = document.getElementById("mouse"); const canvas = document.getElementById("canvas"); const ctx = canvas.getContext("2d"); // 200x100 canvas size canvas.width = 200; canvas.height = 100; canvas.addEventListener("mousemove", e => { mouse.innerText = `mouse x: ${e.offsetX}`; ctx.clearRect(0, 0, canvas.width, canvas.height); // draw at 1:1 physical size to canvas size ctx.fillStyle = "#000"; ctx.fillRect(e.offsetX, e.offsetY, 20, 20); // draw at 2:1 physical size to canvas size ctx.fillStyle = "#f00"; ctx.fillRect(e.offsetX / 2, e.offsetY / 2, 20, 20); // or if the ratio is not known: const width_ratio = canvas.clientWidth / canvas.width; const height_ratio = canvas.clientHeight / canvas.height; ctx.fillRect(e.offsetX / width_ratio, e.offsetY / height_ratio, 20, 20); });
 #canvas { width: 400px; height: 200px; border: 1px solid #000; }
 <div id="mouse"></div> <canvas id="canvas"></canvas>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM