Skip to content

Commit 957aee8

Browse files
authoredMay 21, 2023
fix: don't block events with similar timestamps (#600)
1 parent 0dce221 commit 957aee8

File tree

11 files changed

+222
-6
lines changed

11 files changed

+222
-6
lines changed
 

‎.changeset/rare-avocados-taste.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@use-gesture/core': patch
3+
---
4+
5+
fix: don't block events with similar timestamps #581

‎demo/src/App.jsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import CardZoom from './sandboxes/card-zoom/src/App'
2626
import InfiniteSlideshow from './sandboxes/infinite-slideshow/src/App'
2727
import ActionSheet from './sandboxes/action-sheet/src/App'
2828
import DotsConnect from './sandboxes/dots-connect/src/App'
29+
import NativeVsLib from './sandboxes/native-vs-lib/src/App'
2930

3031
const links = {
3132
'gesture-simplest': Simplest,
@@ -50,7 +51,8 @@ const links = {
5051
'card-zoom': CardZoom,
5152
'infinite-slideshow': InfiniteSlideshow,
5253
'action-sheet': ActionSheet,
53-
'dots-connect': DotsConnect
54+
'dots-connect': DotsConnect,
55+
'native-vs-lib': NativeVsLib
5456
}
5557

5658
const Example = ({ link }) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"name": "native-vs-lib",
3+
"version": "1.0.0",
4+
"main": "src/index.jsx",
5+
"dependencies": {
6+
"@use-gesture/react": "latest",
7+
"react": "^18.1.0",
8+
"react-dom": "^18.1.0",
9+
"react-scripts": "^5.0.1"
10+
},
11+
"scripts": {
12+
"start": "react-scripts start",
13+
"build": "react-scripts build",
14+
"test": "react-scripts test --env=jsdom",
15+
"eject": "react-scripts eject"
16+
},
17+
"browserslist": [
18+
">0.2%",
19+
"not dead",
20+
"not ie <= 11",
21+
"not op_mini all"
22+
],
23+
"devDependencies": {
24+
"@types/lodash-es": "^4.17.4",
25+
"@types/react": "^18.0.3",
26+
"@types/react-dom": "^18.0.0",
27+
"typescript": "^4.9.4",
28+
"typescript-plugin-css-modules": "^4.1.1"
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
6+
<meta name="theme-color" content="#000000" />
7+
<!--
8+
manifest.json provides metadata used when your web app is added to the
9+
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
10+
-->
11+
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
12+
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
13+
<!--
14+
Notice the use of %PUBLIC_URL% in the tags above.
15+
It will be replaced with the URL of the `public` folder during the build.
16+
Only files inside the `public` folder can be referenced from the HTML.
17+
18+
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
19+
work correctly both with client-side routing and a non-root public URL.
20+
Learn how to configure a non-root public URL by running `npm run build`.
21+
-->
22+
<title>Use-Gesture Sandbox</title>
23+
</head>
24+
25+
<body>
26+
<noscript> You need to enable JavaScript to run this app. </noscript>
27+
<div id="root"></div>
28+
<!--
29+
This HTML file is a template.
30+
If you open it directly in the browser, you will see an empty page.
31+
32+
You can add webfonts, meta tags, or analytics to this file.
33+
The build step will place the bundled scripts into the <body> tag.
34+
35+
To begin the development, run `npm start` or `yarn start`.
36+
To create a production bundle, use `npm run build` or `yarn build`.
37+
-->
38+
</body>
39+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { useRef } from 'react'
2+
import { useDrag } from '@use-gesture/react'
3+
4+
function TestDrag() {
5+
const bind = useDrag(({ offset: [x, y], event }) => {
6+
event.currentTarget.style.transform = `translate(${x}px,${y}px)`
7+
})
8+
9+
return (
10+
<div
11+
{...bind()}
12+
style={{
13+
width: '100px',
14+
height: '100px',
15+
backgroundColor: '#ff0000',
16+
touchAction: 'none'
17+
}}
18+
/>
19+
)
20+
}
21+
function TestNaiveDrag() {
22+
const dragging = useRef(false)
23+
const initial = useRef([0, 0])
24+
const px = useRef(0)
25+
const py = useRef(0)
26+
const x = useRef(0)
27+
const y = useRef(0)
28+
29+
const onPointerDown = (e) => {
30+
e.currentTarget.setPointerCapture(e.pointerId)
31+
initial.current = [e.clientX, e.clientY]
32+
px.current = x.current
33+
py.current = y.current
34+
dragging.current = true
35+
}
36+
37+
const onPointerMove = (e) => {
38+
if (!dragging.current) return
39+
x.current = px.current + e.clientX - initial.current[0]
40+
y.current = py.current + e.clientY - initial.current[1]
41+
e.currentTarget.style.transform = `translate(${x.current}px,${y.current}px)`
42+
}
43+
44+
const onPointerUp = (e) => {
45+
dragging.current = false
46+
}
47+
48+
return (
49+
<div
50+
onPointerDown={onPointerDown}
51+
onPointerMove={onPointerMove}
52+
onPointerUp={onPointerUp}
53+
style={{
54+
width: '100px',
55+
height: '100px',
56+
backgroundColor: '#00ff00',
57+
touchAction: 'none'
58+
}}
59+
/>
60+
)
61+
}
62+
63+
export default function App() {
64+
return (
65+
<div>
66+
<TestDrag />
67+
<TestNaiveDrag />
68+
</div>
69+
)
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
html,
2+
body,
3+
#root {
4+
height: 100%;
5+
width: 100%;
6+
}
7+
8+
body {
9+
font-family: system-ui, sans-serif;
10+
min-height: 100vh;
11+
margin: 0;
12+
background-color: #ecede7;
13+
}
14+
15+
*,
16+
*:after,
17+
*:before {
18+
box-sizing: border-box;
19+
}
20+
21+
.flex {
22+
display: flex;
23+
align-items: center;
24+
}
25+
26+
.flex.fill {
27+
height: 100%;
28+
}
29+
30+
.flex.center {
31+
justify-content: center;
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React from 'react'
2+
import { createRoot } from 'react-dom/client'
3+
import App from './App'
4+
5+
import './index.css'
6+
7+
const rootElement = document.getElementById('root')
8+
const root = createRoot(rootElement!)
9+
root.render(
10+
<React.StrictMode>
11+
<App />
12+
</React.StrictMode>
13+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
.drag {
2+
position: absolute;
3+
height: 80px;
4+
width: 80px;
5+
border-radius: 8px;
6+
background-color: hotpink;
7+
cursor: grab;
8+
touch-action: none;
9+
-webkit-user-select: none;
10+
user-select: none;
11+
font-size: 10px;
12+
}
13+
14+
.drag:focus {
15+
border: 2px solid red;
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"include": ["./src/**/*"],
3+
"compilerOptions": {
4+
"strict": true,
5+
"esModuleInterop": true,
6+
"lib": ["dom", "es2015"],
7+
"plugins": [{ "name": "typescript-plugin-css-modules" }],
8+
"jsx": "react-jsx"
9+
}
10+
}

‎packages/core/src/engines/DragEngine.ts

-4
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,6 @@ export class DragEngine extends CoordinatesEngine<'drag'> {
150150

151151
if (!state._pointerActive) return
152152

153-
// if the event has the same timestamp as the previous event
154-
// note that checking type equality is ONLY for tests ¯\_(ツ)_/¯
155-
if (state.type === event.type && event.timeStamp === state.timeStamp) return
156-
157153
const id = pointerId(event)
158154
if (state._pointerId !== undefined && id !== state._pointerId) return
159155
const _values = pointerValues(event)

‎packages/core/src/engines/Engine.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -346,8 +346,11 @@ export abstract class Engine<Key extends GestureKey> {
346346
state.direction = state.delta.map(Math.sign) as Vector2
347347
state._direction = state._delta.map(Math.sign) as Vector2
348348

349+
// calculates kinematics unless the gesture starts or ends or if the
350+
// dt === 0 (which can happen on high frame rate monitors, see issue #581)
351+
// because of privacy protection:
352+
// https://developer.mozilla.org/en-US/docs/Web/API/Event/timeStamp#reduced_time_precision
349353
if (!state.first && dt > 0) {
350-
// calculates kinematics unless the gesture starts or ends
351354
state.velocity = [absoluteDelta[0] / dt, absoluteDelta[1] / dt]
352355
state.timeDelta = dt
353356
}

0 commit comments

Comments
 (0)
Please sign in to comment.