I'm trying to create a canvas on my website, but it's behaving strangely. It doesn't paint at the position where the mouse is. Additionally, it would be nice to have the eraser paint transparent instead of white, but I can't figure out how to make it work.
The strange behavior is because of the CSS size applied to it, but how can I compensate for it to be resizable and responsive?
const mainCanvas = document.getElementById("modal-canvas");
const context = mainCanvas.getContext("2d");
let initialX;
let initialY;
let pencilColor = "#000";
let brushSize = 10;
const dibujar = (cursorX, cursorY) => {
context.beginPath();
context.moveTo(initialX, initialY);
context.lineWidth = brushSize;
context.strokeStyle = pencilColor;
context.lineCap = "round";
context.lineJoin = "round";
context.lineTo(cursorX, cursorY);
context.stroke();
initialX = cursorX;
initialY = cursorY;
};
const mouseDown = (evt) => {
initialX = evt.offsetX;
initialY = evt.offsetY;
dibujar(initialX, initialY);
mainCanvas.addEventListener("mousemove", mouseMoving);
};
const mouseMoving = (evt) => {
dibujar(evt.offsetX, evt.offsetY);
};
const mouseUp = () => {
mainCanvas.removeEventListener("mousemove", mouseMoving);
};
const setPencilColor = (color) => {
pencilColor = color;
};
const setBrushSize = (size) => {
brushSize = size;
};
const saveImage = () => {
const link = document.createElement("a");
link.href = mainCanvas.toDataURL();
link.download = "image.jpg";
link.click();
};
mainCanvas.addEventListener("mousedown", mouseDown);
mainCanvas.addEventListener("mouseup", mouseUp);
#canvas-container{
display: flex;
flex-direction: column;
margin-left: 1rem;
width: calc(100% - 2rem);
}
#modal-canvas{
aspect-ratio: 16/9;
width:100%;
background-color: #ffffff;
}
canvas {
border: 1px solid black;
}
<div id="canvas-container">
<canvas id="modal-canvas" class="canvas" display="none"></canvas>
<div id="canvas-options">
<div>
<button onclick="setPencilColor('black')">Pencil</button>
<button onclick="setPencilColor('white')">Eraser</button>
<span id="brush-sice"><input type="range" min="1" max="10" value="3" oninput="setBrushSize(event.target.value)"></input></span>
</div>
<div>
<button onclick="saveImage()">Save</button>
</div>
</div>
</div>
I don't know how to fix the mismatch between the painted area and the pointer's location. Moreover, I'm also unsure about how to make the eraser erase or "paint with a transparent color" instead of a solid color. I'm not very familiar with the JavaScript canvas API, and I can't seem to find the documentation to solve the issue.
You should set the width and height of your canvas to the size determined by CSS rules so that your canvas is mapped 1:1 to its actual size on the screen. Additionally, you should do this when the window is resized.
The only problem is that when you resize the canvas, you destroy the drawing. Therefore, you need to save it as an image and then restore it after the resizing.
const resize = () => {
mainCanvas.width = mainCanvas.offsetWidth;
mainCanvas.height = mainCanvas.offsetHeight;
};
window.addEventListener('resize', resize() || resize);
const mainCanvas = document.getElementById("modal-canvas");
const resize = () => {
mainCanvas.width = mainCanvas.offsetWidth;
mainCanvas.height = mainCanvas.offsetHeight;
};
window.addEventListener('resize', resize() || resize);
const context = mainCanvas.getContext("2d");
let initialX;
let initialY;
let pencilColor = "#000";
let brushSize = 10;
const dibujar = (cursorX, cursorY) => {
context.beginPath();
context.moveTo(initialX, initialY);
context.lineWidth = brushSize;
context.strokeStyle = pencilColor;
context.lineCap = "round";
context.lineJoin = "round";
context.lineTo(cursorX, cursorY);
context.stroke();
initialX = cursorX;
initialY = cursorY;
};
const mouseDown = (evt) => {
initialX = evt.offsetX;
initialY = evt.offsetY;
dibujar(initialX, initialY);
mainCanvas.addEventListener("mousemove", mouseMoving);
};
const mouseMoving = (evt) => {
dibujar(evt.offsetX, evt.offsetY);
};
const mouseUp = () => {
mainCanvas.removeEventListener("mousemove", mouseMoving);
};
const setPencilColor = (color) => {
pencilColor = color;
};
const setBrushSize = (size) => {
brushSize = size;
};
const saveImage = () => {
const link = document.createElement("a");
link.href = mainCanvas.toDataURL();
link.download = "image.jpg";
link.click();
};
mainCanvas.addEventListener("mousedown", mouseDown);
mainCanvas.addEventListener("mouseup", mouseUp);
#canvas-container{
display: flex;
flex-direction: column;
margin-left: 1rem;
width: calc(100% - 2rem);
}
#modal-canvas{
aspect-ratio: 16/9;
width:100%;
background-color: #ffffff;
}
canvas {
border: 1px solid black;
}
<div id="canvas-container">
<canvas id="modal-canvas" class="canvas" display="none"></canvas>
<div id="canvas-options">
<div>
<button onclick="setPencilColor('black')">Pencil</button>
<button onclick="setPencilColor('white')">Eraser</button>
<span id="brush-sice"><input type="range" min="1" max="10" value="3" oninput="setBrushSize(event.target.value)"></input></span>
</div>
<div>
<button onclick="saveImage()">Save</button>
</div>
</div>
</div>