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)