Skip to content

Commit

Permalink
Merge pull request #2646 from framer/fix/unit-conversion
Browse files Browse the repository at this point in the history
Fix animating between unit type and `0` with keyframes
  • Loading branch information
mergetron[bot] authored May 6, 2024
2 parents df6ccaf + 1e342fa commit bed68e4
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 6 deletions.
23 changes: 23 additions & 0 deletions dev/tests/unit-conversion-to-zero.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { animate } from "framer-motion"
import { useEffect } from "react"

/**
* An example of animating between different value types
*/

export const App = () => {
useEffect(() => {
animate("#box", { y: ["100%", 0] }, { duration: 10, ease: () => 0 })
}, [])

return (
<div
style={{
width: 100,
height: 100,
background: "#ffaa00",
}}
id="box"
/>
)
}
11 changes: 11 additions & 0 deletions packages/framer-motion/cypress/integration/unit-conversion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,15 @@ describe("Unit conversion", () => {
expect($box.textContent).to.equal("Success")
})
})

it("Coerces none keyframes before measuring", () => {
cy.viewport(400, 400)
.visit("?test=unit-conversion-to-zero")
.wait(100)
.get("#box")
.should(([$box]: any) => {
const { top } = $box.getBoundingClientRect()
expect(top).to.equal(100)
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,21 @@ export class DOMKeyframesResolver<
}
}

/**
* Resolve "none" values. We do this potentially twice - once before and once after measuring keyframes.
* This could be seen as inefficient but it's a trade-off to avoid measurements in more situations, which
* have a far bigger performance impact.
*/
this.resolveNoneKeyframes()

/**
* Check to see if unit type has changed. If so schedule jobs that will
* temporarily set styles to the destination keyframes.
* Skip if we have more than two keyframes or this isn't a positional value.
* TODO: We can throw if there are multiple keyframes and the value type changes.
*/
if (!positionalKeys.has(name) || unresolvedKeyframes.length !== 2) {
return this.resolveNoneKeyframes()
return
}

const [origin, target] = unresolvedKeyframes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { UnresolvedKeyframes } from "../../utils/KeyframesResolver"
* the "none" keyframes. In this case "#fff" or "200px 200px" - then these get turned into
* zero equivalents, i.e. "#fff0" or "0px 0px".
*/
const invalidTemplates = new Set(["auto", "none", "0"])

export function makeNoneKeyframesAnimatable(
unresolvedKeyframes: UnresolvedKeyframes<string | number>,
noneKeyframeIndexes: number[],
Expand All @@ -15,11 +17,8 @@ export function makeNoneKeyframesAnimatable(
let i = 0
let animatableTemplate: string | undefined = undefined
while (i < unresolvedKeyframes.length && !animatableTemplate) {
if (
typeof unresolvedKeyframes[i] === "string" &&
unresolvedKeyframes[i] !== "none" &&
unresolvedKeyframes[i] !== "0"
) {
const keyframe = unresolvedKeyframes[i]
if (typeof keyframe === "string" && !invalidTemplates.has(keyframe)) {
animatableTemplate = unresolvedKeyframes[i] as string
}
i++
Expand Down

0 comments on commit bed68e4

Please sign in to comment.