From 872358d69842ad6a929d66bf4a708587d8f645cf Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Mon, 1 Nov 2021 15:12:57 +0100 Subject: [PATCH 1/3] Converting translation from percentage to pixels before drag --- cypress/integration/drag.ts | 23 +++++++++++++++++++ dev/tests/drag-ref-constraints.tsx | 5 ++-- dev/tests/drag.tsx | 13 +++++++++-- package.json | 2 +- .../drag/VisualElementDragControls.ts | 19 ++++++++++++++- 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/cypress/integration/drag.ts b/cypress/integration/drag.ts index 4f03aaa38a..11ea5af826 100644 --- a/cypress/integration/drag.ts +++ b/cypress/integration/drag.ts @@ -50,6 +50,29 @@ describe("Drag", () => { }) }) + it("Drags the element by the defined distance with percentage initial offset", () => { + cy.visit("?test=drag&x=200%&y=200%") + .get("[data-testid='draggable']") + .wait(200) + .trigger("pointerdown", 5, 5) + .trigger("pointermove", 10, 10) // Gesture will start from first move past threshold + .wait(50) + .trigger("pointermove", 200, 300, { force: true }) + .wait(50) + .trigger("pointerup", { force: true }) + .should(($draggable: any) => { + const draggable = $draggable[0] as HTMLDivElement + const { left, top } = draggable.getBoundingClientRect() + + expect(left).to.equal(300) + + // TODO: This should actually be 400, but for some reason the test scroll + // scrolls an additional 100px when dragging starts. But this has been manually verified + // as working + expect(top).to.equal(300) + }) + }) + it("Locks drag to x", () => { cy.visit("?test=drag&axis=x") .get("[data-testid='draggable']") diff --git a/dev/tests/drag-ref-constraints.tsx b/dev/tests/drag-ref-constraints.tsx index 4821bba787..05a2d28fc2 100644 --- a/dev/tests/drag-ref-constraints.tsx +++ b/dev/tests/drag-ref-constraints.tsx @@ -1,4 +1,4 @@ -import { motion } from "@framer" +import { motion, useMotionValue } from "@framer" import * as React from "react" // It's important for this test to only trigger a single rerender while dragging (in response to onDragStart) of draggable component. @@ -13,7 +13,7 @@ export const App = () => { React.useLayoutEffect(() => { window.scrollTo(0, 100) }, []) - + const x = useMotionValue("100%") return (
{ width: 50, height: 50, background: dragging ? "yellow" : "red", + x, }} dragConstraints={containerRef} layout={layout} diff --git a/dev/tests/drag.tsx b/dev/tests/drag.tsx index 5633c7d771..3b4137ebbe 100644 --- a/dev/tests/drag.tsx +++ b/dev/tests/drag.tsx @@ -2,6 +2,15 @@ import { motion } from "@framer" import * as React from "react" // It's important for this test to only trigger a single rerender while dragging (in response to onDragStart) of draggable component. +function getValueParam(params: any, name: string) { + const param = params.get(name) as string | undefined + if (!param) return 0 + if (param.endsWith("%")) { + return param + } else { + return parseFloat(param) + } +} export const App = () => { const params = new URLSearchParams(window.location.search) @@ -12,8 +21,8 @@ export const App = () => { const right = parseFloat(params.get("right")) || undefined const bottom = parseFloat(params.get("bottom")) || undefined const snapToOrigin = Boolean(params.get("return")) - const x = parseFloat(params.get("x")) || 0 - const y = parseFloat(params.get("y")) || 0 + const x = getValueParam(params, "x") + const y = getValueParam(params, "y") const layout = params.get("layout") || undefined // We do this to test when scroll position isn't 0/0 diff --git a/package.json b/package.json index 0b6c494976..74cee88c70 100644 --- a/package.json +++ b/package.json @@ -159,7 +159,7 @@ }, { "path": "./dist/size-webpack-dom-max.js", - "maxSize": "30.2 kB" + "maxSize": "30.3 kB" } ] } diff --git a/src/gestures/drag/VisualElementDragControls.ts b/src/gestures/drag/VisualElementDragControls.ts index 17aaa1d4f5..949f66d24b 100644 --- a/src/gestures/drag/VisualElementDragControls.ts +++ b/src/gestures/drag/VisualElementDragControls.ts @@ -30,6 +30,8 @@ import { import { LayoutUpdateData } from "../../projection/node/types" import { addDomEvent } from "../../events/use-dom-event" import { mix } from "popmotion" +import { percent } from "style-value-types" +import { calcLength } from "../../projection/geometry/delta-calc" export const elementDragControls = new WeakMap< VisualElement, @@ -125,7 +127,22 @@ export class VisualElementDragControls { * Record gesture origin */ eachAxis((axis) => { - this.originPoint[axis] = this.getAxisMotionValue(axis).get() + let current = this.getAxisMotionValue(axis).get() + + /** + * If the MotionValue is a percentage value convert to px + */ + if (percent.test(current)) { + const measuredAxis = + this.visualElement.projection?.layout?.actual[axis] + + if (measuredAxis) { + const length = calcLength(measuredAxis) + current = length * (parseFloat(current) / 100) + } + } + + this.originPoint[axis] = current as number }) // Fire onDragStart event From aea1b393dfd9f41a415c532db0a07e8506de1e02 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Mon, 1 Nov 2021 15:14:02 +0100 Subject: [PATCH 2/3] Updating changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 684f3b77e2..fba6d4a41b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Framer Motion adheres to [Semantic Versioning](http://semver.org/). +## [5.0.2] 2021-11-02 + +- Convert x/y from percent to pixels before drag. [Issue](https://github.com/framer/motion/issues/424) + ## [5.0.1] 2021-11-01 ### Added From f3d04eb80e15126be7675b45706d0d29efbb92e5 Mon Sep 17 00:00:00 2001 From: Matt Perry Date: Mon, 1 Nov 2021 16:30:00 +0100 Subject: [PATCH 3/3] Removing cast and adding default --- dev/tests/drag.tsx | 2 +- src/gestures/drag/VisualElementDragControls.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dev/tests/drag.tsx b/dev/tests/drag.tsx index 3b4137ebbe..fb99eb0fca 100644 --- a/dev/tests/drag.tsx +++ b/dev/tests/drag.tsx @@ -1,7 +1,6 @@ import { motion } from "@framer" import * as React from "react" -// It's important for this test to only trigger a single rerender while dragging (in response to onDragStart) of draggable component. function getValueParam(params: any, name: string) { const param = params.get(name) as string | undefined if (!param) return 0 @@ -12,6 +11,7 @@ function getValueParam(params: any, name: string) { } } +// It's important for this test to only trigger a single rerender while dragging (in response to onDragStart) of draggable component. export const App = () => { const params = new URLSearchParams(window.location.search) const axis = params.get("axis") diff --git a/src/gestures/drag/VisualElementDragControls.ts b/src/gestures/drag/VisualElementDragControls.ts index 949f66d24b..429c80029f 100644 --- a/src/gestures/drag/VisualElementDragControls.ts +++ b/src/gestures/drag/VisualElementDragControls.ts @@ -127,7 +127,7 @@ export class VisualElementDragControls { * Record gesture origin */ eachAxis((axis) => { - let current = this.getAxisMotionValue(axis).get() + let current = this.getAxisMotionValue(axis).get() || 0 /** * If the MotionValue is a percentage value convert to px @@ -142,7 +142,7 @@ export class VisualElementDragControls { } } - this.originPoint[axis] = current as number + this.originPoint[axis] = current }) // Fire onDragStart event