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 000000000..0967ef424 --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1 @@ +{} diff --git a/src/appState.ts b/src/appState.ts index d41b3ba7a..de55b6c5d 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 430af6c82..c37643101 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 52d14c042..e72e57dbe 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 a5f89988d..26e27c29a 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 cd48c4abe..96c93fb19 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 ba92e0cd0..63eb39d42 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 640d4f183..ca7123d80 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<{