Skip to content

Commit

Permalink
Stop removing values from will-change once they finish animating (#…
Browse files Browse the repository at this point in the history
…2811)

* Stop removing will-change when animations finish

* Updating filesize

* Fixing tests
  • Loading branch information
mattgperry authored Sep 25, 2024
1 parent cec5d8f commit c186b5a
Show file tree
Hide file tree
Showing 8 changed files with 27 additions and 76 deletions.
8 changes: 4 additions & 4 deletions packages/framer-motion/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,23 +98,23 @@
"bundlesize": [
{
"path": "./dist/size-rollup-motion.js",
"maxSize": "33.95 kB"
"maxSize": "33.82 kB"
},
{
"path": "./dist/size-rollup-m.js",
"maxSize": "5.9 kB"
},
{
"path": "./dist/size-rollup-dom-animation.js",
"maxSize": "16.9 kB"
"maxSize": "16.8 kB"
},
{
"path": "./dist/size-rollup-dom-max.js",
"maxSize": "29 kB"
"maxSize": "2.82 kB"
},
{
"path": "./dist/size-rollup-animate.js",
"maxSize": "17.9 kB"
"maxSize": "17.7 kB"
},
{
"path": "./dist/size-rollup-scroll.js",
Expand Down
11 changes: 1 addition & 10 deletions packages/framer-motion/src/animation/interfaces/motion-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,7 @@ export const animateMotionValue =
target: V | UnresolvedKeyframes<V>,
transition: Transition & { elapsed?: number } = {},
element?: VisualElement<any>,
isHandoff?: boolean,
/**
* Currently used to remove values from will-change when an animation ends.
* Preferably this would be handled by event listeners on the MotionValue
* but these aren't consistent enough yet when considering the different ways
* an animation can be cancelled.
*/
onEnd?: VoidFunction
isHandoff?: boolean
): StartAnimation =>
(onComplete): AnimationPlaybackControls => {
const valueTransition = getValueTransition(transition, name) || {}
Expand Down Expand Up @@ -60,9 +53,7 @@ export const animateMotionValue =
onComplete: () => {
onComplete()
valueTransition.onComplete && valueTransition.onComplete()
onEnd && onEnd()
},
onStop: onEnd,
name,
motionValue: value,
element: isHandoff ? undefined : element,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ export function animateTarget(
}
}

addValueToWillChange(visualElement, key)

value.start(
animateMotionValue(
key,
Expand All @@ -99,8 +101,7 @@ export function animateTarget(
? { type: false }
: valueTransition,
visualElement,
isHandoff,
addValueToWillChange(visualElement, key)
isHandoff
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ export class VisualElementDragControls {
*/
private elastic = createBox()

private removeWillChange: VoidFunction | undefined

constructor(visualElement: VisualElement<HTMLElement>) {
this.visualElement = visualElement
}
Expand Down Expand Up @@ -160,11 +158,7 @@ export class VisualElementDragControls {
frame.postRender(() => onDragStart(event, info))
}

this.removeWillChange?.()
this.removeWillChange = addValueToWillChange(
this.visualElement,
"transform"
)
addValueToWillChange(this.visualElement, "transform")

const { animationState } = this.visualElement
animationState && animationState.setActive("whileDrag", true)
Expand Down Expand Up @@ -244,8 +238,6 @@ export class VisualElementDragControls {
}

private stop(event: PointerEvent, info: PanInfo) {
this.removeWillChange?.()

const isDragging = this.isDragging
this.cancel()
if (!isDragging) return
Expand Down Expand Up @@ -455,15 +447,16 @@ export class VisualElementDragControls {
) {
const axisValue = this.getAxisMotionValue(axis)

addValueToWillChange(this.visualElement, axis)

return axisValue.start(
animateMotionValue(
axis,
axisValue,
0,
transition,
this.visualElement,
false,
addValueToWillChange(this.visualElement, axis)
false
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,7 @@ describe("animate prop as variant", () => {
})

return expect(promise).resolves.toEqual({
willChange: "auto",
willChange: "transform",
x: 100,
y: 100,
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,21 @@
import { MotionValue } from ".."
import { WillChange } from "./types"
import { getWillChangeName } from "./get-will-change-name"
import { removeItem } from "../../utils/array"
import { addUniqueItem } from "../../utils/array"

export class WillChangeMotionValue extends MotionValue implements WillChange {
private output: string[] = []
private counts = new Map<string, number>()
private values: string[] = []

add(name: string) {
const styleName = getWillChangeName(name)

if (!styleName) return

/**
* Update counter. Each value has an indepdent counter
* as multiple sources could be requesting the same value
* gets added to will-change.
*/
const prevCount = this.counts.get(styleName) || 0
this.counts.set(styleName, prevCount + 1)

if (prevCount === 0) {
this.output.push(styleName)
if (styleName) {
addUniqueItem(this.values, styleName)
this.update()
}

/**
* Prevents the remove function from being called multiple times.
*/
let hasRemoved = false

return () => {
if (hasRemoved) return

hasRemoved = true

const newCount = this.counts.get(styleName)! - 1
this.counts.set(styleName, newCount)

if (newCount === 0) {
removeItem(this.output, styleName)
this.update()
}
}
}

private update() {
this.set(this.output.length ? this.output.join(", ") : "auto")
this.set(this.values.length ? this.values.join(", ") : "auto")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@ import { WillChangeMotionValue } from "../WillChangeMotionValue"
describe("WillChangeMotionValue", () => {
test("Can manage transform alongside independent transforms", async () => {
const willChange = new WillChangeMotionValue("auto")
const removeTransform = willChange.add("transform")
willChange.add("transform")
expect(willChange.get()).toBe("transform")
removeTransform!()
expect(willChange.get()).toBe("auto")
const removeX = willChange.add("x")
const removeY = willChange.add("y")
expect(willChange.get()).toBe("transform")
removeX!()
expect(willChange.get()).toBe("transform")
removeY!()
expect(willChange.get()).toBe("auto")

const willChange2 = new WillChangeMotionValue("auto")
willChange2.add("x")
willChange2.add("y")
expect(willChange2.get()).toBe("transform")
})
})

Expand Down Expand Up @@ -139,7 +135,7 @@ describe("willChange", () => {
expect(container.firstChild).not.toHaveStyle("will-change: opacity;")
})

test("Removes values when they finish animating", async () => {
test("Don't remove values when they finish animating", async () => {
return new Promise<void>((resolve) => {
const Component = () => {
return (
Expand All @@ -149,7 +145,7 @@ describe("willChange", () => {
onAnimationComplete={() => {
frame.postRender(() => {
expect(container.firstChild).toHaveStyle(
"will-change: auto;"
"will-change: transform;"
)
resolve()
})
Expand Down
2 changes: 1 addition & 1 deletion packages/framer-motion/src/value/use-will-change/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { MotionValue } from ".."

export interface WillChange extends MotionValue {
add(name: string): undefined | VoidFunction
add(name: string): void
}

0 comments on commit c186b5a

Please sign in to comment.