From 69a299aa82da7f0afa87260bb87abb1f8fbb1fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rk=20Tolm=C3=A1cs?= Date: Mon, 10 Nov 2025 22:31:23 +0100 Subject: [PATCH] feat: Animation support (#10042) --- .../components/canvases/InteractiveCanvas.tsx | 30 +++++++++---------- .../excalidraw/renderer/interactiveScene.ts | 22 ++------------ packages/excalidraw/scene/Renderer.ts | 2 -- 3 files changed, 17 insertions(+), 37 deletions(-) diff --git a/packages/excalidraw/components/canvases/InteractiveCanvas.tsx b/packages/excalidraw/components/canvases/InteractiveCanvas.tsx index 05e19b17e8..32c118431e 100644 --- a/packages/excalidraw/components/canvases/InteractiveCanvas.tsx +++ b/packages/excalidraw/components/canvases/InteractiveCanvas.tsx @@ -8,6 +8,14 @@ import { } from "@excalidraw/common"; import { AnimationController } from "@excalidraw/excalidraw/renderer/animation"; +import type { + InteractiveCanvasRenderConfig, + InteractiveSceneRenderAnimationState, + InteractiveSceneRenderConfig, + RenderableElementsMap, + RenderInteractiveSceneCallback, +} from "@excalidraw/excalidraw/scene/types"; + import type { NonDeletedExcalidrawElement, NonDeletedSceneElementsMap, @@ -16,13 +24,6 @@ import type { import { t } from "../../i18n"; import { renderInteractiveScene } from "../../renderer/interactiveScene"; -import type { - InteractiveCanvasRenderConfig, - InteractiveSceneRenderAnimationState, - InteractiveSceneRenderConfig, - RenderableElementsMap, - RenderInteractiveSceneCallback, -} from "../../scene/types"; import type { AppClassProperties, AppState, @@ -148,7 +149,6 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => { allElementsMap: props.allElementsMap, scale: window.devicePixelRatio, appState: props.appState, - editorInterface: props.editorInterface, renderConfig: { remotePointerViewportCoords, remotePointerButton, @@ -160,6 +160,7 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => { // NOTE not memoized on so we don't rerender on cursor move lastViewportPosition: props.app.lastViewportPosition, }, + editorInterface: props.editorInterface, callback: props.renderInteractiveSceneCallback, animationState: { bindingHighlight: undefined, @@ -171,14 +172,11 @@ const InteractiveCanvas = (props: InteractiveCanvasProps) => { AnimationController.start( INTERACTIVE_SCENE_ANIMATION_KEY, ({ deltaTime, state }) => { - const nextAnimationState = renderInteractiveScene( - { - ...rendererParams.current!, - deltaTime, - animationState: state, - }, - false, - ).animationState; + const nextAnimationState = renderInteractiveScene({ + ...rendererParams.current!, + deltaTime, + animationState: state, + }).animationState; if (nextAnimationState) { for (const key in nextAnimationState) { diff --git a/packages/excalidraw/renderer/interactiveScene.ts b/packages/excalidraw/renderer/interactiveScene.ts index 5ee94a68ad..fd1cf60faa 100644 --- a/packages/excalidraw/renderer/interactiveScene.ts +++ b/packages/excalidraw/renderer/interactiveScene.ts @@ -16,7 +16,6 @@ import { getFeatureFlag, invariant, THEME, - throttleRAF, } from "@excalidraw/common"; import { @@ -1120,9 +1119,9 @@ const _renderInteractiveScene = ({ scale, appState, renderConfig, + editorInterface, animationState, deltaTime, - editorInterface, }: InteractiveSceneRenderConfig): { scrollBars?: ReturnType; 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 * and other ui stuff. */ export const renderInteractiveScene = < U extends typeof _renderInteractiveScene, - T extends boolean = false, >( renderConfig: InteractiveSceneRenderConfig, - throttle?: T, -): T extends true ? void : ReturnType => { - if (throttle) { - renderInteractiveSceneThrottled(renderConfig); - return undefined as T extends true ? void : ReturnType; - } +): ReturnType => { const ret = _renderInteractiveScene(renderConfig); renderConfig.callback(ret); - return ret as T extends true ? void : ReturnType; + return ret as ReturnType; }; diff --git a/packages/excalidraw/scene/Renderer.ts b/packages/excalidraw/scene/Renderer.ts index 6a016ccc1d..56ae3e5c58 100644 --- a/packages/excalidraw/scene/Renderer.ts +++ b/packages/excalidraw/scene/Renderer.ts @@ -10,7 +10,6 @@ import type { import type { Scene } from "@excalidraw/element"; -import { renderInteractiveSceneThrottled } from "../renderer/interactiveScene"; import { renderStaticSceneThrottled } from "../renderer/staticScene"; 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 // safe to break TS contract here (for upstream cases) public destroy() { - renderInteractiveSceneThrottled.cancel(); renderStaticSceneThrottled.cancel(); this.getRenderableElements.clear(); }