From 03ab254f63014d32f1e69038ad39935c72c9d069 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Tue, 24 Jun 2025 19:42:28 +0200 Subject: [PATCH] Improve layer state comparison and mirror methods Added getStateSignature to compare layer states by serializing relevant properties, avoiding issues with image object references. Refactored mirrorHorizontal and mirrorVertical to be async and await all image updates before rendering and saving state, ensuring consistency and preventing race conditions. --- js/Canvas.js | 87 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/js/Canvas.js b/js/Canvas.js index c300926..d74642a 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -194,6 +194,17 @@ export class Canvas { }); } + getStateSignature(layers) { + return JSON.stringify(layers.map(layer => { + const sig = { ...layer }; + if (sig.image instanceof HTMLImageElement) { + sig.imageSrc = sig.image.src; + } + delete sig.image; + return sig; + })); + } + saveState(replaceLast = false) { if (replaceLast && this.undoStack.length > 0) { this.undoStack.pop(); @@ -203,7 +214,7 @@ export class Canvas { if (this.undoStack.length > 0) { const lastState = this.undoStack[this.undoStack.length - 1]; - if (JSON.stringify(currentState) === JSON.stringify(lastState)) { + if (this.getStateSignature(currentState) === this.getStateSignature(lastState)) { return; } } @@ -1817,50 +1828,60 @@ export class Canvas { return null; } - mirrorHorizontal() { + async mirrorHorizontal() { if (this.selectedLayers.length === 0) return; - this.selectedLayers.forEach(layer => { - const tempCanvas = document.createElement('canvas'); - const tempCtx = tempCanvas.getContext('2d'); - tempCanvas.width = layer.image.width; - tempCanvas.height = layer.image.height; + const promises = this.selectedLayers.map(layer => { + return new Promise(resolve => { + const tempCanvas = document.createElement('canvas'); + const tempCtx = tempCanvas.getContext('2d'); + tempCanvas.width = layer.image.width; + tempCanvas.height = layer.image.height; - tempCtx.translate(tempCanvas.width, 0); - tempCtx.scale(-1, 1); - tempCtx.drawImage(layer.image, 0, 0); + tempCtx.translate(tempCanvas.width, 0); + tempCtx.scale(-1, 1); + tempCtx.drawImage(layer.image, 0, 0); - const newImage = new Image(); - newImage.onload = () => { - layer.image = newImage; - this.render(); - this.saveState(); - }; - newImage.src = tempCanvas.toDataURL(); + const newImage = new Image(); + newImage.onload = () => { + layer.image = newImage; + resolve(); + }; + newImage.src = tempCanvas.toDataURL(); + }); }); + + await Promise.all(promises); + this.render(); + this.saveState(); } - mirrorVertical() { + async mirrorVertical() { if (this.selectedLayers.length === 0) return; - this.selectedLayers.forEach(layer => { - const tempCanvas = document.createElement('canvas'); - const tempCtx = tempCanvas.getContext('2d'); - tempCanvas.width = layer.image.width; - tempCanvas.height = layer.image.height; + const promises = this.selectedLayers.map(layer => { + return new Promise(resolve => { + const tempCanvas = document.createElement('canvas'); + const tempCtx = tempCanvas.getContext('2d'); + tempCanvas.width = layer.image.width; + tempCanvas.height = layer.image.height; - tempCtx.translate(0, tempCanvas.height); - tempCtx.scale(1, -1); - tempCtx.drawImage(layer.image, 0, 0); + tempCtx.translate(0, tempCanvas.height); + tempCtx.scale(1, -1); + tempCtx.drawImage(layer.image, 0, 0); - const newImage = new Image(); - newImage.onload = () => { - layer.image = newImage; - this.render(); - this.saveState(); - }; - newImage.src = tempCanvas.toDataURL(); + const newImage = new Image(); + newImage.onload = () => { + layer.image = newImage; + resolve(); + }; + newImage.src = tempCanvas.toDataURL(); + }); }); + + await Promise.all(promises); + this.render(); + this.saveState(); } async getLayerImageData(layer) {