feat: async renderScene
This commit is contained in:
parent
f2611f5d1f
commit
9e9f564ea9
@ -82,6 +82,7 @@ import {
|
||||
isElementInFrame,
|
||||
} from "../frame";
|
||||
import "canvas-roundrect-polyfill";
|
||||
import { Unpromisify } from "../utility-types";
|
||||
|
||||
export const DEFAULT_SPACING = 2;
|
||||
|
||||
@ -577,7 +578,7 @@ export const paintBackground = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const _renderScene = ({
|
||||
export const _renderScene = async ({
|
||||
elements,
|
||||
appState,
|
||||
scale,
|
||||
@ -591,11 +592,14 @@ export const _renderScene = ({
|
||||
rc: RoughCanvas;
|
||||
canvas: HTMLCanvasElement;
|
||||
renderConfig: RenderConfig;
|
||||
}) =>
|
||||
}): Promise<{
|
||||
atLeastOneVisibleElement: boolean;
|
||||
scrollBars: ScrollBars | undefined;
|
||||
}> =>
|
||||
// extra options passed to the renderer
|
||||
{
|
||||
if (canvas === null) {
|
||||
return { atLeastOneVisibleElement: false };
|
||||
return { atLeastOneVisibleElement: false, scrollBars: undefined };
|
||||
}
|
||||
const {
|
||||
renderScrollbars = false,
|
||||
@ -621,19 +625,16 @@ export const _renderScene = ({
|
||||
context.filter = THEME_FILTER;
|
||||
}
|
||||
|
||||
let renderOutput: {
|
||||
atLeastOneVisibleElement: boolean;
|
||||
scrollBars: ScrollBars | undefined;
|
||||
} = {
|
||||
atLeastOneVisibleElement: false,
|
||||
scrollBars: undefined,
|
||||
};
|
||||
|
||||
paintBackground(context, normalizedCanvasWidth, normalizedCanvasHeight, {
|
||||
await paintBackground(
|
||||
context,
|
||||
normalizedCanvasWidth,
|
||||
normalizedCanvasHeight,
|
||||
{
|
||||
isExporting,
|
||||
viewBackgroundColor,
|
||||
exportBackgroundImage,
|
||||
}).then(() => {
|
||||
},
|
||||
);
|
||||
// Apply zoom
|
||||
context.save();
|
||||
context.scale(renderConfig.zoom.value, renderConfig.zoom.value);
|
||||
@ -653,18 +654,13 @@ export const _renderScene = ({
|
||||
|
||||
// Paint visible elements
|
||||
const visibleElements = elements.filter((element) =>
|
||||
isVisibleElement(
|
||||
element,
|
||||
normalizedCanvasWidth,
|
||||
normalizedCanvasHeight,
|
||||
{
|
||||
isVisibleElement(element, normalizedCanvasWidth, normalizedCanvasHeight, {
|
||||
zoom: renderConfig.zoom,
|
||||
offsetLeft: appState.offsetLeft,
|
||||
offsetTop: appState.offsetTop,
|
||||
scrollX: renderConfig.scrollX,
|
||||
scrollY: renderConfig.scrollY,
|
||||
},
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
if (isExporting && exportBackgroundImage) {
|
||||
@ -707,9 +703,8 @@ export const _renderScene = ({
|
||||
}
|
||||
});
|
||||
|
||||
let editingLinearElement:
|
||||
| NonDeleted<ExcalidrawLinearElement>
|
||||
| undefined = undefined;
|
||||
let editingLinearElement: NonDeleted<ExcalidrawLinearElement> | undefined =
|
||||
undefined;
|
||||
|
||||
visibleElements
|
||||
.filter((el) => !isEmbeddableOrFrameLabel(el))
|
||||
@ -863,8 +858,7 @@ export const _renderScene = ({
|
||||
// correct element from visible elements
|
||||
if (
|
||||
locallySelectedElements.length === 1 &&
|
||||
appState.editingLinearElement?.elementId ===
|
||||
locallySelectedElements[0].id
|
||||
appState.editingLinearElement?.elementId === locallySelectedElements[0].id
|
||||
) {
|
||||
renderLinearPointHandles(
|
||||
context,
|
||||
@ -1213,36 +1207,30 @@ export const _renderScene = ({
|
||||
|
||||
context.restore();
|
||||
|
||||
renderOutput = {
|
||||
return {
|
||||
atLeastOneVisibleElement: visibleElements.length > 0,
|
||||
scrollBars,
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
atLeastOneVisibleElement: renderOutput.atLeastOneVisibleElement,
|
||||
scrollBars: renderOutput.scrollBars ?? undefined,
|
||||
};
|
||||
};
|
||||
|
||||
const renderSceneThrottled = throttleRAF(
|
||||
(config: {
|
||||
async (config: {
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
appState: AppState;
|
||||
scale: number;
|
||||
rc: RoughCanvas;
|
||||
canvas: HTMLCanvasElement;
|
||||
renderConfig: RenderConfig;
|
||||
callback?: (data: ReturnType<typeof _renderScene>) => void;
|
||||
callback?: (data: Unpromisify<ReturnType<typeof _renderScene>>) => void;
|
||||
}) => {
|
||||
const ret = _renderScene(config);
|
||||
const ret = await _renderScene(config);
|
||||
config.callback?.(ret);
|
||||
},
|
||||
{ trailing: true },
|
||||
);
|
||||
|
||||
/** renderScene throttled to animation framerate */
|
||||
export const renderScene = <T extends boolean = false>(
|
||||
export const renderScene = async <T extends boolean = false>(
|
||||
config: {
|
||||
elements: readonly NonDeletedExcalidrawElement[];
|
||||
appState: AppState;
|
||||
@ -1250,19 +1238,25 @@ export const renderScene = <T extends boolean = false>(
|
||||
rc: RoughCanvas;
|
||||
canvas: HTMLCanvasElement;
|
||||
renderConfig: RenderConfig;
|
||||
callback?: (data: ReturnType<typeof _renderScene>) => void;
|
||||
callback?: (data: Unpromisify<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> => {
|
||||
): Promise<
|
||||
T extends true ? void : Unpromisify<ReturnType<typeof _renderScene>>
|
||||
> => {
|
||||
if (throttle) {
|
||||
renderSceneThrottled(config);
|
||||
return undefined as T extends true ? void : ReturnType<typeof _renderScene>;
|
||||
return undefined as T extends true
|
||||
? void
|
||||
: Unpromisify<ReturnType<typeof _renderScene>>;
|
||||
}
|
||||
const ret = _renderScene(config);
|
||||
const ret = await _renderScene(config);
|
||||
config.callback?.(ret);
|
||||
return ret as T extends true ? void : ReturnType<typeof _renderScene>;
|
||||
return ret as T extends true
|
||||
? void
|
||||
: Unpromisify<ReturnType<typeof _renderScene>>;
|
||||
};
|
||||
|
||||
const renderTransformHandles = (
|
||||
|
@ -54,7 +54,7 @@ export const exportToCanvas = async (
|
||||
|
||||
const onlyExportingSingleFrame = isOnlyExportingSingleFrame(elements);
|
||||
|
||||
renderScene({
|
||||
await renderScene({
|
||||
elements,
|
||||
appState,
|
||||
scale,
|
||||
|
@ -27,7 +27,7 @@ export type RenderConfig = {
|
||||
/** when exporting the behavior is slightly different (e.g. we can't use
|
||||
CSS filters), and we disable render optimizations for best output */
|
||||
isExporting: boolean;
|
||||
exportBackgroundImage?: string;
|
||||
exportBackgroundImage?: string | null;
|
||||
selectionColor?: string;
|
||||
};
|
||||
|
||||
|
@ -54,3 +54,5 @@ export type Assert<T extends true> = T;
|
||||
export type NestedKeyOf<T, K = keyof T> = K extends keyof T & (string | number)
|
||||
? `${K}` | (T[K] extends object ? `${K}.${NestedKeyOf<T[K]>}` : never)
|
||||
: never;
|
||||
|
||||
export type Unpromisify<T> = T extends Promise<infer U> ? U : T;
|
||||
|
Loading…
x
Reference in New Issue
Block a user