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 0000000000..0967ef424b --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/src/appState.ts b/src/appState.ts index d41b3ba7a9..de55b6c5d5 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 430af6c821..c37643101a 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 52d14c0424..e72e57dbeb 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 a5f89988dc..26e27c29a3 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 cd48c4abef..96c93fb19a 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 ba92e0cd02..63eb39d42a 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 640d4f183d..ca7123d806 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<{