From ce6543fd49be5c3cb84edef6832021deab097d65 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Tue, 18 Aug 2026 21:00:40 +0200 Subject: [PATCH] perf: defer blend cache builds during interaction --- js/canvas/canvas_layers.js | 11 +++++++++++ src/canvas/canvas_layers.ts | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/js/canvas/canvas_layers.js b/js/canvas/canvas_layers.js index eb50561..1a7cb72 100644 --- a/js/canvas/canvas_layers.js +++ b/js/canvas/canvas_layers.js @@ -732,6 +732,10 @@ export class CanvasLayers { 'nocrop'; return `${layer.id}_${blendArea}_${cropKey}_${layer.width}_${layer.height}`; } + isUserInteractionActive() { + const mode = this.canvas.canvasInteractions?.interaction?.mode; + return mode !== undefined && mode !== 'none'; + } /** * Get processed image with all effects applied (blend area, crop, etc.) * Uses live rendering for layers being actively adjusted, debounced processing for others @@ -790,6 +794,13 @@ export class CanvasLayers { // Schedule new timer const timer = window.setTimeout(() => { this.processedImageBuildTimers.delete(cacheKey); + // Distance-field generation is synchronous and can block for a + // large image. Never start it while pointer/key interaction is in + // progress; retry after the interaction has been idle. + if (this.isUserInteractionActive()) { + this.scheduleProcessedImageCreation(layer, cacheKey); + return; + } void this.buildProcessedImage(layer, cacheKey, 'debounced processed image'); }, this.PROCESSED_IMAGE_DEBOUNCE_DELAY); this.processedImageBuildTimers.set(cacheKey, timer); diff --git a/src/canvas/canvas_layers.ts b/src/canvas/canvas_layers.ts index 72e743a..bdd368f 100644 --- a/src/canvas/canvas_layers.ts +++ b/src/canvas/canvas_layers.ts @@ -876,6 +876,11 @@ export class CanvasLayers { return `${layer.id}_${blendArea}_${cropKey}_${layer.width}_${layer.height}`; } + private isUserInteractionActive(): boolean { + const mode = this.canvas.canvasInteractions?.interaction?.mode; + return mode !== undefined && mode !== 'none'; + } + /** * Get processed image with all effects applied (blend area, crop, etc.) * Uses live rendering for layers being actively adjusted, debounced processing for others @@ -946,6 +951,15 @@ export class CanvasLayers { // Schedule new timer const timer = window.setTimeout(() => { this.processedImageBuildTimers.delete(cacheKey); + + // Distance-field generation is synchronous and can block for a + // large image. Never start it while pointer/key interaction is in + // progress; retry after the interaction has been idle. + if (this.isUserInteractionActive()) { + this.scheduleProcessedImageCreation(layer, cacheKey); + return; + } + void this.buildProcessedImage(layer, cacheKey, 'debounced processed image'); }, this.PROCESSED_IMAGE_DEBOUNCE_DELAY);