perf: blit only dirty drag regions

This commit is contained in:
Dariusz L
2026-08-18 22:37:53 +02:00
parent 4f79de8fb2
commit d0f47336bc
3 changed files with 42 additions and 4 deletions
+16 -2
View File
@@ -327,8 +327,22 @@ export class CanvasRenderer {
this.canvas.canvas.width = this.canvas.offscreenCanvas.width;
this.canvas.canvas.height = this.canvas.offscreenCanvas.height;
}
this.canvas.ctx.clearRect(0, 0, this.canvas.canvas.width, this.canvas.canvas.height);
this.canvas.ctx.drawImage(this.canvas.offscreenCanvas, 0, 0);
if (canReuseDragFrame) {
// The offscreen canvas already contains the unchanged pixels. A
// partial blit keeps the visible canvas from copying the entire
// viewport on every drag frame.
this.canvas.ctx.save();
this.canvas.ctx.globalCompositeOperation = 'source-over';
this.canvas.ctx.globalAlpha = 1;
dirtyRects.forEach(rect => {
this.canvas.ctx.drawImage(this.canvas.offscreenCanvas, rect.x, rect.y, rect.width, rect.height, rect.x, rect.y, rect.width, rect.height);
});
this.canvas.ctx.restore();
}
else {
this.canvas.ctx.clearRect(0, 0, this.canvas.canvas.width, this.canvas.canvas.height);
this.canvas.ctx.drawImage(this.canvas.offscreenCanvas, 0, 0);
}
// Ensure overlay canvases are in DOM and properly sized
this.addOverlayToDOM();
this.updateOverlaySize();
+25 -2
View File
@@ -425,8 +425,31 @@ export class CanvasRenderer {
this.canvas.canvas.width = this.canvas.offscreenCanvas.width;
this.canvas.canvas.height = this.canvas.offscreenCanvas.height;
}
this.canvas.ctx.clearRect(0, 0, this.canvas.canvas.width, this.canvas.canvas.height);
this.canvas.ctx.drawImage(this.canvas.offscreenCanvas, 0, 0);
if (canReuseDragFrame) {
// The offscreen canvas already contains the unchanged pixels. A
// partial blit keeps the visible canvas from copying the entire
// viewport on every drag frame.
this.canvas.ctx.save();
this.canvas.ctx.globalCompositeOperation = 'source-over';
this.canvas.ctx.globalAlpha = 1;
dirtyRects.forEach(rect => {
this.canvas.ctx.drawImage(
this.canvas.offscreenCanvas,
rect.x,
rect.y,
rect.width,
rect.height,
rect.x,
rect.y,
rect.width,
rect.height
);
});
this.canvas.ctx.restore();
} else {
this.canvas.ctx.clearRect(0, 0, this.canvas.canvas.width, this.canvas.canvas.height);
this.canvas.ctx.drawImage(this.canvas.offscreenCanvas, 0, 0);
}
// Ensure overlay canvases are in DOM and properly sized
this.addOverlayToDOM();
@@ -100,6 +100,7 @@ test('drag scene cache groups source-over layers and replays non-normal modes',
assert.match(canvasRendererSource, /canReuseDragFrame/);
assert.match(canvasRendererSource, /getDragDirtyRects/);
assert.match(canvasRendererSource, /dirtyRects\.forEach/);
assert.match(canvasRendererSource, /this\.canvas\.offscreenCanvas,\s*rect\.x/);
assert.match(canvasRendererSource, /ctx\.clip\(\)/);
assert.match(canvasLayersSource, /getOrCreateDragLayerPreview/);
assert.match(canvasLayersSource, /prepareDragLayerPreviews/);