From 24fa657093755734432684aed98892785ae0e4d8 Mon Sep 17 00:00:00 2001 From: Christopher Chedeau Date: Sat, 28 Mar 2020 16:59:36 -0700 Subject: [PATCH] Don't reset cache while zooming using a gesture (#1103) * Don't reset cache while zooming using a gesture This reuses the cached canvas while the gesture is happening. Once it has stop updating, then recompute the cache with the proper zoom. This should massively improve performance when panning on big scenes on mobile Fixes #1056 * update snapshot tests --- .watchmanconfig | 1 + src/appState.ts | 1 + src/components/App.tsx | 7 ++++ src/renderer/renderElement.ts | 18 ++++---- src/scene/export.ts | 1 + src/scene/types.ts | 1 + .../regressionTests.test.tsx.snap | 41 +++++++++++++++++++ src/types.ts | 1 + 8 files changed, 64 insertions(+), 7 deletions(-) create mode 100644 .watchmanconfig diff --git a/.watchmanconfig b/.watchmanconfig new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/src/appState.ts b/src/appState.ts index d41b3ba7..de55b6c5 100644 --- a/src/appState.ts +++ b/src/appState.ts @@ -35,6 +35,7 @@ export function getDefaultAppState(): AppState { lastPointerDownWith: "mouse", selectedElementIds: {}, collaborators: new Map(), + shouldCacheIgnoreZoom: false, }; } diff --git a/src/components/App.tsx b/src/components/App.tsx index 430af6c8..c3764310 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -481,6 +481,7 @@ export class App extends React.Component { viewBackgroundColor: this.state.viewBackgroundColor, zoom: this.state.zoom, remotePointerViewportCoords: pointerViewportCoords, + shouldCacheIgnoreZoom: this.state.shouldCacheIgnoreZoom, }, { renderOptimizations: true, @@ -1247,7 +1248,9 @@ export class App extends React.Component { scrollX: normalizeScroll(this.state.scrollX + deltaX / this.state.zoom), scrollY: normalizeScroll(this.state.scrollY + deltaY / this.state.zoom), zoom: getNormalizedZoom(gesture.initialScale! * scaleFactor), + shouldCacheIgnoreZoom: true, }); + this.resetShouldCacheIgnoreZoomDebounced(); } else { gesture.lastCenter = gesture.initialDistance = gesture.initialScale = null; } @@ -2553,6 +2556,10 @@ export class App extends React.Component { this.socket && this.broadcastMouseLocation({ pointerCoords }); }; + private resetShouldCacheIgnoreZoomDebounced = debounce(() => { + this.setState({ shouldCacheIgnoreZoom: false }); + }, 1000); + private saveDebounced = debounce(() => { saveToLocalStorage(globalSceneState.getAllElements(), this.state); }, 300); diff --git a/src/renderer/renderElement.ts b/src/renderer/renderElement.ts index 52d14c04..e72e57db 100644 --- a/src/renderer/renderElement.ts +++ b/src/renderer/renderElement.ts @@ -245,7 +245,11 @@ function generateElement( } const zoom = sceneState ? sceneState.zoom : 1; const prevElementWithCanvas = elementWithCanvasCache.get(element); - if (!prevElementWithCanvas || prevElementWithCanvas.canvasZoom !== zoom) { + const shouldRegenerateBecauseZoom = + prevElementWithCanvas && + prevElementWithCanvas.canvasZoom !== zoom && + !sceneState?.shouldCacheIgnoreZoom; + if (!prevElementWithCanvas || shouldRegenerateBecauseZoom) { const elementWithCanvas = generateElementCanvas(element, zoom); elementWithCanvasCache.set(element, elementWithCanvas); return elementWithCanvas; @@ -261,8 +265,8 @@ function drawElementFromCanvas( ) { context.scale(1 / window.devicePixelRatio, 1 / window.devicePixelRatio); context.translate( - -CANVAS_PADDING / sceneState.zoom, - -CANVAS_PADDING / sceneState.zoom, + -CANVAS_PADDING / elementWithCanvas.canvasZoom, + -CANVAS_PADDING / elementWithCanvas.canvasZoom, ); context.drawImage( elementWithCanvas.canvas!, @@ -276,12 +280,12 @@ function drawElementFromCanvas( (Math.floor(elementWithCanvas.element.y) + sceneState.scrollY) * window.devicePixelRatio, ), - elementWithCanvas.canvas!.width / sceneState.zoom, - elementWithCanvas.canvas!.height / sceneState.zoom, + elementWithCanvas.canvas!.width / elementWithCanvas.canvasZoom, + elementWithCanvas.canvas!.height / elementWithCanvas.canvasZoom, ); context.translate( - CANVAS_PADDING / sceneState.zoom, - CANVAS_PADDING / sceneState.zoom, + CANVAS_PADDING / elementWithCanvas.canvasZoom, + CANVAS_PADDING / elementWithCanvas.canvasZoom, ); context.scale(window.devicePixelRatio, window.devicePixelRatio); } diff --git a/src/scene/export.ts b/src/scene/export.ts index a5f89988..26e27c29 100644 --- a/src/scene/export.ts +++ b/src/scene/export.ts @@ -50,6 +50,7 @@ export function exportToCanvas( scrollY: normalizeScroll(-minY + exportPadding), zoom: 1, remotePointerViewportCoords: {}, + shouldCacheIgnoreZoom: false, }, { renderScrollbars: false, diff --git a/src/scene/types.ts b/src/scene/types.ts index cd48c4ab..96c93fb1 100644 --- a/src/scene/types.ts +++ b/src/scene/types.ts @@ -7,6 +7,7 @@ export type SceneState = { // null indicates transparent bg viewBackgroundColor: string | null; zoom: number; + shouldCacheIgnoreZoom: boolean; remotePointerViewportCoords: { [id: string]: { x: number; y: number } }; }; diff --git a/src/tests/__snapshots__/regressionTests.test.tsx.snap b/src/tests/__snapshots__/regressionTests.test.tsx.snap index ba92e0cd..63eb39d4 100644 --- a/src/tests/__snapshots__/regressionTests.test.tsx.snap +++ b/src/tests/__snapshots__/regressionTests.test.tsx.snap @@ -33,6 +33,7 @@ Object { "id2": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -211,6 +212,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -316,6 +318,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -558,6 +561,7 @@ Object { "id1": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -699,6 +703,7 @@ Object { "id2": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -873,6 +878,7 @@ Object { "id2": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -1053,6 +1059,7 @@ Object { "id3": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -1320,6 +1327,7 @@ Object { "scrolledOutside": false, "selectedElementIds": Object {}, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -1895,6 +1903,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2000,6 +2009,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2105,6 +2115,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2210,6 +2221,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2337,6 +2349,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2464,6 +2477,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2591,6 +2605,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2696,6 +2711,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2801,6 +2817,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -2928,6 +2945,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -3033,6 +3051,7 @@ Object { "id0": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": true, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -3098,6 +3117,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -3754,6 +3774,7 @@ Object { "id7": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -4102,6 +4123,7 @@ Object { "id5": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -4380,6 +4402,7 @@ Object { "id3": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -4588,6 +4611,7 @@ Object { "id1": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -4742,6 +4766,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -5370,6 +5395,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -5928,6 +5954,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -6416,6 +6443,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -6835,6 +6863,7 @@ Object { "id8": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -7218,6 +7247,7 @@ Object { "id6": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -7531,6 +7561,7 @@ Object { "id4": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -7774,6 +7805,7 @@ Object { "id2": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -7963,6 +7995,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -8626,6 +8659,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -9219,6 +9253,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -9742,6 +9777,7 @@ Object { "id9": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -10191,6 +10227,7 @@ Object { "id4": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -10419,6 +10456,7 @@ Object { "scrolledOutside": false, "selectedElementIds": Object {}, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -10468,6 +10506,7 @@ Object { "id1": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": true, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -10517,6 +10556,7 @@ Object { "id2": true, }, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } @@ -10785,6 +10825,7 @@ Object { "scrolledOutside": false, "selectedElementIds": Object {}, "selectionElement": null, + "shouldCacheIgnoreZoom": false, "viewBackgroundColor": "#ffffff", "zoom": 1, } diff --git a/src/types.ts b/src/types.ts index 640d4f18..ca7123d8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -42,6 +42,7 @@ export type AppState = { lastPointerDownWith: PointerType; selectedElementIds: { [id: string]: boolean }; collaborators: Map; + shouldCacheIgnoreZoom: boolean; }; export type PointerCoords = Readonly<{