Skip to content

Commit

Permalink
Don't reset cache while zooming using a gesture (excalidraw#1103)
Browse files Browse the repository at this point in the history
* 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 excalidraw#1056

* update snapshot tests
  • Loading branch information
vjeux authored Mar 28, 2020
1 parent 95eaade commit 24fa657
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 7 deletions.
1 change: 1 addition & 0 deletions .watchmanconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions src/appState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export function getDefaultAppState(): AppState {
lastPointerDownWith: "mouse",
selectedElementIds: {},
collaborators: new Map(),
shouldCacheIgnoreZoom: false,
};
}

Expand Down
7 changes: 7 additions & 0 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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);
Expand Down
18 changes: 11 additions & 7 deletions src/renderer/renderElement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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!,
Expand All @@ -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);
}
Expand Down
1 change: 1 addition & 0 deletions src/scene/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export function exportToCanvas(
scrollY: normalizeScroll(-minY + exportPadding),
zoom: 1,
remotePointerViewportCoords: {},
shouldCacheIgnoreZoom: false,
},
{
renderScrollbars: false,
Expand Down
1 change: 1 addition & 0 deletions src/scene/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 } };
};

Expand Down
Loading

0 comments on commit 24fa657

Please sign in to comment.