fix: disable render throttling by default & during resize (#5451)

This commit is contained in:
David Luzar 2022-07-16 11:36:55 +02:00 committed by GitHub
parent df14c69977
commit 5bc40402a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 66 additions and 14 deletions

View File

@ -166,7 +166,7 @@ import {
isAndroid,
} from "../keys";
import { distance2d, getGridPoint, isPathALoop } from "../math";
import { renderSceneThrottled } from "../renderer/renderScene";
import { renderScene } from "../renderer/renderScene";
import { invalidateShapeForElement } from "../renderer/renderElement";
import {
calculateScrollCenter,
@ -286,6 +286,10 @@ let currentScrollBars: ScrollBars = { horizontal: null, vertical: null };
let touchTimeout = 0;
let invalidateContextMenu = false;
// remove this hack when we can sync render & resizeObserver (state update)
// to rAF. See #5439
let THROTTLE_NEXT_RENDER = true;
let lastPointerUp: ((event: any) => void) | null = null;
const gesture: Gesture = {
pointers: new Map(),
@ -858,6 +862,7 @@ class App extends React.Component<AppProps, AppState> {
if ("ResizeObserver" in window && this.excalidrawContainerRef?.current) {
this.resizeObserver = new ResizeObserver(() => {
THROTTLE_NEXT_RENDER = false;
// recompute device dimensions state
// ---------------------------------------------------------------------
this.refreshDeviceState(this.excalidrawContainerRef.current!);
@ -1221,7 +1226,7 @@ class App extends React.Component<AppProps, AppState> {
);
});
renderSceneThrottled(
renderScene(
renderingElements,
this.state,
this.state.selectionElement,
@ -1259,8 +1264,13 @@ class App extends React.Component<AppProps, AppState> {
this.scheduleImageRefresh();
},
THROTTLE_NEXT_RENDER && window.EXCALIDRAW_THROTTLE_RENDER === true,
);
if (!THROTTLE_NEXT_RENDER) {
THROTTLE_NEXT_RENDER = true;
}
this.history.record(this.state, this.scene.getElementsIncludingDeleted());
// Do not notify consumers if we're still loading the scene. Among other

View File

@ -83,6 +83,8 @@ import { jotaiStore, useAtomWithInitialValue } from "../jotai";
import { reconcileElements } from "./collab/reconciliation";
import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library";
window.EXCALIDRAW_THROTTLE_RENDER = true;
const isExcalidrawPlusSignedUser = document.cookie.includes(
COOKIES.AUTH_STATE_COOKIE,
);

1
src/global.d.ts vendored
View File

@ -14,6 +14,7 @@ interface Window {
__EXCALIDRAW_SHA__: string | undefined;
EXCALIDRAW_ASSET_PATH: string | undefined;
EXCALIDRAW_EXPORT_SOURCE: string;
EXCALIDRAW_THROTTLE_RENDER: boolean | undefined;
gtag: Function;
}

View File

@ -181,7 +181,7 @@ const renderLinearPointHandles = (
context.restore();
};
export const renderScene = (
export const _renderScene = (
elements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
selectionElement: NonDeletedExcalidrawElement | null,
@ -572,8 +572,7 @@ export const renderScene = (
return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
};
/** renderScene throttled to animation framerate */
export const renderSceneThrottled = throttleRAF(
const renderSceneThrottled = throttleRAF(
(
elements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
@ -582,9 +581,9 @@ export const renderSceneThrottled = throttleRAF(
rc: RoughCanvas,
canvas: HTMLCanvasElement,
renderConfig: RenderConfig,
callback?: (data: ReturnType<typeof renderScene>) => void,
callback?: (data: ReturnType<typeof _renderScene>) => void,
) => {
const ret = renderScene(
const ret = _renderScene(
elements,
appState,
selectionElement,
@ -598,6 +597,46 @@ export const renderSceneThrottled = throttleRAF(
{ trailing: true },
);
/** renderScene throttled to animation framerate */
export const renderScene = <T extends boolean = false>(
elements: readonly NonDeletedExcalidrawElement[],
appState: AppState,
selectionElement: NonDeletedExcalidrawElement | null,
scale: number,
rc: RoughCanvas,
canvas: HTMLCanvasElement,
renderConfig: RenderConfig,
callback?: (data: ReturnType<typeof _renderScene>) => void,
/** Whether to throttle rendering. Defaults to false.
* When throttling, no value is returned. Use the callback instead. */
throttle?: T,
): T extends true ? void : ReturnType<typeof _renderScene> => {
if (throttle) {
renderSceneThrottled(
elements,
appState,
selectionElement,
scale,
rc,
canvas,
renderConfig,
callback,
);
return undefined as T extends true ? void : ReturnType<typeof _renderScene>;
}
const ret = _renderScene(
elements,
appState,
selectionElement,
scale,
rc,
canvas,
renderConfig,
);
callback?.(ret);
return ret as T extends true ? void : ReturnType<typeof _renderScene>;
};
const renderTransformHandles = (
context: CanvasRenderingContext2D,
renderConfig: RenderConfig,

View File

@ -39,7 +39,7 @@ const mouse = new Pointer("mouse");
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
const renderScene = jest.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View File

@ -14,7 +14,7 @@ import { reseed } from "../random";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
const renderScene = jest.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View File

@ -16,7 +16,7 @@ import { KEYS } from "../keys";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
const renderScene = jest.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View File

@ -14,7 +14,7 @@ import { reseed } from "../random";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
const renderScene = jest.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View File

@ -20,7 +20,7 @@ import { t } from "../i18n";
const { h } = window;
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
const renderScene = jest.spyOn(Renderer, "renderScene");
const mouse = new Pointer("mouse");
const finger1 = new Pointer("touch", 1);

View File

@ -18,7 +18,7 @@ const mouse = new Pointer("mouse");
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
const renderScene = jest.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();

View File

@ -16,7 +16,7 @@ import { Keyboard, Pointer } from "./helpers/ui";
// Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
const renderScene = jest.spyOn(Renderer, "renderScene");
beforeEach(() => {
localStorage.clear();
renderScene.mockClear();