Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map WAAPI easing names #1829

Merged
merged 5 commits into from Dec 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,12 @@ Framer Motion adheres to [Semantic Versioning](http://semver.org/).

Undocumented APIs should be considered internal and may change without warning.

## [7.9.1] 2022-12-14

### Fixed

- Fixing mapping Framer Motion easing names to WAAPI.

## [7.9.0] 2022-12-14

### Added
Expand Down
23 changes: 23 additions & 0 deletions dev/examples/WAAPI-background-color.tsx
@@ -0,0 +1,23 @@
import * as React from "react"
import { useState } from "react"
import { motion } from "framer-motion"

const style = {
width: 100,
height: 100,
background: "white",
}

export const App = () => {
const [state, setState] = useState(false)

return (
<motion.div
initial={{ backgroundColor: "#00f" }}
animate={{ backgroundColor: state ? "#00f" : "#f00" }}
onClick={() => setState(!state)}
transition={{ duration: 1 }}
style={style}
/>
)
}
2 changes: 1 addition & 1 deletion packages/framer-motion/package.json
Expand Up @@ -39,7 +39,7 @@
"clean": "rm -rf types dist lib",
"test": "yarn test-server && yarn test-client",
"test-ci": "yarn test",
"test-client": "jest --config jest.config.json --max-workers=2 src/motion/__tests__/waapi.test.tsx",
"test-client": "jest --config jest.config.json --max-workers=2",
"test-server": "jest --config jest.config.ssr.json ",
"test-watch": "jest --watch --coverage --coverageReporters=lcov --config jest.config.json",
"test-appear": "yarn run collect-appear-tests && start-server-and-test 'pushd ../../; python -m SimpleHTTPServer; popd' http://0.0.0.0:8000 'cypress run -s cypress/integration/appear.chrome.ts --config baseUrl=http://localhost:8000/'",
Expand Down
24 changes: 23 additions & 1 deletion packages/framer-motion/src/animation/waapi/easing.ts
@@ -1,4 +1,26 @@
import { BezierDefinition } from "../../easing/types"
import { BezierDefinition, EasingDefinition } from "../../easing/types"
import { camelToDash } from "../../render/dom/utils/camel-to-dash"

export const cubicBezierAsString = ([a, b, c, d]: BezierDefinition) =>
`cubic-bezier(${a}, ${b}, ${c}, ${d})`

const validWaapiEasing = new Set([
"linear",
"ease-in",
"ease-out",
"ease-in-out",
])

export function mapEasingName(easingName: string): string {
const name = camelToDash(easingName)
return validWaapiEasing.has(name) ? name : "ease"
}

export function mapEasingToNativeEasing(
easing?: EasingDefinition
): string | undefined {
if (!easing) return undefined
return Array.isArray(easing)
? cubicBezierAsString(easing)
: mapEasingName(easing)
}
4 changes: 2 additions & 2 deletions packages/framer-motion/src/animation/waapi/index.ts
@@ -1,4 +1,4 @@
import { cubicBezierAsString } from "./easing"
import { mapEasingToNativeEasing } from "./easing"
import { NativeAnimationOptions } from "./types"

export function animateStyle(
Expand All @@ -19,7 +19,7 @@ export function animateStyle(
{
delay,
duration,
easing: Array.isArray(ease) ? cubicBezierAsString(ease) : ease,
easing: mapEasingToNativeEasing(ease),
fill: "both",
iterations: repeat + 1,
direction: repeatType === "reverse" ? "alternate" : "normal",
Expand Down
120 changes: 120 additions & 0 deletions packages/framer-motion/src/motion/__tests__/waapi.test.tsx
Expand Up @@ -74,6 +74,126 @@ describe("WAAPI animations", () => {
)
})

test("Maps 'easeIn' to 'ease-in'", () => {
const ref = createRef<HTMLDivElement>()
const Component = () => (
<motion.div
ref={ref}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
ease: "easeIn",
}}
/>
)
const { rerender } = render(<Component />)
rerender(<Component />)

expect(ref.current!.animate).toBeCalled()
expect(ref.current!.animate).toBeCalledWith(
{ opacity: [0, 1], offset: undefined },
{
easing: "ease-in",
delay: -0,
duration: 0.3,
direction: "normal",
fill: "both",
iterations: 1,
}
)
})

test("Maps 'easeOut' to 'ease-out'", () => {
const ref = createRef<HTMLDivElement>()
const Component = () => (
<motion.div
ref={ref}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
ease: "easeOut",
}}
/>
)
const { rerender } = render(<Component />)
rerender(<Component />)

expect(ref.current!.animate).toBeCalled()
expect(ref.current!.animate).toBeCalledWith(
{ opacity: [0, 1], offset: undefined },
{
easing: "ease-out",
delay: -0,
duration: 0.3,
direction: "normal",
fill: "both",
iterations: 1,
}
)
})

