Skip to content

Commit

Permalink
Add pointer capture to pointer events (#3066)
Browse files Browse the repository at this point in the history
* Adding pointer capture

* Latest

* Latest

* v12.4.4-alpha.0

* Fixing test

* Dry pointer capture
  • Loading branch information
mattgperry authored Feb 18, 2025
1 parent 609ddb2 commit 2ca6370
Show file tree
Hide file tree
Showing 24 changed files with 338 additions and 134 deletions.
8 changes: 4 additions & 4 deletions dev/html/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "html-env",
"private": true,
"version": "12.4.3",
"version": "12.4.4-alpha.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand All @@ -10,9 +10,9 @@
"preview": "vite preview"
},
"dependencies": {
"framer-motion": "^12.4.3",
"motion": "^12.4.3",
"motion-dom": "^12.0.0"
"framer-motion": "^12.4.4-alpha.0",
"motion": "^12.4.4-alpha.0",
"motion-dom": "^12.4.4-alpha.0"
},
"devDependencies": {
"vite": "^5.2.0"
Expand Down
25 changes: 23 additions & 2 deletions dev/html/public/playwright/gestures/press.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
background-color: #0077ff;
}

.box:focus {
.box:focus-visible {
background-color: #ff0000;
}
</style>
Expand All @@ -25,11 +25,14 @@
press
</button>
<div tabindex="-1" class="box" id="press-no-tab-index-2">press</div>
<input type="text" id="document-output" value="" />
<input type="text" id="window-output" value="" />
<script type="module" src="/src/inc.js"></script>
<script type="module">
const { press } = window.MotionDOM

press("#press-div", (target) => {
press("#press-div", (target, event) => {
event.stopPropagation()
console.log("pointer down")
target.innerHTML = "start"

Expand All @@ -52,6 +55,24 @@
// Ensure elements with tabindex explicitly set to -1 are not reset to 0
press("#press-no-tab-index-1", () => {})
press("#press-no-tab-index-2", () => {})

press(window, () => {
document.getElementById("window-output").value = "start"
return (_, { success }) => {
document.getElementById("window-output").value = success
? "end"
: "cancel"
}
})

press(document, () => {
document.getElementById("document-output").value = "start"
return (_, { success }) => {
document.getElementById("document-output").value = success
? "end"
: "cancel"
}
})
</script>
</body>
</html>
4 changes: 2 additions & 2 deletions dev/next/package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"name": "next-env",
"private": true,
"version": "12.4.3",
"version": "12.4.4-alpha.0",
"type": "module",
"scripts": {
"dev": "next dev",
"dev-server": "next dev",
"start": "next start"
},
"dependencies": {
"motion": "^12.4.3",
"motion": "^12.4.4-alpha.0",
"next": "14.x",
"react": "^18.3.1",
"react-dom": "^18.3.1"
Expand Down
4 changes: 2 additions & 2 deletions dev/react-19/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-19-env",
"private": true,
"version": "12.4.3",
"version": "12.4.4-alpha.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand All @@ -11,7 +11,7 @@
"preview": "vite preview"
},
"dependencies": {
"motion": "^12.4.3",
"motion": "^12.4.4-alpha.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
Expand Down
4 changes: 2 additions & 2 deletions dev/react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-env",
"private": true,
"version": "12.4.3",
"version": "12.4.4-alpha.0",
"type": "module",
"scripts": {
"dev": "vite",
Expand All @@ -11,7 +11,7 @@
"preview": "vite preview"
},
"dependencies": {
"framer-motion": "^12.4.3",
"framer-motion": "^12.4.4-alpha.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand Down
6 changes: 5 additions & 1 deletion dev/react/src/examples/Events-whileTap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ const style = {

export const App = () => {
return (
<motion.div whileTap="pressed">
<motion.div
whileTap="pressed"
onTap={() => console.log("tap")}
onTapCancel={() => console.log("tap cancel")}
>
<motion.div
variants={{
pressed: {
Expand Down
47 changes: 47 additions & 0 deletions dev/react/src/tests/animate-presence-layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { AnimatePresence, motion } from "framer-motion"
import { useEffect, useState } from "react"

export const App = () => {
const [width, setWidth] = useState(100)
const [presenceState, setPresenceState] = useState(true)

useEffect(() => {
if (width === 200) return
const timeout = setTimeout(() => {
setWidth(50)

setTimeout(() => {
setWidth(200)
}, 1000)
}, 1000)

return () => clearTimeout(timeout)
}, [width])

useEffect(() => {
setTimeout(() => {
setPresenceState(false)
}, 2100)
}, [presenceState])

return (
<>
<AnimatePresence initial={false}>
{presenceState && (
<motion.div exit={{ opacity: 0 }}>
<motion.div
layout
style={{
width,
height: 100,
background: "red",
}}
>
Presence
</motion.div>
</motion.div>
)}
</AnimatePresence>
</>
)
}
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "12.4.3",
"version": "12.4.4-alpha.0",
"packages": [
"packages/*",
"dev/*"
Expand Down
4 changes: 2 additions & 2 deletions packages/framer-motion-3d/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "framer-motion-3d",
"version": "12.4.3",
"version": "12.4.4-alpha.0",
"description": "A simple and powerful React animation library for @react-three/fiber",
"main": "dist/cjs/index.js",
"module": "dist/es/index.mjs",
Expand Down Expand Up @@ -45,7 +45,7 @@
"postpublish": "git push --tags"
},
"dependencies": {
"framer-motion": "^12.4.3",
"framer-motion": "^12.4.4-alpha.0",
"react-merge-refs": "^2.0.1"
},
"peerDependencies": {
Expand Down
3 changes: 2 additions & 1 deletion packages/framer-motion/jest.setup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import "@testing-library/jest-dom"
// because @testing-library/react one switches out pointerEnter and pointerLeave
import { fireEvent, getByTestId } from "@testing-library/dom"
import { render as testRender } from "@testing-library/react"
import { act, StrictMode, Fragment } from "react"
import { act, Fragment, StrictMode } from "react"

/**
* Stub PointerEvent - this is so we can pass through PointerEvent.isPrimary
*/
const pointerEventProps = ["isPrimary", "pointerType", "button"]
class PointerEventFake extends Event {
pointerId?: number = 1
constructor(type: any, props: any) {
super(type, props)
if (!props) return
Expand Down
4 changes: 2 additions & 2 deletions packages/framer-motion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "framer-motion",
"version": "12.4.3",
"version": "12.4.4-alpha.0",
"description": "A simple and powerful JavaScript animation library",
"main": "dist/cjs/index.js",
"module": "dist/es/index.mjs",
Expand Down Expand Up @@ -89,7 +89,7 @@
"measure": "rollup -c ./rollup.size.config.mjs"
},
"dependencies": {
"motion-dom": "^12.0.0",
"motion-dom": "^12.4.4-alpha.0",
"motion-utils": "^12.0.0",
"tslib": "^2.4.0"
},
Expand Down
3 changes: 2 additions & 1 deletion packages/framer-motion/src/gestures/__tests__/press.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,8 @@ describe("press", () => {
expect(press).toBeCalledTimes(1)
})

test("press cancel fires if press released outside element", async () => {
// Replaced with end to end test but ideally would also run here
test.skip("press cancel fires if press released outside element", async () => {
const pressCancel = jest.fn()
const Component = () => (
<motion.div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
import { invariant } from "motion-utils"
import { setDragLock } from "motion-dom"
import { PanSession, PanInfo } from "../pan/PanSession"
import { ResolvedConstraints } from "./types"
import { isRefObject } from "../../utils/is-ref-object"
import { invariant } from "motion-utils"
import { animateMotionValue } from "../../animation/interfaces/motion-value"
import { addDomEvent } from "../../events/add-dom-event"
import { addPointerEvent } from "../../events/add-pointer-event"
import {
calcRelativeConstraints,
calcViewportConstraints,
applyConstraints,
rebaseAxisConstraints,
resolveDragElastic,
defaultElastic,
calcOrigin,
} from "./utils/constraints"
import type { VisualElement } from "../../render/VisualElement"
import { MotionProps } from "../../motion/types"
import { Axis, Point } from "../../projection/geometry/types"
import { createBox } from "../../projection/geometry/models"
import { eachAxis } from "../../projection/utils/each-axis"
import { measurePageBox } from "../../projection/utils/measure"
import { extractEventInfo } from "../../events/event-info"
import { Transition } from "../../types"
import { frame } from "../../frameloop"
import { MotionProps } from "../../motion/types"
import {
convertBoundingBoxToBox,
convertBoxToBoundingBox,
} from "../../projection/geometry/conversion"
import { LayoutUpdateData } from "../../projection/node/types"
import { addDomEvent } from "../../events/add-dom-event"
import { calcLength } from "../../projection/geometry/delta-calc"
import { createBox } from "../../projection/geometry/models"
import { Axis, Point } from "../../projection/geometry/types"
import { LayoutUpdateData } from "../../projection/node/types"
import { eachAxis } from "../../projection/utils/each-axis"
import { measurePageBox } from "../../projection/utils/measure"
import type { VisualElement } from "../../render/VisualElement"
import { Transition } from "../../types"
import { isRefObject } from "../../utils/is-ref-object"
import { mixNumber } from "../../utils/mix/number"
import { percent } from "../../value/types/numbers/units"
import { animateMotionValue } from "../../animation/interfaces/motion-value"
import { getContextWindow } from "../../utils/get-context-window"
import { frame } from "../../frameloop"
import { addValueToWillChange } from "../../value/use-will-change/add-will-change"
import { PanInfo, PanSession } from "../pan/PanSession"
import { ResolvedConstraints } from "./types"
import {
applyConstraints,
calcOrigin,
calcRelativeConstraints,
calcViewportConstraints,
defaultElastic,
rebaseAxisConstraints,
resolveDragElastic,
} from "./utils/constraints"

export const elementDragControls = new WeakMap<
VisualElement,
Expand Down Expand Up @@ -230,7 +229,6 @@ export class VisualElementDragControls {
{
transformPagePoint: this.visualElement.getTransformPagePoint(),
dragSnapToOrigin,
contextWindow: getContextWindow(this.visualElement),
}
)
}
Expand Down
6 changes: 3 additions & 3 deletions packages/framer-motion/src/gestures/drag/__tests__/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as React from "react"
import { frame } from "../../../frameloop"
import { MotionConfig } from "../../../components/MotionConfig"
import { pointerDown, pointerMove, pointerUp } from "../../../../jest.setup"
import { MotionConfig } from "../../../components/MotionConfig"
import { frame } from "../../../frameloop"

export type Point = {
x: number
Expand Down Expand Up @@ -41,7 +41,7 @@ export const drag = (element: any, triggerElement?: any) => {
pos.y = y

await React.act(async () => {
pointerMove(document.body)
pointerMove(element)
await dragFrame.postRender()
})

Expand Down
Loading

0 comments on commit 2ca6370

Please sign in to comment.