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:
Repeat until mouse released:
- Store current image on canvas.
- Paint straight line from starting point to current cursor position.
- Restore previous image seen in step #1 on canvas upon detecting mouse movement.
- Paint new straight line from starting point to current cursor position.
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();
}
}
}
};
2
Answers
I think, this is because of
canvasState
is restored inside the path building. The following fix solved the problem for me:Why not make the preview line updates dynamically as the mouse moves and the final line drawn accurately when the mouse is released.