test("Maps 'easeInOut' to 'ease-in-out'", () => {
const ref = createRef<HTMLDivElement>()
const Component = () => (
<motion.div
ref={ref}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
ease: "easeInOut",
}}
/>
)
const { rerender } = render(<Component />)
rerender(<Component />)

expect(ref.current!.animate).toBeCalled()
expect(ref.current!.animate).toBeCalledWith(
{ opacity: [0, 1], offset: undefined },
{
easing: "ease-in-out",
delay: -0,
duration: 0.3,
direction: "normal",
fill: "both",
iterations: 1,
}
)
})

/**
* TODO: Wait for comments and either bump these back to the main thread,
* generate keyframes, generate linear() easing, or create similar cubic beziers.
*/
test("Maps remaining easings to 'ease'", () => {
const ref = createRef<HTMLDivElement>()
const Component = () => (
<motion.div
ref={ref}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
ease: "anticipate",
}}
/>
)
const { rerender } = render(<Component />)
rerender(<Component />)

expect(ref.current!.animate).toBeCalled()
expect(ref.current!.animate).toBeCalledWith(
{ opacity: [0, 1], offset: undefined },
{
easing: "ease",
delay: -0,
duration: 0.3,
direction: "normal",
fill: "both",
iterations: 1,
}
)
})

test("WAAPI is called with pre-generated spring keyframes", () => {
const ref = createRef<HTMLDivElement>()
const Component = () => (
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Expand Up @@ -7909,8 +7909,8 @@ __metadata:
cache-loader: ^1.2.5
convert-tsconfig-paths-to-webpack-aliases: ^0.9.2
fork-ts-checker-webpack-plugin: ^6.2.0
framer-motion: ^7.8.1
framer-motion-3d: ^7.8.1
framer-motion: ^7.9.0
framer-motion-3d: ^7.9.0
path-browserify: ^1.0.1
react: ^18.2.0
react-dom: ^18.2.0
Expand Down Expand Up @@ -7976,14 +7976,14 @@ __metadata:
languageName: unknown
linkType: soft

"framer-motion-3d@^7.8.1, framer-motion-3d@workspace:packages/framer-motion-3d":
"framer-motion-3d@^7.9.0, framer-motion-3d@workspace:packages/framer-motion-3d":
version: 0.0.0-use.local
resolution: "framer-motion-3d@workspace:packages/framer-motion-3d"
dependencies:
"@react-three/fiber": ^8.2.2
"@react-three/test-renderer": ^9.0.0
"@rollup/plugin-commonjs": ^22.0.1
framer-motion: ^7.8.1
framer-motion: ^7.9.0
react-merge-refs: ^2.0.1
peerDependencies:
"@react-three/fiber": ^8.2.2
Expand All @@ -7993,7 +7993,7 @@ __metadata:
languageName: unknown
linkType: soft

"framer-motion@^7.8.1, framer-motion@workspace:packages/framer-motion":
"framer-motion@^7.9.0, framer-motion@workspace:packages/framer-motion":
version: 0.0.0-use.local
resolution: "framer-motion@workspace:packages/framer-motion"
dependencies:
Expand Down