Skip to content

Commit

Permalink
Allowing transition on initial (#3052)
Browse files Browse the repository at this point in the history
* Allowing transition on initial

* Reverting test
  • Loading branch information
mattgperry authored Feb 6, 2025
1 parent b85e3a9 commit 919b59c
Show file tree
Hide file tree
Showing 8 changed files with 72 additions and 13 deletions.
2 changes: 1 addition & 1 deletion dev/react/src/examples/Events-whileHover.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from "react";
import { motion } from "framer-motion"
import { useState } from "react"

export function App() {
const [scale, setScale] = useState(2)
Expand Down
8 changes: 4 additions & 4 deletions packages/framer-motion/src/animation/__tests__/index.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { render } from "../../../jest.setup"
import * as React from "react"
import { useEffect } from "react"
import { motion } from "../.."
import { useAnimation } from "../hooks/use-animation"
import { render } from "../../../jest.setup"
import { useMotionValue } from "../../value/use-motion-value"
import { useAnimation } from "../hooks/use-animation"

describe("useAnimation", () => {
test("animates on mount", async () => {
Expand Down Expand Up @@ -98,13 +98,13 @@ describe("useAnimation", () => {
initial="hidden"
animate="visible"
variants={variants}
transition={{ transition: false }}
transition={{ type: false }}
>
<motion.div
onAnimationComplete={(definition) =>
resolve(definition)
}
transition={{ transition: false }}
transition={{ type: false }}
variants={variants}
/>
</motion.div>
Expand Down
44 changes: 43 additions & 1 deletion packages/framer-motion/src/gestures/__tests__/hover.test.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { frame, motion } from "../../"
import {
pointerDown,
pointerEnter,
pointerLeave,
render,
} from "../../../jest.setup"
import { frame, motion } from "../../"
import { motionValue } from "../../value"
import { nextFrame } from "./utils"

Expand Down Expand Up @@ -170,6 +170,48 @@ describe("hover", () => {
return expect(promise).resolves.toBe(1)
})

test("Correctly uses transition applied to initial", () => {
const promise = new Promise(async (resolve) => {
const variant = {
initial: { opacity: 0.9, transition: { type: false } },
hidden: {
opacity: 0.5,
transition: { type: false },
transitionEnd: { opacity: 0.75 },
},
}
const opacity = motionValue(0.9)

let hasMousedOut = false
const onComplete = () => {
frame.postRender(() => hasMousedOut && resolve(opacity.get()))
}

const Component = ({ onAnimationComplete }: any) => (
<motion.div
whileHover="hidden"
variants={variant}
style={{ opacity }}
onAnimationComplete={onAnimationComplete}
/>
)

const { container } = render(
<Component onAnimationComplete={onComplete} />
)

pointerEnter(container.firstChild as Element)

await nextFrame()
setTimeout(() => {
hasMousedOut = true
pointerLeave(container.firstChild as Element)
}, 10)
})

return expect(promise).resolves.toBe(0.9)
})

test("whileHover only animates values that aren't being controlled by a higher-priority gesture ", () => {
const promise = new Promise(async (resolve) => {
const variant = {
Expand Down
4 changes: 2 additions & 2 deletions packages/framer-motion/src/gestures/__tests__/press.test.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { fireEvent } from "@testing-library/dom"
import { useState } from "react"
import { motion } from "../.."
import { motionValue } from "../../value"
import {
pointerDown,
pointerEnter,
pointerLeave,
pointerUp,
render,
} from "../../../jest.setup"
import { motionValue } from "../../value"
import { drag, MockDrag } from "../drag/__tests__/utils"
import { fireEvent } from "@testing-library/dom"
import { nextFrame } from "./utils"

const enterKey = {
Expand Down
2 changes: 1 addition & 1 deletion packages/framer-motion/src/motion/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ export interface AnimationProps {
* <motion.div initial={false} animate={{ opacity: 0 }} />
* ```
*/
initial?: boolean | Target | VariantLabels
initial?: TargetAndTransition | VariantLabels | boolean

/**
* Values to animate to, variant label(s), or `AnimationControls`.
Expand Down
21 changes: 19 additions & 2 deletions packages/framer-motion/src/render/utils/animation-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { isKeyframesTarget } from "../../animation/utils/is-keyframes-target"
import { VariantLabels } from "../../motion/types"
import { TargetAndTransition } from "../../types"
import { shallowCompare } from "../../utils/shallow-compare"
import { ResolvedValues } from "../types"
import type { VisualElement } from "../VisualElement"
import { getVariantContext } from "./get-variant-context"
import { isVariantLabel } from "./is-variant-label"
Expand Down Expand Up @@ -336,7 +335,25 @@ export function createAnimationState(
* defined in the style prop, or the last read value.
*/
if (removedKeys.size) {
const fallbackAnimation: ResolvedValues = {}
const fallbackAnimation: TargetAndTransition = {}

/**
* If the initial prop contains a transition we can use that, otherwise
* allow the animation function to use the visual element's default.
*/
if (typeof props.initial !== "boolean") {
const initialTransition = resolveVariant(
visualElement,
Array.isArray(props.initial)
? props.initial[0]
: props.initial
)

if (initialTransition && initialTransition.transition) {
fallbackAnimation.transition = initialTransition.transition
}
}

removedKeys.forEach((key) => {
const fallbackTarget = visualElement.getBaseTarget(key)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { resolveVariantFromProps } from "./resolve-variants"
*/
export function resolveVariant(
visualElement: VisualElement,
definition: TargetAndTransition | TargetResolver,
definition?: TargetAndTransition | TargetResolver,
custom?: any
): TargetAndTransition
export function resolveVariant(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { render } from "../../../../jest.setup"
import { frame, motion, useMotionValue, useWillChange } from "../../.."
import { render } from "../../../../jest.setup"
import { nextFrame } from "../../../gestures/__tests__/utils"
import { WillChangeMotionValue } from "../WillChangeMotionValue"

Expand Down

0 comments on commit 919b59c

Please sign in to comment.