Skip to content

Commit

Permalink
Removing background-color from willChange
Browse files Browse the repository at this point in the history
  • Loading branch information
mattgperry committed Jul 11, 2024
1 parent 4d1ec5d commit 1cdf396
Show file tree
Hide file tree
Showing 5 changed files with 325 additions and 18 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
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.

## [11.3.2] 2024-07-11

### Fixed

- No longer adding `background-color` to `will-change`.

## [11.3.1] 2024-07-11

### Updated
Expand Down
315 changes: 315 additions & 0 deletions dev/html/public/benchmarks/pregenerated-background-color.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,315 @@
<html>
<head>
<title>Pre-generated keyframes performance</title>
<!--
This file demonstrates that the performance of pre-generated keyframes is poor in Chrome
under surprising circumstances.
Specifically, pre-generated keyframes are fine in isolation. But when combined with a
requestAnimationFrame animation, AND another WAAPI animation, performance becomes
(literally) 100x worse.
To use:
1. Open this file in Chrome
2. Open performance tab and throttle 6x
3. On an M1 Pro notice style recalculations of ~170ms
4. Comment `animation.ready` block, disabling requestAnimationFrame animation
5. Profile and notice style recalculations of ~1.5ms
6. Undo changes
7. Comment out `opacity` WAAPI animation, moving `const animation = ` to background-color animation.
8. Profile and notice style recalculations of ~2.5ms
9. Undo changes
10. Replace `backgroundColor: pregeneratedKeyframes` with `backgroundColor: ["#f00", "#00f"]`
11. Profile and notice style recalculations of ~2.5ms
-->
<style>
body {
display: flex;
flex-wrap: wrap;
gap: 10px;
height: 100vh;
overflow: hidden;
}

.box {
width: 50px;
height: 50px;
background-color: #f00;
opacity: 0;
will-change: auto !important;
}
</style>
</head>
<body>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
<script type="module" src="/src/imports/framer-motion-dom.js"></script>
<script type="module">
const boxes = document.querySelectorAll(".box")
let startTime = 0
const duration = 10000
const rotateRate = 360 / duration

function rotate(timestamp) {
const elapsed = timestamp - startTime
boxes.forEach((box, index) => {
box.style.transform = `rotate(${elapsed * rotateRate}deg)`
})

requestAnimationFrame(rotate)
}

function animateRotation() {
startTime = performance.now()
requestAnimationFrame(rotate)
}
// animateRotation()

Motion.animate(
boxes,
{ opacity: [0, 1], backgroundColor: ["#f00", "#00f"] },
{ duration: duration / 1000, repeat: Infinity }
)
</script>
</body>
</html>
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ const maxDuration = 20_000
function requiresPregeneratedKeyframes<T extends string | number>(
options: ValueAnimationOptions<T>
) {
return (
options.type === "spring" ||
options.name === "backgroundColor" ||
!isWaapiSupportedEasing(options.ease)
)
return options.type === "spring" || !isWaapiSupportedEasing(options.ease)
}

function pregenerateKeyframes<T extends string | number>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,7 @@ describe("willChange", () => {

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

expect(container.firstChild).toHaveStyle(
"will-change: transform,background-color;"
)
expect(container.firstChild).toHaveStyle("will-change: transform;")
})

test("Static mode: Doesn't render values defined in animate on initial render", async () => {
Expand All @@ -64,9 +62,7 @@ describe("willChange", () => {

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

expect(container.firstChild).not.toHaveStyle(
"will-change: transform,background-color;"
)
expect(container.firstChild).not.toHaveStyle("will-change: transform;")
})

test("Renders values defined in animate on initial render", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ import { transformProps } from "../../render/html/utils/transform"
export function getWillChangeName(name: string): string | undefined {
if (transformProps.has(name)) {
return "transform"
} else if (
acceleratedValues.has(name) ||
// Manually check for backgroundColor as accelerated animations
// are currently disabled for this value (see `acceleratedValues`)
// but can still be put on the compositor.
name === "backgroundColor"
) {
} else if (acceleratedValues.has(name)) {
return camelToDash(name)
}
}

0 comments on commit 1cdf396

Please sign in to comment.