fix: disable render throttling by default & during resize (#5451)
This commit is contained in:
parent
df14c69977
commit
5bc40402a6
@ -166,7 +166,7 @@ import {
|
|||||||
isAndroid,
|
isAndroid,
|
||||||
} from "../keys";
|
} from "../keys";
|
||||||
import { distance2d, getGridPoint, isPathALoop } from "../math";
|
import { distance2d, getGridPoint, isPathALoop } from "../math";
|
||||||
import { renderSceneThrottled } from "../renderer/renderScene";
|
import { renderScene } from "../renderer/renderScene";
|
||||||
import { invalidateShapeForElement } from "../renderer/renderElement";
|
import { invalidateShapeForElement } from "../renderer/renderElement";
|
||||||
import {
|
import {
|
||||||
calculateScrollCenter,
|
calculateScrollCenter,
|
||||||
@ -286,6 +286,10 @@ let currentScrollBars: ScrollBars = { horizontal: null, vertical: null };
|
|||||||
let touchTimeout = 0;
|
let touchTimeout = 0;
|
||||||
let invalidateContextMenu = false;
|
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;
|
let lastPointerUp: ((event: any) => void) | null = null;
|
||||||
const gesture: Gesture = {
|
const gesture: Gesture = {
|
||||||
pointers: new Map(),
|
pointers: new Map(),
|
||||||
@ -858,6 +862,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
|
|
||||||
if ("ResizeObserver" in window && this.excalidrawContainerRef?.current) {
|
if ("ResizeObserver" in window && this.excalidrawContainerRef?.current) {
|
||||||
this.resizeObserver = new ResizeObserver(() => {
|
this.resizeObserver = new ResizeObserver(() => {
|
||||||
|
THROTTLE_NEXT_RENDER = false;
|
||||||
// recompute device dimensions state
|
// recompute device dimensions state
|
||||||
// ---------------------------------------------------------------------
|
// ---------------------------------------------------------------------
|
||||||
this.refreshDeviceState(this.excalidrawContainerRef.current!);
|
this.refreshDeviceState(this.excalidrawContainerRef.current!);
|
||||||
@ -1221,7 +1226,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
renderSceneThrottled(
|
renderScene(
|
||||||
renderingElements,
|
renderingElements,
|
||||||
this.state,
|
this.state,
|
||||||
this.state.selectionElement,
|
this.state.selectionElement,
|
||||||
@ -1259,8 +1264,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
|
|
||||||
this.scheduleImageRefresh();
|
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());
|
this.history.record(this.state, this.scene.getElementsIncludingDeleted());
|
||||||
|
|
||||||
// Do not notify consumers if we're still loading the scene. Among other
|
// Do not notify consumers if we're still loading the scene. Among other
|
||||||
|
@ -83,6 +83,8 @@ import { jotaiStore, useAtomWithInitialValue } from "../jotai";
|
|||||||
import { reconcileElements } from "./collab/reconciliation";
|
import { reconcileElements } from "./collab/reconciliation";
|
||||||
import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library";
|
import { parseLibraryTokensFromUrl, useHandleLibrary } from "../data/library";
|
||||||
|
|
||||||
|
window.EXCALIDRAW_THROTTLE_RENDER = true;
|
||||||
|
|
||||||
const isExcalidrawPlusSignedUser = document.cookie.includes(
|
const isExcalidrawPlusSignedUser = document.cookie.includes(
|
||||||
COOKIES.AUTH_STATE_COOKIE,
|
COOKIES.AUTH_STATE_COOKIE,
|
||||||
);
|
);
|
||||||
|
1
src/global.d.ts
vendored
1
src/global.d.ts
vendored
@ -14,6 +14,7 @@ interface Window {
|
|||||||
__EXCALIDRAW_SHA__: string | undefined;
|
__EXCALIDRAW_SHA__: string | undefined;
|
||||||
EXCALIDRAW_ASSET_PATH: string | undefined;
|
EXCALIDRAW_ASSET_PATH: string | undefined;
|
||||||
EXCALIDRAW_EXPORT_SOURCE: string;
|
EXCALIDRAW_EXPORT_SOURCE: string;
|
||||||
|
EXCALIDRAW_THROTTLE_RENDER: boolean | undefined;
|
||||||
gtag: Function;
|
gtag: Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +181,7 @@ const renderLinearPointHandles = (
|
|||||||
context.restore();
|
context.restore();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const renderScene = (
|
export const _renderScene = (
|
||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
selectionElement: NonDeletedExcalidrawElement | null,
|
selectionElement: NonDeletedExcalidrawElement | null,
|
||||||
@ -572,8 +572,7 @@ export const renderScene = (
|
|||||||
return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
|
return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
|
||||||
};
|
};
|
||||||
|
|
||||||
/** renderScene throttled to animation framerate */
|
const renderSceneThrottled = throttleRAF(
|
||||||
export const renderSceneThrottled = throttleRAF(
|
|
||||||
(
|
(
|
||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
@ -582,9 +581,9 @@ export const renderSceneThrottled = throttleRAF(
|
|||||||
rc: RoughCanvas,
|
rc: RoughCanvas,
|
||||||
canvas: HTMLCanvasElement,
|
canvas: HTMLCanvasElement,
|
||||||
renderConfig: RenderConfig,
|
renderConfig: RenderConfig,
|
||||||
callback?: (data: ReturnType<typeof renderScene>) => void,
|
callback?: (data: ReturnType<typeof _renderScene>) => void,
|
||||||
) => {
|
) => {
|
||||||
const ret = renderScene(
|
const ret = _renderScene(
|
||||||
elements,
|
elements,
|
||||||
appState,
|
appState,
|
||||||
selectionElement,
|
selectionElement,
|
||||||
@ -598,6 +597,46 @@ export const renderSceneThrottled = throttleRAF(
|
|||||||
{ trailing: true },
|
{ 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 = (
|
const renderTransformHandles = (
|
||||||
context: CanvasRenderingContext2D,
|
context: CanvasRenderingContext2D,
|
||||||
renderConfig: RenderConfig,
|
renderConfig: RenderConfig,
|
||||||
|
@ -39,7 +39,7 @@ const mouse = new Pointer("mouse");
|
|||||||
// Unmount ReactDOM from root
|
// Unmount ReactDOM from root
|
||||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
|
|
||||||
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
|
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
renderScene.mockClear();
|
renderScene.mockClear();
|
||||||
|
@ -14,7 +14,7 @@ import { reseed } from "../random";
|
|||||||
// Unmount ReactDOM from root
|
// Unmount ReactDOM from root
|
||||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
|
|
||||||
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
|
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
renderScene.mockClear();
|
renderScene.mockClear();
|
||||||
|
@ -16,7 +16,7 @@ import { KEYS } from "../keys";
|
|||||||
// Unmount ReactDOM from root
|
// Unmount ReactDOM from root
|
||||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
|
|
||||||
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
|
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
renderScene.mockClear();
|
renderScene.mockClear();
|
||||||
|
@ -14,7 +14,7 @@ import { reseed } from "../random";
|
|||||||
// Unmount ReactDOM from root
|
// Unmount ReactDOM from root
|
||||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
|
|
||||||
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
|
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
renderScene.mockClear();
|
renderScene.mockClear();
|
||||||
|
@ -20,7 +20,7 @@ import { t } from "../i18n";
|
|||||||
|
|
||||||
const { h } = window;
|
const { h } = window;
|
||||||
|
|
||||||
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
|
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||||
|
|
||||||
const mouse = new Pointer("mouse");
|
const mouse = new Pointer("mouse");
|
||||||
const finger1 = new Pointer("touch", 1);
|
const finger1 = new Pointer("touch", 1);
|
||||||
|
@ -18,7 +18,7 @@ const mouse = new Pointer("mouse");
|
|||||||
// Unmount ReactDOM from root
|
// Unmount ReactDOM from root
|
||||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
|
|
||||||
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
|
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
renderScene.mockClear();
|
renderScene.mockClear();
|
||||||
|
@ -16,7 +16,7 @@ import { Keyboard, Pointer } from "./helpers/ui";
|
|||||||
// Unmount ReactDOM from root
|
// Unmount ReactDOM from root
|
||||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||||
|
|
||||||
const renderScene = jest.spyOn(Renderer, "renderSceneThrottled");
|
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
renderScene.mockClear();
|
renderScene.mockClear();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user