Browse Source

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
Christopher Chedeau 5 years ago
parent
commit
24fa657093

+ 1 - 0
.watchmanconfig

@@ -0,0 +1 @@
+{}

+ 1 - 0
src/appState.ts

@@ -35,6 +35,7 @@ export function getDefaultAppState(): AppState {
     lastPointerDownWith: "mouse",
     selectedElementIds: {},
     collaborators: new Map(),
+    shouldCacheIgnoreZoom: false,
   };
 }
 

+ 7 - 0
src/components/App.tsx

@@ -481,6 +481,7 @@ export class App extends React.Component<any, AppState> {
         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<any, AppState> {
         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<any, AppState> {
     this.socket && this.broadcastMouseLocation({ pointerCoords });
   };
 
+  private resetShouldCacheIgnoreZoomDebounced = debounce(() => {
+    this.setState({ shouldCacheIgnoreZoom: false });
+  }, 1000);
+
   private saveDebounced = debounce(() => {
     saveToLocalStorage(globalSceneState.getAllElements(), this.state);
   }, 300);

+ 11 - 7
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);
 }

+ 1 - 0
src/scene/export.ts

@@ -50,6 +50,7 @@ export function exportToCanvas(
       scrollY: normalizeScroll(-minY + exportPadding),
       zoom: 1,
       remotePointerViewportCoords: {},
+      shouldCacheIgnoreZoom: false,
     },
     {
       renderScrollbars: false,

+ 1 - 0
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 } };
 };
 

+ 41 - 0
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,
 }

+ 1 - 0
src/types.ts

@@ -42,6 +42,7 @@ export type AppState = {
   lastPointerDownWith: PointerType;
   selectedElementIds: { [id: string]: boolean };
   collaborators: Map<string, { pointer?: { x: number; y: number } }>;
+  shouldCacheIgnoreZoom: boolean;
 };
 
 export type PointerCoords = Readonly<{