diff --git a/dev/react/src/examples/Drag-constraints-ref.tsx b/dev/react/src/examples/Drag-constraints-ref.tsx index 28e5454680..5577f9184b 100644 --- a/dev/react/src/examples/Drag-constraints-ref.tsx +++ b/dev/react/src/examples/Drag-constraints-ref.tsx @@ -1,4 +1,4 @@ -import { useRef, useState } from "react" +import { useEffect, useRef, useState } from "react" import { motion } from "framer-motion" const container = { @@ -18,20 +18,50 @@ const child = { borderRadius: 20, } +/** + * This sibling layout animation is designed to fuzz/stress the drag constraints + * measurements. Remeasuring the constraints during drag would previously mess + * up the position of the draggable element. + */ +const SiblingLayoutAnimation = () => { + const [state, setState] = useState(false) + + useEffect(() => { + const timer = setTimeout(() => setState(!state), 500) + + return () => clearTimeout(timer) + }, [state]) + + return ( + + ) +} + export const App = () => { const ref = useRef() const [count, setCount] = useState(0) return ( -
- setCount(count + 1)} - /> -
+ <> +
+ setCount(count + 1)} + id="draggable" + /> +
+ + ) } diff --git a/dev/react/src/tests/drag-ref-constraints.tsx b/dev/react/src/tests/drag-ref-constraints.tsx index 97f02f0c19..4953a9c5c3 100644 --- a/dev/react/src/tests/drag-ref-constraints.tsx +++ b/dev/react/src/tests/drag-ref-constraints.tsx @@ -1,5 +1,5 @@ import { motion, useMotionValue } from "framer-motion" -import { useRef, useState, useLayoutEffect } from "react"; +import { useRef, useState, useLayoutEffect, useEffect } from "react" // It's important for this test to only trigger a single rerender while dragging (in response to onDragStart) of draggable component. @@ -16,30 +16,62 @@ export const App = () => { const x = useMotionValue("100%") return ( -
- + <> +
setDragging(true)} - onDragEnd={() => setDragging(false)} - /> - -
+ data-testid="constraint" + style={{ width: 200, height: 200, background: "blue" }} + ref={containerRef} + > + setDragging(true)} + onDragEnd={() => setDragging(false)} + /> + +
+ + + ) +} + +/** + * This sibling layout animation is designed to fuzz/stress the drag constraints + * measurements. Remeasuring the constraints during drag would previously mess + * up the position of the draggable element. + */ +const SiblingLayoutAnimation = () => { + const [state, setState] = useState(false) + + useEffect(() => { + const timer = setTimeout(() => setState(!state), 200) + + return () => clearTimeout(timer) + }, [state]) + + return ( + ) } diff --git a/packages/framer-motion/cypress/integration/drag.ts b/packages/framer-motion/cypress/integration/drag.ts index af7d7e563b..125740e80f 100644 --- a/packages/framer-motion/cypress/integration/drag.ts +++ b/packages/framer-motion/cypress/integration/drag.ts @@ -234,9 +234,9 @@ describe("Drag", () => { .get("[data-testid='draggable']") .trigger("pointerdown", 10, 10) .trigger("pointermove", 15, 15) - .wait(50) + .wait(200) .trigger("pointermove", 300, 300, { force: true }) - .wait(50) + .wait(200) .trigger("pointerup", { force: true }) .should(($draggable: any) => { const draggable = $draggable[0] as HTMLDivElement diff --git a/packages/framer-motion/src/projection/node/create-projection-node.ts b/packages/framer-motion/src/projection/node/create-projection-node.ts index c750abe2b0..ae06f41c6e 100644 --- a/packages/framer-motion/src/projection/node/create-projection-node.ts +++ b/packages/framer-motion/src/projection/node/create-projection-node.ts @@ -872,8 +872,11 @@ export function createProjectionNode({ resetTransform() { if (!resetTransform) return + const isResetRequested = - this.isLayoutDirty || this.shouldResetTransform + this.isLayoutDirty || + this.shouldResetTransform || + this.options.alwaysMeasureLayout const hasProjection = this.projectionDelta && !isDeltaZero(this.projectionDelta)