繁体   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