I want to use MS paint style line tool to draw straight line using UI on html canvas element using JS.
The issue is that line preview must be visible on canvas as mouse is being dragged after click at starting position (line preview is basically straight line from starting click point to current mouse position). Once mouse shifts to new position without release, the old line preview should vanish and new one should show. Finally, once mouse is released, the final line should be painted on the canvas. Instead, I get the kind of image you see below.
I tried various ways to perform the following steps in this order but no success:
Loop steps 2. and 3. until mouse released:
window.onload = function() {
const canvas = document.getElementById('paintCanvas');
const ctx = canvas.getContext('2d', { willReadFrequently: true });
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
let painting = false;
let startX, startY;
let canvasState = false;
canvas.addEventListener('mousedown', startPosition);
canvas.addEventListener('mouseup', endPosition);
canvas.addEventListener('mousemove', draw);
ctx.lineWidth = 5;
ctx.lineCap = 'round';
function startPosition(e) {
painting = true;
[startX, startY] = [e.offsetX, e.offsetY];
ctx.save();
ctx.beginPath();
draw(e);
}
function endPosition() {
ctx.closePath();
ctx.restore();
painting = false;
canvasState = false;
}
function draw(e) {
if (!painting) return;
if (selectedTool.id === 'pencil') {
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
} else if (selectedTool.id === 'line') {
// write code that works with a line
if (e.type === 'mousemove') {
// remove old preview line
if (canvasState) {
ctx.putImageData(canvasState, 0, 0);
} else {
canvasState = ctx.getImageData(0,0,canvas.width,canvas.height);
}
// paint straight line from original start to current mouse position
ctx.moveTo(startX, startY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
}
} else if (selectedTool.id === 'eraser') {
if (e.type === 'mousedown') {
ctx.strokeStyle = ctx.fillStyle;
ctx.lineWidth += 5;
} else {
ctx.lineTo(e.offsetX, e.offsetY);
ctx.stroke();
}
}
}
};
I think, this is because of canvasState
is restored inside the path building. The following fix solved the problem for me:
// remove old preview line
if (canvasState) {
ctx.putImageData(canvasState, 0, 0);
} else {
canvasState = ctx.getImageData(0,0,canvas.width,canvas.height);
}
// paint straight line from original start to current mouse position
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(e.offsetX, e.offsetY);
ctx.closePath();
ctx.stroke();