From 91aff08f02135beba04ed5948f68e7beede49d3f Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Thu, 16 May 2024 14:39:38 +0200 Subject: [PATCH 1/6] Improving --- dev/examples/useSpring.tsx | 74 ++++++++++--------- .../framer-motion/src/frameloop/batcher.ts | 3 + packages/framer-motion/src/frameloop/types.ts | 1 + .../framer-motion/src/value/use-spring.ts | 28 +++++-- 4 files changed, 65 insertions(+), 41 deletions(-) diff --git a/dev/examples/useSpring.tsx b/dev/examples/useSpring.tsx index 79e0fe76f9..5451f969ef 100644 --- a/dev/examples/useSpring.tsx +++ b/dev/examples/useSpring.tsx @@ -4,39 +4,22 @@ import { useEffect, useRef, useState } from "react" const spring = { stiffness: 300, damping: 28, - restDelta: 0.001, - restSpeed: 0.001, -} - -const useMousePosition = () => { - const [mousePosition, setMousePosition] = useState({ x: null, y: null }) - - const updateMousePosition = (e) => { - setMousePosition({ x: e.clientX, y: e.clientY }) - } - - useEffect(() => { - window.addEventListener("mousemove", updateMousePosition) - - return () => - window.removeEventListener("mousemove", updateMousePosition) - }, []) - - return mousePosition + restDelta: 0.00001, + restSpeed: 0.00001, } function DragExample() { const dragX = useMotionValue(0) - const dragY = useMotionValue(0) - const x = useSpring(dragX) - const y = useSpring(dragY, spring) + // const dragY = useMotionValue(0) + const x = useSpring(dragX, spring) + // const y = useSpring(dragY, spring) return ( Drag @@ -44,14 +27,19 @@ function DragExample() { } function RerenderExample() { - const { x, y } = useMousePosition() + const [mousePosition, setMousePosition] = useState({ x: null, y: null }) + + const updateMousePosition = useRef((e) => { + setMousePosition({ x: e.clientX, y: e.clientY }) + }) + const size = 40 const ref = useRef(null) - console.log(x) + // console.log(x) return ( { + window.addEventListener( + "mousemove", + updateMousePosition.current + ) + }} + onTap={() => { + window.removeEventListener( + "mousemove", + updateMousePosition.current + ) + }} + onTapCancel={() => { + window.removeEventListener( + "mousemove", + updateMousePosition.current + ) + }} > Rerender @@ -69,15 +75,17 @@ function RerenderExample() { function MouseEventExample() { const xPoint = useMotionValue(0) const yPoint = useMotionValue(0) - const x = useSpring(xPoint, spring) - const y = useSpring(yPoint, spring) + const x = useSpring(0, spring) + const y = useSpring(0, spring) const ref = useRef(null) const onMove = useRef<(event: MouseEvent) => void>( ({ clientX, clientY }: MouseEvent) => { const element = ref.current! - xPoint.set(clientX - element.offsetLeft - element.offsetWidth / 2) - yPoint.set(clientY - element.offsetTop - element.offsetHeight / 2) + // frame.update(() => { + x.set(clientX - element.offsetLeft - element.offsetWidth / 2) + // y.set(clientY - element.offsetTop - element.offsetHeight / 2) + // }) } ) @@ -92,7 +100,7 @@ function MouseEventExample() { return ( { @@ -32,7 +33,9 @@ export function createRenderBatcher( }, {} as Steps) const processStep = (stepId: StepId) => { + state.currentStep = stepId steps[stepId].process(state) + state.currentStep = "" } const processBatch = () => { diff --git a/packages/framer-motion/src/frameloop/types.ts b/packages/framer-motion/src/frameloop/types.ts index cb3b075b14..6b55e951b3 100644 --- a/packages/framer-motion/src/frameloop/types.ts +++ b/packages/framer-motion/src/frameloop/types.ts @@ -34,4 +34,5 @@ export interface FrameData { delta: number timestamp: number isProcessing: boolean + currentStep: StepId | "" } diff --git a/packages/framer-motion/src/value/use-spring.ts b/packages/framer-motion/src/value/use-spring.ts index b967bd8b90..12ab9e5499 100644 --- a/packages/framer-motion/src/value/use-spring.ts +++ b/packages/framer-motion/src/value/use-spring.ts @@ -5,7 +5,7 @@ import { useMotionValue } from "./use-motion-value" import { MotionConfigContext } from "../context/MotionConfigContext" import { SpringOptions } from "../animation/types" import { useIsomorphicLayoutEffect } from "../utils/use-isomorphic-effect" -import { frameData } from "../frameloop" +import { frame, frameData } from "../frameloop" import { MainThreadAnimation, animateValue, @@ -47,13 +47,10 @@ export function useSpring( } useInsertionEffect(() => { - return value.attach((v, set) => { - /** - * A more hollistic approach to this might be to use isStatic to fix VisualElement animations - * at that level, but this will work for now - */ - if (isStatic) return set(v) + let latestValue: number + let set: (v: number) => void + const startAnimation = () => { /** * If the previous animation hasn't had the chance to even render a frame, render it now. */ @@ -64,8 +61,10 @@ export function useSpring( stopAnimation() + console.log("useSpring", value.getVelocity(), frameData.currentStep) + activeSpringAnimation.current = animateValue({ - keyframes: [value.get(), v], + keyframes: [value.get(), latestValue], velocity: value.getVelocity(), type: "spring", restDelta: 0.001, @@ -73,6 +72,19 @@ export function useSpring( ...config, onUpdate: set, }) + } + + return value.attach((v, frameSet) => { + /** + * A more hollistic approach to this might be to use isStatic to fix VisualElement animations + * at that level, but this will work for now + */ + if (isStatic) return set(v) + + latestValue = v + set = frameSet + + frame.update(startAnimation, true) return value.get() }, stopAnimation) From 748187709d004644d4f3cbb34eefe59874887123 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Thu, 16 May 2024 14:40:33 +0200 Subject: [PATCH 2/6] Updating --- packages/framer-motion/src/frameloop/batcher.ts | 3 --- packages/framer-motion/src/frameloop/types.ts | 1 - 2 files changed, 4 deletions(-) diff --git a/packages/framer-motion/src/frameloop/batcher.ts b/packages/framer-motion/src/frameloop/batcher.ts index 38e3639f1e..07bdcc347f 100644 --- a/packages/framer-motion/src/frameloop/batcher.ts +++ b/packages/framer-motion/src/frameloop/batcher.ts @@ -24,7 +24,6 @@ export function createRenderBatcher( delta: 0, timestamp: 0, isProcessing: false, - currentStep: "", } const steps = stepsOrder.reduce((acc, key) => { @@ -33,9 +32,7 @@ export function createRenderBatcher( }, {} as Steps) const processStep = (stepId: StepId) => { - state.currentStep = stepId steps[stepId].process(state) - state.currentStep = "" } const processBatch = () => { diff --git a/packages/framer-motion/src/frameloop/types.ts b/packages/framer-motion/src/frameloop/types.ts index 6b55e951b3..cb3b075b14 100644 --- a/packages/framer-motion/src/frameloop/types.ts +++ b/packages/framer-motion/src/frameloop/types.ts @@ -34,5 +34,4 @@ export interface FrameData { delta: number timestamp: number isProcessing: boolean - currentStep: StepId | "" } From 736a47d2ea628667289360dc2cf56fa2e5d420c2 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Thu, 16 May 2024 14:49:16 +0200 Subject: [PATCH 3/6] Latest --- packages/framer-motion/src/value/use-spring.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/framer-motion/src/value/use-spring.ts b/packages/framer-motion/src/value/use-spring.ts index 12ab9e5499..c34c9ad401 100644 --- a/packages/framer-motion/src/value/use-spring.ts +++ b/packages/framer-motion/src/value/use-spring.ts @@ -48,21 +48,20 @@ export function useSpring( useInsertionEffect(() => { let latestValue: number - let set: (v: number) => void + let latestSet: (v: number) => void const startAnimation = () => { /** * If the previous animation hasn't had the chance to even render a frame, render it now. */ const animation = activeSpringAnimation.current + if (animation && animation.time === 0) { animation.sample(frameData.delta) } stopAnimation() - console.log("useSpring", value.getVelocity(), frameData.currentStep) - activeSpringAnimation.current = animateValue({ keyframes: [value.get(), latestValue], velocity: value.getVelocity(), @@ -70,11 +69,11 @@ export function useSpring( restDelta: 0.001, restSpeed: 0.01, ...config, - onUpdate: set, + onUpdate: latestSet, }) } - return value.attach((v, frameSet) => { + return value.attach((v, set) => { /** * A more hollistic approach to this might be to use isStatic to fix VisualElement animations * at that level, but this will work for now @@ -82,9 +81,9 @@ export function useSpring( if (isStatic) return set(v) latestValue = v - set = frameSet + latestSet = set - frame.update(startAnimation, true) + frame.update(startAnimation) return value.get() }, stopAnimation) From 767834b94eddb397ac75f1fb3d8fcab6908b0119 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Thu, 16 May 2024 14:52:43 +0200 Subject: [PATCH 4/6] Cleanup --- .../framer-motion/src/value/use-spring.ts | 55 +++++++++---------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/packages/framer-motion/src/value/use-spring.ts b/packages/framer-motion/src/value/use-spring.ts index c34c9ad401..395b35d6b3 100644 --- a/packages/framer-motion/src/value/use-spring.ts +++ b/packages/framer-motion/src/value/use-spring.ts @@ -39,6 +39,31 @@ export function useSpring( null ) const value = useMotionValue(isMotionValue(source) ? source.get() : source) + const latestValue = useRef(value.get()) + const latestSetter = useRef<(v: number) => void>(() => {}) + + const startAnimation = () => { + /** + * If the previous animation hasn't had the chance to even render a frame, render it now. + */ + const animation = activeSpringAnimation.current + + if (animation && animation.time === 0) { + animation.sample(frameData.delta) + } + + stopAnimation() + + activeSpringAnimation.current = animateValue({ + keyframes: [value.get(), latestValue.current], + velocity: value.getVelocity(), + type: "spring", + restDelta: 0.001, + restSpeed: 0.01, + ...config, + onUpdate: latestSetter.current, + }) + } const stopAnimation = () => { if (activeSpringAnimation.current) { @@ -47,32 +72,6 @@ export function useSpring( } useInsertionEffect(() => { - let latestValue: number - let latestSet: (v: number) => void - - const startAnimation = () => { - /** - * If the previous animation hasn't had the chance to even render a frame, render it now. - */ - const animation = activeSpringAnimation.current - - if (animation && animation.time === 0) { - animation.sample(frameData.delta) - } - - stopAnimation() - - activeSpringAnimation.current = animateValue({ - keyframes: [value.get(), latestValue], - velocity: value.getVelocity(), - type: "spring", - restDelta: 0.001, - restSpeed: 0.01, - ...config, - onUpdate: latestSet, - }) - } - return value.attach((v, set) => { /** * A more hollistic approach to this might be to use isStatic to fix VisualElement animations @@ -80,8 +79,8 @@ export function useSpring( */ if (isStatic) return set(v) - latestValue = v - latestSet = set + latestValue.current = v + latestSetter.current = set frame.update(startAnimation) From 25c59ed0537359ad8594d54613cad34d997c4d86 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Thu, 16 May 2024 15:20:05 +0200 Subject: [PATCH 5/6] Fixing example --- dev/examples/useSpring.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dev/examples/useSpring.tsx b/dev/examples/useSpring.tsx index 5451f969ef..f28e2d9133 100644 --- a/dev/examples/useSpring.tsx +++ b/dev/examples/useSpring.tsx @@ -10,16 +10,16 @@ const spring = { function DragExample() { const dragX = useMotionValue(0) - // const dragY = useMotionValue(0) + const dragY = useMotionValue(0) const x = useSpring(dragX, spring) - // const y = useSpring(dragY, spring) + const y = useSpring(dragY, spring) return ( Drag @@ -27,7 +27,7 @@ function DragExample() { } function RerenderExample() { - const [mousePosition, setMousePosition] = useState({ x: null, y: null }) + const [{ x, y }, setMousePosition] = useState({ x: null, y: null }) const updateMousePosition = useRef((e) => { setMousePosition({ x: e.clientX, y: e.clientY }) @@ -35,11 +35,11 @@ function RerenderExample() { const size = 40 const ref = useRef(null) - // console.log(x) + return ( Date: Thu, 16 May 2024 19:22:45 +0200 Subject: [PATCH 6/6] Updating --- dev/examples/useSpring.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dev/examples/useSpring.tsx b/dev/examples/useSpring.tsx index f28e2d9133..963b67b5e0 100644 --- a/dev/examples/useSpring.tsx +++ b/dev/examples/useSpring.tsx @@ -82,10 +82,8 @@ function MouseEventExample() { ({ clientX, clientY }: MouseEvent) => { const element = ref.current! - // frame.update(() => { x.set(clientX - element.offsetLeft - element.offsetWidth / 2) - // y.set(clientY - element.offsetTop - element.offsetHeight / 2) - // }) + y.set(clientY - element.offsetTop - element.offsetHeight / 2) } )