Just for fun and learning, I am trying to create a web based forms editor in the manner of Visual Studio. First requirement is the ability to draw a rectangle so that the outline follows the mouse movement while drawing. So with every mouse displacement, the previously drawn rectangle should be wiped. To achieve this, before drawing a new rectangle, I restore a snapshot of the canvas that was taken when the mouse button was pressed down.
This works well enough, the flickering is understandable but not too disturbing as long as I keep some continuous movement in the mouse. But what I fail to understand, and what is really annoying, is that the rectangle disappears as soon as I stop the mouse movement for more than a split second. I put a minimal test set in this Codepen [https://codepen.io/cbreemer/pen/mdgOQdQ] so the issue will be clear even is maybe my explanation wasn't.
I am hoping somebody can tell me what I am missing here.
I thought that maybe moving the rectangle-drawing code inside the image load handler (in the mouse move handler) might fix it. But sadly, then I don't see anything until the mouse button is released. I can't think of anything else to try.
var ctx;
var startX, startY, endX, endY, lastX, lastY;
var rect;
var lastCanvas;
var isMouseDown = false;
function init()
{
rect = myCanvas.getBoundingClientRect();
ctx = myCanvas.getContext("2d");
ctx.lineWidth = 1;
ctx.strokeStyle = "black";
ctx.fillStyle = "wheat";
ctx.fillRect(0, 0, rect.width, rect.height);
myCanvas.addEventListener("mousemove",(e)=>
{
if ( isMouseDown )
{
var canvasPic = new Image();
canvasPic.src = lastCanvas;
canvasPic.addEventListener("load", (e)=> { ctx.drawImage(canvasPic, 0, 0); });
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
ctx.strokeRect(startX, startY, x-startX, y-startY);
lastX = x;
lastY = y;
}
});
myCanvas.addEventListener("mousedown",(e)=>
{
lastCanvas = myCanvas.toDataURL();
startX = e.clientX - rect.left;
startY = e.clientY - rect.top;
lastX = startX;
lastY = startY;
isMouseDown = true;
});
myCanvas.addEventListener("mouseup",(e)=>
{
isMouseDown = false;
endX = e.clientX - rect.left;
endY = e.clientY - rect.top;
ctx.strokeRect(startX, startY, endX-startX, endY-startY);
});
}
<!DOCTYPE html>
<html>
<body onLoad="init();">
<canvas id="myCanvas" width="600" height="400" style="border:1px solid black"></canvas>
</body>
</html>
Taken from another answer of mine, Here's the basic idea of redrawing the canvas every tick of the requestAnimationFrame
.
It also demonstrated how to better redraw image (taken using getImageData
.
let canvas = document.querySelector(".result");
var ctx = canvas.getContext("2d");
//Loading of the home test image - img1
var img1 = new Image();
var imageData
//drawing of the test image - img1
img1.onload = function() {
ctx.fillStyle = "rgba(0, 0, 0, 255)";
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(img1, 0, 0);
imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
drawRect()
};
img1.crossOrigin = "Anonymous";
img1.src = 'https://picsum.photos/400/200';
function drawRect() {
let SldrVal = document.getElementById("MySldr").value;
//ctx.strokeStyle = "#305ef2";
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.putImageData(imageData, 0, 0);
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, SldrVal, 0, 2 * Math.PI);
ctx.stroke();
ctx.font = "32px Arial";
ctx.fillText(SldrVal, 10, 50);
requestAnimationFrame(drawRect)
}
<input type="range" id="MySldr" value="15" min="1" max="100">
<br>
<canvas class="result" width="400" height="160"></canvas>