From c8da9c25894f0d5acd1ac4465ce1d8a297b8c99f Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 21 May 2024 14:08:16 +0200 Subject: [PATCH 1/3] Adding failing test --- dev/tests/layout-portal.tsx | 33 ++++++++++++++ .../cypress/integration/layout.ts | 44 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 dev/tests/layout-portal.tsx diff --git a/dev/tests/layout-portal.tsx b/dev/tests/layout-portal.tsx new file mode 100644 index 0000000000..2145d44dda --- /dev/null +++ b/dev/tests/layout-portal.tsx @@ -0,0 +1,33 @@ +import { motion } from "framer-motion" +import { useState } from "react" +import { createPortal } from "react-dom" + +export const App = () => { + const [count, setCount] = useState(0) + + const size = count === 0 ? 100 : 300 + + return ( + setCount(count + 1)} + transition={{ duration: 10, ease: () => 0.5 }} + > + {createPortal( + 0.5 }} + />, + document.body + )} + + ) +} diff --git a/packages/framer-motion/cypress/integration/layout.ts b/packages/framer-motion/cypress/integration/layout.ts index 5777696a47..71489061fd 100644 --- a/packages/framer-motion/cypress/integration/layout.ts +++ b/packages/framer-motion/cypress/integration/layout.ts @@ -255,4 +255,48 @@ describe("Layout animation", () => { expect(bbox.left).not.to.equal(170) }) }) + + it("Elements within portal don't perform scale correction on parents", () => { + cy.visit("?test=layout-portal") + .wait(50) + .get("#parent") + .should(([$box]: any) => { + expectBbox($box, { + top: 0, + left: 0, + width: 100, + height: 100, + }) + }) + .get("#child") + .should(([$box]: any) => { + expectBbox($box, { + top: 100, + left: 100, + width: 100, + height: 100, + }) + }) + .get("#parent") + .trigger("click") + .wait(50) + .should(([$box]: any) => { + expectBbox($box, { + top: 0, + left: 0, + width: 200, + height: 200, + }) + }) + .trigger("click") + .wait(50) + .should(([$box]: any) => { + expectBbox($box, { + top: 200, + left: 200, + width: 100, + height: 100, + }) + }) + }) }) From 60ad2b6fb13b58d5601dd24e6a1eeb02d3644fdc Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 21 May 2024 14:39:51 +0200 Subject: [PATCH 2/3] Fixing layout animations within portals --- dev/tests/layout-portal.tsx | 1 + .../src/motion/features/layout/types.ts | 6 ++++++ packages/framer-motion/src/render/VisualElement.ts | 13 ++++++++----- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/dev/tests/layout-portal.tsx b/dev/tests/layout-portal.tsx index 2145d44dda..8146428ca1 100644 --- a/dev/tests/layout-portal.tsx +++ b/dev/tests/layout-portal.tsx @@ -25,6 +25,7 @@ export const App = () => { layout style={{ width: 100, height: 100, background: "blue" }} transition={{ duration: 10, ease: () => 0.5 }} + data-framer-portal-id="test" />, document.body )} diff --git a/packages/framer-motion/src/motion/features/layout/types.ts b/packages/framer-motion/src/motion/features/layout/types.ts index 9c0a4c0f18..3480aeb972 100644 --- a/packages/framer-motion/src/motion/features/layout/types.ts +++ b/packages/framer-motion/src/motion/features/layout/types.ts @@ -87,4 +87,10 @@ export interface LayoutProps { * Currently used for `position: sticky` elements in Framer. */ layoutRoot?: boolean + + /** + * Attached to a portal root to ensure we attach the child to the document root and don't + * perform scale correction on it. + */ + "data-framer-portal-id"?: string } diff --git a/packages/framer-motion/src/render/VisualElement.ts b/packages/framer-motion/src/render/VisualElement.ts index 6e32aec3f7..18d29c2f09 100644 --- a/packages/framer-motion/src/render/VisualElement.ts +++ b/packages/framer-motion/src/render/VisualElement.ts @@ -578,11 +578,6 @@ export abstract class VisualElement< !this.projection && ProjectionNodeConstructor ) { - this.projection = new ProjectionNodeConstructor( - this.latestValues, - getClosestProjectingNode(this.parent) - ) as IProjectionNode - const { layoutId, layout, @@ -591,6 +586,14 @@ export abstract class VisualElement< layoutScroll, layoutRoot, } = renderedProps + + this.projection = new ProjectionNodeConstructor( + this.latestValues, + renderedProps["data-framer-portal-id"] + ? undefined + : getClosestProjectingNode(this.parent) + ) as IProjectionNode + this.projection.setOptions({ layoutId, layout, From fe2e489ee31a4ffe81164e0ad28f45cc37a8e5c0 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Tue, 21 May 2024 14:44:52 +0200 Subject: [PATCH 3/3] Updating id --- dev/tests/layout-portal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/tests/layout-portal.tsx b/dev/tests/layout-portal.tsx index 8146428ca1..b25dd8e284 100644 --- a/dev/tests/layout-portal.tsx +++ b/dev/tests/layout-portal.tsx @@ -25,7 +25,7 @@ export const App = () => { layout style={{ width: 100, height: 100, background: "blue" }} transition={{ duration: 10, ease: () => 0.5 }} - data-framer-portal-id="test" + data-framer-portal-id="parent" />, document.body )}