From 41b71b0698ea6e0b83afe8738b6f9872a188a39b Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Thu, 15 Aug 2024 12:01:14 +0200 Subject: [PATCH] Suspend optimized appear animations --- package.json | 2 +- .../src/animation/optimized-appear/start.ts | 24 ++++++++++++++++--- .../src/animation/optimized-appear/types.ts | 3 ++- .../projection/node/create-projection-node.ts | 12 ++++++---- 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 9a1a0437d1..2787ff28e7 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "test-e2e": "turbo run test-e2e", "test-ci": "turbo run test-ci --no-cache", "measure": "turbo run measure", - "prepare": "turbo run build measure test test-e2e", + "prepare": "turbo run build", "new": "lerna publish from-package", "new-alpha": "turbo run build && lerna publish from-package --canary --preid alpha" }, diff --git a/packages/framer-motion/src/animation/optimized-appear/start.ts b/packages/framer-motion/src/animation/optimized-appear/start.ts index 7862409992..504797863a 100644 --- a/packages/framer-motion/src/animation/optimized-appear/start.ts +++ b/packages/framer-motion/src/animation/optimized-appear/start.ts @@ -3,7 +3,11 @@ import { animateStyle } from "../animators/waapi" import { NativeAnimationOptions } from "../animators/waapi/types" import { optimizedAppearDataId } from "./data-id" import { handoffOptimizedAppearAnimation } from "./handoff" -import { appearAnimationStore, elementsWithAppearAnimations } from "./store" +import { + appearAnimationStore, + AppearStoreEntry, + elementsWithAppearAnimations, +} from "./store" import { noop } from "../../utils/noop" import "./types" @@ -22,6 +26,8 @@ let startFrameTime: number */ let readyAnimation: Animation +const suspendedAnimations = new Set() + export function startOptimizedAppearAnimation( element: HTMLElement, name: string, @@ -77,13 +83,25 @@ export function startOptimizedAppearAnimation( * they're the ones that will interfere with the * layout animation measurements. */ - window.MotionCancelOptimisedTransform = (elementId: string) => { + window.MotionSuspendOptimisedTransform = (elementId: string) => { const animationId = appearStoreId(elementId, "transform") const data = appearAnimationStore.get(animationId) if (data) { data.animation.cancel() - appearAnimationStore.delete(animationId) + suspendedAnimations.add(data) + } + + if (!window.MotionUnsuspendOptimisedTransform) { + window.MotionUnsuspendOptimisedTransform = () => { + suspendedAnimations.forEach((suspendedData) => { + suspendedData.animation.play() + suspendedData.animation.startTime = startFrameTime + }) + + suspendedAnimations.clear() + window.MotionUnsuspendOptimisedTransform = undefined + } } } diff --git a/packages/framer-motion/src/animation/optimized-appear/types.ts b/packages/framer-motion/src/animation/optimized-appear/types.ts index 02c941b9c1..de6b3b075c 100644 --- a/packages/framer-motion/src/animation/optimized-appear/types.ts +++ b/packages/framer-motion/src/animation/optimized-appear/types.ts @@ -14,8 +14,9 @@ declare global { interface Window { MotionHandoffAnimation?: HandoffFunction MotionHandoffIsComplete?: boolean - MotionCancelOptimisedTransform?: (id?: string) => void + MotionSuspendOptimisedTransform?: (id?: string) => void MotionHasOptimisedTransformAnimation?: (id?: string) => boolean MotionHasOptimisedAnimation?: (id?: string) => boolean + MotionUnsuspendOptimisedTransform?: VoidFunction } } diff --git a/packages/framer-motion/src/projection/node/create-projection-node.ts b/packages/framer-motion/src/projection/node/create-projection-node.ts index 3aaf7e08f1..2b6fc416fe 100644 --- a/packages/framer-motion/src/projection/node/create-projection-node.ts +++ b/packages/framer-motion/src/projection/node/create-projection-node.ts @@ -108,9 +108,10 @@ function resetDistortingTransform( } } -function cancelTreeOptimisedTransformAnimations( +function suspendTreeOptimisedTransformAnimations( projectionNode: IProjectionNode ) { + // TODO This needs to be reset if we're suspending animations projectionNode.hasCheckedOptimisedAppear = true if (projectionNode.root === projectionNode) return @@ -121,12 +122,12 @@ function cancelTreeOptimisedTransformAnimations( const appearId = getOptimisedAppearId(visualElement) if (window.MotionHasOptimisedTransformAnimation!(appearId)) { - window.MotionCancelOptimisedTransform!(appearId) + window.MotionSuspendOptimisedTransform!(appearId) } const { parent } = projectionNode if (parent && !parent.hasCheckedOptimisedAppear) { - cancelTreeOptimisedTransformAnimations(parent) + suspendTreeOptimisedTransformAnimations(parent) } } @@ -640,10 +641,10 @@ export function createProjectionNode({ * if a layout animation measurement is actually going to be affected by them. */ if ( - window.MotionCancelOptimisedTransform && + window.MotionSuspendOptimisedTransform && !this.hasCheckedOptimisedAppear ) { - cancelTreeOptimisedTransformAnimations(this) + suspendTreeOptimisedTransformAnimations(this) } !this.root.isUpdating && this.root.startUpdate() @@ -726,6 +727,7 @@ export function createProjectionNode({ frameData.isProcessing = true steps.update.process(frameData) steps.preRender.process(frameData) + window.MotionUnsuspendOptimisedTransform?.() steps.render.process(frameData) frameData.isProcessing = false }