The problem line is in the handleLineEnd
function.
I ripped off all the lines not necessary to show the problem.
Without the line: I can draw multiple rectangles on the ctx.
With the line: Each rectangle is overwritten by the next rectangle.
The code runs fine in older Chrome (118.0.5993.71) vs not working in version 119.0.6045.106
function handleLineEnd(evt) {
{
startDrawing = false;
var dummy = ctx.getImageData(3, 3, 5, 5); // problem line
ctx.drawImage(canvaslayer, 0, 0);
ctxlayer.clearRect(0, 0, el.width, el.height);
}
}
var el = document.getElementById("canvasID");
var elcontainer = document.getElementById("containerID");
var ctx = el.getContext('2d');
var startpuntX, startpuntY, eindpuntX, eindpuntY;
var startDrawing = false;
var container = el.parentNode; // Add the temporary canvas.
var containerHandletje = document.getElementById('containerID');
var ongoingTouches = new Array();
var huidigeKleur = 'black';
var huidigeDikte = 2;
var selectie = document.getElementById('drawingtoolID');
elcontainer.setAttribute("style", 'width: ' + (window.innerWidth - 12) + 'px; height: ' + (window.innerHeight - 80) + 'px; overflow: auto;');
el.height = window.innerHeight - 90;
el.width = window.innerWidth - 42;
canvaslayer = document.createElement('canvas');
canvaslayer.id = 'imageTemp';
canvaslayer.width = el.width;
canvaslayer.height = el.height;
canvaslayer.style = 'touch-action:none;';
container.appendChild(canvaslayer);
ctxlayer = canvaslayer.getContext('2d');
ctxlayer.lineCap = 'round';
ctx.lineWidth = 1;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
ctx.fillStyle = 'white'
ctx.fillRect(0, 0, el.width, el.height);
ctx.lineWidth = huidigeDikte;
canvaslayer.addEventListener('pointerdown', handleLineStart, false);
canvaslayer.addEventListener('pointerup', handleLineEnd, false);
canvaslayer.addEventListener('pointermove', handleLineMove, false);
function handleLineStart(evt) {
startpuntX = evt.clientX - el.offsetLeft + pageXOffset + containerHandletje.scrollLeft;
startpuntY = evt.clientY - container.offsetTop + pageYOffset + containerHandletje.scrollTop;
ctxlayer.lineWidth = huidigeDikte;
ctxlayer.strokeStyle = huidigeKleur;
ctxlayer.fillStyle = huidigeKleur;
startDrawing = true;
ctxlayer.stroke();
ctxlayer.beginPath();
handleLineMove(evt);
}
function handleLineMove(evt) {
var x, y, w, h, hokDx, xi, yi, asX, asY, asXeind, asYeind, fontsize, dzx, dzy, situatie;
if (startDrawing) {
eindpuntX = evt.clientX - el.offsetLeft + pageXOffset + containerHandletje.scrollLeft;
eindpuntY = evt.clientY - container.offsetTop + pageYOffset + containerHandletje.scrollTop;
ctxlayer.clearRect(0, 0, el.width, el.height);
ctxlayer.beginPath();
ctxlayer.moveTo(startpuntX, startpuntY);
x = Math.min(startpuntX, eindpuntX);
y = Math.min(startpuntY, eindpuntY);
w = Math.abs(startpuntX - eindpuntX);
h = Math.abs(startpuntY - eindpuntY);
ctxlayer.strokeRect(x, y, w, h);
ctxlayer.stroke();
ctxlayer.beginPath();
}
}
function handleLineEnd(evt) {
{
startDrawing = false;
var dummy = ctx.getImageData(3, 3, 5, 5); // problem line
ctx.drawImage(canvaslayer, 0, 0);
ctxlayer.clearRect(0, 0, el.width, el.height);
}
}
#containerID {
position: relative;
}
#canvasID {
border: 1px solid #000;
}
#imageTemp {
position: absolute;
top: 1px;
left: 11px;
}
<div id="containerID" style="touch-action:none; width: 100px; height: 500px; overflow: auto;">
<canvas id='canvasID' width='80' height='80' style='margin-left:10px;background:white;border:solid black 1px; touch-action:none; '>
Your browser does not support canvas element.
</canvas>
</div>
2
Answers
I get a feeling you are reinventing the wheel here, there are libraries that will make all that and much more, and your code will look a lot simpler, for example http://fabricjs.com/ that will allow you to add shapes and give the user control of location, rotation and dimensions, you can also group shapes or make it not selectable.
Look at my sample below, I added 3 rectangles to a group and added an event listener, double click will add a new rectangle that user can modify later.
This is a known and fixed Chrome bug (CRBUG 1500272 and CRBUG 1499539) that was caused by this CL. Basically they’re rewriting their canvas layer logic and this messed up with when these get flushed. Roughly, calling
getImageData()
does move the layer around from the GPU to the CPU and that change made the bridge not handle that moving correctly, leaving the GPU side without a resource provider.Note that we can reduce the bug repro down to:
Of possible workarounds:
Do not use an accelerated canvas. By requesting your 2D context with the
willReadFrequently
option, you’ll avoid your canvas is moved around. If you are going to do many readbacks on your canvas, you may really want to consider this option, even without the bug. Moving the canvas around is very slow and the little you may have won through hardware acceleration (which might not even be much depending on what you draw), will be lost in these calls.If you really think you must use an accelerated canvas, then you could use an
OffscreenCanvas
instead of thecanvaslayer
<canvas>
element.OffscreenCanvas
aren’t causing that bug (for whatever reasons…).From your code though, since you need the
layercanvas
to be visible, you’d uselayercanvas.transferControlToOffscreen()
instead of the constructor, so that the<canvas>
element becomes a placeholder canvas and still paints its content to screen.But that’s an ugly hack-around, for a bug that’s already been fixed and which should disappear in the next Chrome release (current Chrome Beta has the fix already).