Skip to content

Commit 57f6935

Browse files
committed
Adding willChange injection
1 parent 490a378 commit 57f6935

File tree

5 files changed

+162
-3
lines changed

5 files changed

+162
-3
lines changed

packages/framer-motion/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export { useTime } from "./value/use-time"
5555
export { useTransform } from "./value/use-transform"
5656
export { useVelocity } from "./value/use-velocity"
5757
export { useWillChange } from "./value/use-will-change"
58+
export { WillChangeMotionValue } from "./value/use-will-change/WillChangeMotionValue"
5859
export { resolveMotionValue } from "./value/utils/resolve-motion-value"
5960

6061
/**

packages/framer-motion/src/value/use-will-change/__tests__/will-change.test.tsx

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { frame, motion, useMotionValue, useWillChange } from "../../.."
1+
import {
2+
frame,
3+
motion,
4+
MotionGlobalConfig,
5+
useMotionValue,
6+
useWillChange,
7+
} from "../../.."
28
import { render } from "../../../../jest.setup"
39
import { nextFrame } from "../../../gestures/__tests__/utils"
410
import { WillChangeMotionValue } from "../WillChangeMotionValue"
@@ -159,3 +165,145 @@ describe("willChange", () => {
159165
expect(container.firstChild).toHaveStyle("will-change: transform;")
160166
})
161167
})
168+
169+
describe("willChange injection", () => {
170+
beforeAll(() => {
171+
MotionGlobalConfig.WillChange = WillChangeMotionValue
172+
})
173+
174+
afterAll(() => {
175+
MotionGlobalConfig.WillChange = undefined
176+
})
177+
178+
test("Renders values defined in animate on initial render", async () => {
179+
const Component = () => {
180+
const opacity = useMotionValue(0)
181+
return (
182+
<motion.div
183+
animate={{ x: 100, backgroundColor: "#000" }}
184+
style={{ opacity }}
185+
/>
186+
)
187+
}
188+
189+
const { container, rerender } = render(<Component />)
190+
rerender(<Component />)
191+
192+
await nextFrame()
193+
194+
expect(container.firstChild).toHaveStyle("will-change: transform;")
195+
})
196+
197+
test("Doesn't render CSS variables or non-hardware accelerated values", async () => {
198+
const Component = () => {
199+
return (
200+
<motion.div
201+
animate={
202+
{
203+
filter: "blur(10px)",
204+
background: "#000",
205+
"--test": "#000",
206+
} as any
207+
}
208+
/>
209+
)
210+
}
211+
212+
const { container } = render(<Component />)
213+
214+
await nextFrame()
215+
216+
expect(container.firstChild).toHaveStyle("will-change: filter;")
217+
})
218+
219+
test("Externally-provided motion values are not added to will-change", async () => {
220+
const Component = () => {
221+
const opacity = useMotionValue(0)
222+
const height = useMotionValue(100)
223+
return <motion.div style={{ opacity, height }} />
224+
}
225+
226+
const { container } = render(<Component />)
227+
228+
expect(container.firstChild).not.toHaveStyle("will-change: opacity;")
229+
expect(container.firstChild).not.toHaveStyle(
230+
"will-change: height, opacity;"
231+
)
232+
expect(container.firstChild).not.toHaveStyle(
233+
"will-change: opacity, height;"
234+
)
235+
})
236+
237+
test("Don't remove values when they finish animating", async () => {
238+
return new Promise<void>(async (resolve) => {
239+
const Component = () => {
240+
return (
241+
<motion.div
242+
transition={{ duration: 0.1 }}
243+
animate={{ x: 100 }}
244+
onAnimationComplete={() => {
245+
frame.postRender(() => {
246+
expect(container.firstChild).toHaveStyle(
247+
"will-change: transform;"
248+
)
249+
resolve()
250+
})
251+
}}
252+
/>
253+
)
254+
}
255+
256+
const { container } = render(<Component />)
257+
258+
await nextFrame()
259+
260+
expect(container.firstChild).toHaveStyle("will-change: transform;")
261+
})
262+
})
263+
264+
test("Add values when they start animating", async () => {
265+
const Component = ({ animate }: any) => {
266+
return (
267+
<motion.div
268+
initial={false}
269+
animate={animate}
270+
transition={{ duration: 0.1 }}
271+
/>
272+
)
273+
}
274+
const { container, rerender } = render(<Component animate={{}} />)
275+
await nextFrame()
276+
277+
expect(container.firstChild).not.toHaveStyle("will-change: transform;")
278+
rerender(<Component animate={{ x: 100 }} />)
279+
280+
await nextFrame()
281+
282+
expect(container.firstChild).toHaveStyle("will-change: transform;")
283+
})
284+
285+
test("Doesn't remove values when animation interrupted", async () => {
286+
const Component = ({ animate }: any) => {
287+
return (
288+
<motion.div
289+
initial={{ x: 0 }}
290+
animate={animate}
291+
transition={{ duration: 0.1 }}
292+
/>
293+
)
294+
}
295+
const { container, rerender } = render(<Component animate={{}} />)
296+
await nextFrame()
297+
298+
expect(container.firstChild).not.toHaveStyle("will-change: transform;")
299+
rerender(<Component animate={{ x: 100 }} />)
300+
301+
await nextFrame()
302+
303+
expect(container.firstChild).toHaveStyle("will-change: transform;")
304+
rerender(<Component animate={{ x: 200 }} />)
305+
306+
await nextFrame()
307+
expect(container.firstChild).toHaveStyle("will-change: transform;")
308+
})
309+
})

packages/framer-motion/src/value/use-will-change/add-will-change.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { MotionGlobalConfig } from "motion-utils"
12
import type { VisualElement } from "../../render/VisualElement"
23
import { isWillChangeMotionValue } from "./is"
34

@@ -13,5 +14,10 @@ export function addValueToWillChange(
1314
*/
1415
if (isWillChangeMotionValue(willChange)) {
1516
return willChange.add(key)
17+
} else if (!willChange && MotionGlobalConfig.WillChange) {
18+
const newWillChange = new MotionGlobalConfig.WillChange("auto")
19+
20+
visualElement.addValue("willChange", newWillChange)
21+
newWillChange.add(key)
1622
}
1723
}

packages/motion-utils/src/__tests__/velocity-per-second.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { velocityPerSecond } from "motion-utils"
1+
import { velocityPerSecond } from "../velocity-per-second"
22

33
test("velocityPerSecond", () => {
44
expect(velocityPerSecond(0.835, 16.7)).toBe(50)
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
export const MotionGlobalConfig = {
1+
export const MotionGlobalConfig: {
2+
skipAnimations: boolean
3+
useManualTiming: boolean
4+
WillChange?: any
5+
} = {
26
skipAnimations: false,
37
useManualTiming: false,
48
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy