Skip to content

Commit

Permalink
Fixing start after hydration
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgperry committed Oct 4, 2024
1 parent 7eb6728 commit 5340a38
Show file tree
Hide file tree
Showing 6 changed files with 30 additions and 33 deletions.
2 changes: 1 addition & 1 deletion packages/framer-motion/cypress/fixtures/appear-tests.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
["defer-handoff-block.html","defer-handoff-external-values.html","defer-handoff-layout-ancestor-suspend.html","defer-handoff-layout-ancestor.html","defer-handoff-layout-child.html","defer-handoff-layout-opacity.html","defer-handoff-layout-sibling-transform.html","defer-handoff-layout-sibling.html","defer-handoff-layout-useeffect.html","defer-handoff-layout-uselayouteffect.html","defer-handoff-layout.html","defer-handoff.html","interrupt-delay-after.html","interrupt-delay-before-accelerated.html","interrupt-delay-before.html","interrupt-spring.html","interrupt-tween-opacity-waapi.html","interrupt-tween-opacity.html","interrupt-tween-transforms.html","interrupt-tween-x.html","persist-optimised-animation.html","persist.html","portal.html","resync-delay.html","resync.html","start-after-hydration.html"]
["defer-handoff-block.html","defer-handoff-external-values.html","defer-handoff-layout-ancestor-suspend.html","defer-handoff-layout-ancestor.html","defer-handoff-layout-child.html","defer-handoff-layout-opacity.html","defer-handoff-layout-sibling-transform.html","defer-handoff-layout-sibling.html","defer-handoff-layout-useeffect.html","defer-handoff-layout-uselayouteffect.html","defer-handoff-layout.html","defer-handoff-suspense.html","defer-handoff.html","interrupt-delay-after.html","interrupt-delay-before-accelerated.html","interrupt-delay-before.html","interrupt-spring.html","interrupt-tween-opacity-waapi.html","interrupt-tween-opacity.html","interrupt-tween-transforms.html","interrupt-tween-x.html","persist-optimised-animation.html","persist.html","portal.html","resync-delay.html","resync.html","start-after-hydration.html"]
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,7 @@ export function handoffOptimizedAppearAnimation(
*/
animation.onfinish = cancelAnimation

if (
startTime === null ||
window.MotionOptimisedAnimationHandedover?.(elementId)
) {
if (startTime === null || window.MotionHandoffIsComplete?.(elementId)) {
/**
* If the startTime is null, this animation is the Paint Ready detection animation
* and we can cancel it immediately without handoff.
Expand Down
24 changes: 9 additions & 15 deletions packages/framer-motion/src/animation/optimized-appear/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,7 @@ import { startWaapiAnimation } from "../animators/waapi"
import { NativeAnimationOptions } from "../animators/waapi/types"
import { optimizedAppearDataId } from "./data-id"
import { handoffOptimizedAppearAnimation } from "./handoff"
import {
appearAnimationStore,
AppearStoreEntry,
elementsWithAppearAnimations,
} from "./store"
import { appearAnimationStore, AppearStoreEntry, appearComplete } from "./store"
import { noop } from "../../utils/noop"
import "./types"
import { getOptimisedAppearId } from "./get-appear-id"
Expand Down Expand Up @@ -52,7 +48,7 @@ export function startOptimizedAppearAnimation(
onReady?: (animation: Animation) => void
): void {
// Prevent optimised appear animations if Motion has already started animating.
if (window.MotionOptimisedAnimationHandedover?.(optimizedAppearDataId)) {
if (window.MotionIsMounted) {
return
}

Expand Down Expand Up @@ -101,23 +97,21 @@ export function startOptimizedAppearAnimation(
* breakpoint.
*/
if (!valueName) {
return elementsWithAppearAnimations.has(elementId)
return appearComplete.has(elementId)
}

const animationId = appearStoreId(elementId, valueName)
return Boolean(appearAnimationStore.get(animationId))
}

window.MotionOptimisedAnimationHandoff = (elementId: string): void => {
if (elementsWithAppearAnimations.has(elementId)) {
elementsWithAppearAnimations.set(elementId, true)
window.MotionHandoffMarkAsComplete = (elementId: string): void => {
if (appearComplete.has(elementId)) {
appearComplete.set(elementId, true)
}
}

window.MotionOptimisedAnimationHandedover = (
elementId: string
): boolean => {
return elementsWithAppearAnimations.get(elementId) === true
window.MotionHandoffIsComplete = (elementId: string): boolean => {
return appearComplete.get(elementId) === true
}

/**
Expand Down Expand Up @@ -231,7 +225,7 @@ export function startOptimizedAppearAnimation(
if (onReady) onReady(appearAnimation)
}

elementsWithAppearAnimations.set(id, false)
appearComplete.set(id, false)

if (readyAnimation.ready) {
readyAnimation.ready.then(startAnimation).catch(noop)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ export interface AppearStoreEntry {
startTime: number | null
}

export const appearAnimationStore = new Map<string, AppearStoreEntry>()
export type AppearElementId = string

export const elementsWithAppearAnimations = new Map<string, boolean>() // optimisedAppearId, handedOff
export type IsComplete = boolean

export const appearAnimationStore = new Map<AppearElementId, AppearStoreEntry>()

export const appearComplete = new Map<AppearElementId, IsComplete>()
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export type HandoffFunction = (
declare global {
interface Window {
MotionHandoffAnimation?: HandoffFunction
MotionOptimisedAnimationHandoff?: (elementId: string) => void
MotionOptimisedAnimationHandedover?: (elementId: string) => boolean
MotionHandoffMarkAsComplete?: (elementId: string) => void
MotionHandoffIsComplete?: (elementId: string) => boolean
MotionHasOptimisedAnimation?: (
elementId?: string,
valueName?: string
Expand All @@ -45,5 +45,6 @@ declare global {
valueName: string,
value: MotionValue
) => VoidFunction | void
MotionIsMounted?: boolean
}
}
19 changes: 10 additions & 9 deletions packages/framer-motion/src/motion/utils/use-visual-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,15 @@ export function useVisualElement<Instance, RenderState>(
props[optimizedAppearDataAttribute as keyof typeof props]
const wantsHandoff = useRef(
Boolean(optimisedAppearId) &&
!window.MotionOptimisedAnimationHandedover?.(optimisedAppearId) &&
!window.MotionHandoffIsComplete?.(optimisedAppearId) &&
window.MotionHasOptimisedAnimation?.(optimisedAppearId)
)

useIsomorphicLayoutEffect(() => {
if (!visualElement) return

window.MotionIsMounted = true

visualElement.updateFeatures()

microtask.render(visualElement.render)
Expand All @@ -113,19 +115,18 @@ export function useVisualElement<Instance, RenderState>(
useEffect(() => {
if (!visualElement) return

const handoffNeeded = wantsHandoff.current
if (!handoffNeeded && visualElement.animationState) {
if (!wantsHandoff.current && visualElement.animationState) {
visualElement.animationState.animateChanges()
}

if (handoffNeeded) {
if (wantsHandoff.current) {
// This ensures all future calls to animateChanges() in this component will run in useEffect
queueMicrotask(() =>
window.MotionOptimisedAnimationHandoff?.(optimisedAppearId)
)
}
queueMicrotask(() => {
window.MotionHandoffMarkAsComplete?.(optimisedAppearId)
})

wantsHandoff.current = false
wantsHandoff.current = false
}
})

return visualElement
Expand Down

0 comments on commit 5340a38

Please sign in to comment.