refactor: cleanup renderScene (#5573)

* refactor: cleanup renderScene

* pass object instead of individual params
This commit is contained in:
Aakansha Doshi 2022-08-16 16:09:53 +05:30 committed by GitHub
parent c37977af4b
commit fd946adbae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 511 additions and 517 deletions

View File

@ -1165,7 +1165,23 @@ class App extends React.Component<AppProps, AppState> {
), ),
); );
} }
this.renderScene();
this.history.record(this.state, this.scene.getElementsIncludingDeleted());
// Do not notify consumers if we're still loading the scene. Among other
// potential issues, this fixes a case where the tab isn't focused during
// init, which would trigger onChange with empty elements, which would then
// override whatever is in localStorage currently.
if (!this.state.isLoading) {
this.props.onChange?.(
this.scene.getElementsIncludingDeleted(),
this.state,
this.files,
);
}
}
private renderScene = () => {
const cursorButton: { const cursorButton: {
[id: string]: string | undefined; [id: string]: string | undefined;
} = {}; } = {};
@ -1202,6 +1218,7 @@ class App extends React.Component<AppProps, AppState> {
); );
cursorButton[socketId] = user.button; cursorButton[socketId] = user.button;
}); });
const renderingElements = this.scene const renderingElements = this.scene
.getNonDeletedElements() .getNonDeletedElements()
.filter((element) => { .filter((element) => {
@ -1223,13 +1240,13 @@ class App extends React.Component<AppProps, AppState> {
}); });
renderScene( renderScene(
renderingElements,
this.state,
this.state.selectionElement,
window.devicePixelRatio,
this.rc!,
this.canvas!,
{ {
elements: renderingElements,
appState: this.state,
scale: window.devicePixelRatio,
rc: this.rc!,
canvas: this.canvas!,
renderConfig: {
scrollX: this.state.scrollX, scrollX: this.state.scrollX,
scrollY: this.state.scrollY, scrollY: this.state.scrollY,
viewBackgroundColor: this.state.viewBackgroundColor, viewBackgroundColor: this.state.viewBackgroundColor,
@ -1245,7 +1262,7 @@ class App extends React.Component<AppProps, AppState> {
isExporting: false, isExporting: false,
renderScrollbars: !this.device.isMobile, renderScrollbars: !this.device.isMobile,
}, },
({ atLeastOneVisibleElement, scrollBars }) => { callback: ({ atLeastOneVisibleElement, scrollBars }) => {
if (scrollBars) { if (scrollBars) {
currentScrollBars = scrollBars; currentScrollBars = scrollBars;
} }
@ -1260,27 +1277,14 @@ class App extends React.Component<AppProps, AppState> {
this.scheduleImageRefresh(); this.scheduleImageRefresh();
}, },
},
THROTTLE_NEXT_RENDER && window.EXCALIDRAW_THROTTLE_RENDER === true, THROTTLE_NEXT_RENDER && window.EXCALIDRAW_THROTTLE_RENDER === true,
); );
if (!THROTTLE_NEXT_RENDER) { if (!THROTTLE_NEXT_RENDER) {
THROTTLE_NEXT_RENDER = true; 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
// potential issues, this fixes a case where the tab isn't focused during
// init, which would trigger onChange with empty elements, which would then
// override whatever is in localStorage currently.
if (!this.state.isLoading) {
this.props.onChange?.(
this.scene.getElementsIncludingDeleted(),
this.state,
this.files,
);
}
}
private onScroll = debounce(() => { private onScroll = debounce(() => {
const { offsetTop, offsetLeft } = this.getCanvasOffsets(); const { offsetTop, offsetLeft } = this.getCanvasOffsets();

View File

@ -284,20 +284,26 @@ const renderLinearElementPointHighlight = (
context.restore(); context.restore();
}; };
export const _renderScene = ( export const _renderScene = ({
elements: readonly NonDeletedExcalidrawElement[], elements,
appState: AppState, appState,
selectionElement: NonDeletedExcalidrawElement | null, scale,
scale: number, rc,
rc: RoughCanvas, canvas,
canvas: HTMLCanvasElement, renderConfig,
renderConfig: RenderConfig, }: {
elements: readonly NonDeletedExcalidrawElement[];
appState: AppState;
scale: number;
rc: RoughCanvas;
canvas: HTMLCanvasElement;
renderConfig: RenderConfig;
}) =>
// extra options passed to the renderer // extra options passed to the renderer
) => { {
if (canvas === null) { if (canvas === null) {
return { atLeastOneVisibleElement: false }; return { atLeastOneVisibleElement: false };
} }
const { const {
renderScrollbars = true, renderScrollbars = true,
renderSelection = true, renderSelection = true,
@ -389,9 +395,9 @@ export const _renderScene = (
} }
// Paint selection element // Paint selection element
if (selectionElement) { if (appState.selectionElement) {
try { try {
renderElement(selectionElement, rc, context, renderConfig); renderElement(appState.selectionElement, rc, context, renderConfig);
} catch (error: any) { } catch (error: any) {
console.error(error); console.error(error);
} }
@ -503,7 +509,9 @@ export const _renderScene = (
context, context,
renderConfig, renderConfig,
selection, selection,
isSingleLinearElementSelected ? DEFAULT_SPACING * 2 : DEFAULT_SPACING, isSingleLinearElementSelected
? DEFAULT_SPACING * 2
: DEFAULT_SPACING,
), ),
); );
} }
@ -706,70 +714,45 @@ export const _renderScene = (
context.restore(); context.restore();
return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars }; return { atLeastOneVisibleElement: visibleElements.length > 0, scrollBars };
}; };
const renderSceneThrottled = throttleRAF( const renderSceneThrottled = throttleRAF(
( (config: {
elements: readonly NonDeletedExcalidrawElement[], elements: readonly NonDeletedExcalidrawElement[];
appState: AppState, appState: AppState;
selectionElement: NonDeletedExcalidrawElement | null, scale: number;
scale: number, 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(config);
const ret = _renderScene( config.callback?.(ret);
elements,
appState,
selectionElement,
scale,
rc,
canvas,
renderConfig,
);
callback?.(ret);
}, },
{ trailing: true }, { trailing: true },
); );
/** renderScene throttled to animation framerate */ /** renderScene throttled to animation framerate */
export const renderScene = <T extends boolean = false>( export const renderScene = <T extends boolean = false>(
elements: readonly NonDeletedExcalidrawElement[], config: {
appState: AppState, elements: readonly NonDeletedExcalidrawElement[];
selectionElement: NonDeletedExcalidrawElement | null, appState: AppState;
scale: number, scale: number;
rc: RoughCanvas, rc: RoughCanvas;
canvas: HTMLCanvasElement, canvas: HTMLCanvasElement;
renderConfig: RenderConfig, renderConfig: RenderConfig;
callback?: (data: ReturnType<typeof _renderScene>) => void, callback?: (data: ReturnType<typeof _renderScene>) => void;
},
/** Whether to throttle rendering. Defaults to false. /** Whether to throttle rendering. Defaults to false.
* When throttling, no value is returned. Use the callback instead. */ * When throttling, no value is returned. Use the callback instead. */
throttle?: T, throttle?: T,
): T extends true ? void : ReturnType<typeof _renderScene> => { ): T extends true ? void : ReturnType<typeof _renderScene> => {
if (throttle) { if (throttle) {
renderSceneThrottled( renderSceneThrottled(config);
elements,
appState,
selectionElement,
scale,
rc,
canvas,
renderConfig,
callback,
);
return undefined as T extends true ? void : ReturnType<typeof _renderScene>; return undefined as T extends true ? void : ReturnType<typeof _renderScene>;
} }
const ret = _renderScene( const ret = _renderScene(config);
elements, config.callback?.(ret);
appState,
selectionElement,
scale,
rc,
canvas,
renderConfig,
);
callback?.(ret);
return ret as T extends true ? void : ReturnType<typeof _renderScene>; return ret as T extends true ? void : ReturnType<typeof _renderScene>;
}; };

View File

@ -51,7 +51,13 @@ export const exportToCanvas = async (
files, files,
}); });
renderScene(elements, appState, null, scale, rough.canvas(canvas), canvas, { renderScene({
elements,
appState,
scale,
rc: rough.canvas(canvas),
canvas,
renderConfig: {
viewBackgroundColor: exportBackground ? viewBackgroundColor : null, viewBackgroundColor: exportBackground ? viewBackgroundColor : null,
scrollX: -minX + exportPadding, scrollX: -minX + exportPadding,
scrollY: -minY + exportPadding, scrollY: -minY + exportPadding,
@ -67,6 +73,7 @@ export const exportToCanvas = async (
renderSelection: false, renderSelection: false,
renderGrid: false, renderGrid: false,
isExporting: true, isExporting: true,
},
}); });
return canvas; return canvas;