mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-11-28 00:26:21 +01:00
feat: Animation support (#10042)
This commit is contained in:
committed by
Mark Tolmacs
parent
f6fb3d294f
commit
69a299aa82
@@ -8,6 +8,14 @@ import {
|
|||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
import { AnimationController } from "@excalidraw/excalidraw/renderer/animation";
|
import { AnimationController } from "@excalidraw/excalidraw/renderer/animation";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
InteractiveCanvasRenderConfig,
|
||||||
|
InteractiveSceneRenderAnimationState,
|
||||||
|
InteractiveSceneRenderConfig,
|
||||||
|
RenderableElementsMap,
|
||||||
|
RenderInteractiveSceneCallback,
|
||||||
|
} from "@excalidraw/excalidraw/scene/types";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
NonDeletedExcalidrawElement,
|
NonDeletedExcalidrawElement,
|
||||||
NonDeletedSceneElementsMap,
|
NonDeletedSceneElementsMap,
|
||||||
@@ -16,13 +24,6 @@ import type {
|
|||||||
import { t } from "../../i18n";
|
import { t } from "../../i18n";
|
||||||
import { renderInteractiveScene } from "../../renderer/interactiveScene";
|
import { renderInteractiveScene } from "../../renderer/interactiveScene";
|
||||||
|
|
||||||
import type {
|
|
||||||
InteractiveCanvasRenderConfig,
|
|
||||||
InteractiveSceneRenderAnimationState,
|
|
||||||
InteractiveSceneRenderConfig,
|
|
||||||
RenderableElementsMap,
|
|
||||||
RenderInteractiveSceneCallback,
|
|
||||||
} from "../../scene/types";
|
|
||||||
import type {
|
import type {
|
||||||
AppClassProperties,
|
AppClassProperties,
|
||||||
AppState,
|
AppState,
|
||||||
@@ -148,7 +149,6 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => {
|
|||||||
allElementsMap: props.allElementsMap,
|
allElementsMap: props.allElementsMap,
|
||||||
scale: window.devicePixelRatio,
|
scale: window.devicePixelRatio,
|
||||||
appState: props.appState,
|
appState: props.appState,
|
||||||
editorInterface: props.editorInterface,
|
|
||||||
renderConfig: {
|
renderConfig: {
|
||||||
remotePointerViewportCoords,
|
remotePointerViewportCoords,
|
||||||
remotePointerButton,
|
remotePointerButton,
|
||||||
@@ -160,6 +160,7 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => {
|
|||||||
// NOTE not memoized on so we don't rerender on cursor move
|
// NOTE not memoized on so we don't rerender on cursor move
|
||||||
lastViewportPosition: props.app.lastViewportPosition,
|
lastViewportPosition: props.app.lastViewportPosition,
|
||||||
},
|
},
|
||||||
|
editorInterface: props.editorInterface,
|
||||||
callback: props.renderInteractiveSceneCallback,
|
callback: props.renderInteractiveSceneCallback,
|
||||||
animationState: {
|
animationState: {
|
||||||
bindingHighlight: undefined,
|
bindingHighlight: undefined,
|
||||||
@@ -171,14 +172,11 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => {
|
|||||||
AnimationController.start<InteractiveSceneRenderAnimationState>(
|
AnimationController.start<InteractiveSceneRenderAnimationState>(
|
||||||
INTERACTIVE_SCENE_ANIMATION_KEY,
|
INTERACTIVE_SCENE_ANIMATION_KEY,
|
||||||
({ deltaTime, state }) => {
|
({ deltaTime, state }) => {
|
||||||
const nextAnimationState = renderInteractiveScene(
|
const nextAnimationState = renderInteractiveScene({
|
||||||
{
|
...rendererParams.current!,
|
||||||
...rendererParams.current!,
|
deltaTime,
|
||||||
deltaTime,
|
animationState: state,
|
||||||
animationState: state,
|
}).animationState;
|
||||||
},
|
|
||||||
false,
|
|
||||||
).animationState;
|
|
||||||
|
|
||||||
if (nextAnimationState) {
|
if (nextAnimationState) {
|
||||||
for (const key in nextAnimationState) {
|
for (const key in nextAnimationState) {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
getFeatureFlag,
|
getFeatureFlag,
|
||||||
invariant,
|
invariant,
|
||||||
THEME,
|
THEME,
|
||||||
throttleRAF,
|
|
||||||
} from "@excalidraw/common";
|
} from "@excalidraw/common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -1120,9 +1119,9 @@ const _renderInteractiveScene = ({
|
|||||||
scale,
|
scale,
|
||||||
appState,
|
appState,
|
||||||
renderConfig,
|
renderConfig,
|
||||||
|
editorInterface,
|
||||||
animationState,
|
animationState,
|
||||||
deltaTime,
|
deltaTime,
|
||||||
editorInterface,
|
|
||||||
}: InteractiveSceneRenderConfig): {
|
}: InteractiveSceneRenderConfig): {
|
||||||
scrollBars?: ReturnType<typeof getScrollBars>;
|
scrollBars?: ReturnType<typeof getScrollBars>;
|
||||||
atLeastOneVisibleElement: boolean;
|
atLeastOneVisibleElement: boolean;
|
||||||
@@ -1607,31 +1606,16 @@ const _renderInteractiveScene = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/** throttled to animation framerate */
|
|
||||||
export const renderInteractiveSceneThrottled = throttleRAF(
|
|
||||||
(config: InteractiveSceneRenderConfig) => {
|
|
||||||
const ret = _renderInteractiveScene(config);
|
|
||||||
config.callback?.(ret);
|
|
||||||
},
|
|
||||||
{ trailing: true },
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interactive scene is the ui-canvas where we render bounding boxes, selections
|
* Interactive scene is the ui-canvas where we render bounding boxes, selections
|
||||||
* and other ui stuff.
|
* and other ui stuff.
|
||||||
*/
|
*/
|
||||||
export const renderInteractiveScene = <
|
export const renderInteractiveScene = <
|
||||||
U extends typeof _renderInteractiveScene,
|
U extends typeof _renderInteractiveScene,
|
||||||
T extends boolean = false,
|
|
||||||
>(
|
>(
|
||||||
renderConfig: InteractiveSceneRenderConfig,
|
renderConfig: InteractiveSceneRenderConfig,
|
||||||
throttle?: T,
|
): ReturnType<U> => {
|
||||||
): T extends true ? void : ReturnType<U> => {
|
|
||||||
if (throttle) {
|
|
||||||
renderInteractiveSceneThrottled(renderConfig);
|
|
||||||
return undefined as T extends true ? void : ReturnType<U>;
|
|
||||||
}
|
|
||||||
const ret = _renderInteractiveScene(renderConfig);
|
const ret = _renderInteractiveScene(renderConfig);
|
||||||
renderConfig.callback(ret);
|
renderConfig.callback(ret);
|
||||||
return ret as T extends true ? void : ReturnType<U>;
|
return ret as ReturnType<U>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import type {
|
|||||||
|
|
||||||
import type { Scene } from "@excalidraw/element";
|
import type { Scene } from "@excalidraw/element";
|
||||||
|
|
||||||
import { renderInteractiveSceneThrottled } from "../renderer/interactiveScene";
|
|
||||||
import { renderStaticSceneThrottled } from "../renderer/staticScene";
|
import { renderStaticSceneThrottled } from "../renderer/staticScene";
|
||||||
|
|
||||||
import type { RenderableElementsMap } from "./types";
|
import type { RenderableElementsMap } from "./types";
|
||||||
@@ -150,7 +149,6 @@ export class Renderer {
|
|||||||
// NOTE Doesn't destroy everything (scene, rc, etc.) because it may not be
|
// NOTE Doesn't destroy everything (scene, rc, etc.) because it may not be
|
||||||
// safe to break TS contract here (for upstream cases)
|
// safe to break TS contract here (for upstream cases)
|
||||||
public destroy() {
|
public destroy() {
|
||||||
renderInteractiveSceneThrottled.cancel();
|
|
||||||
renderStaticSceneThrottled.cancel();
|
renderStaticSceneThrottled.cancel();
|
||||||
this.getRenderableElements.clear();
|
this.getRenderableElements.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user