pmndrs / use-gesture Goto Github PK
View Code? Open in Web Editor NEW👇Bread n butter utility for component-tied mouse/touch gestures in React and Vanilla Javascript.
Home Page: https://use-gesture.netlify.app
License: MIT License
👇Bread n butter utility for component-tied mouse/touch gestures in React and Vanilla Javascript.
Home Page: https://use-gesture.netlify.app
License: MIT License
Try to drag the square with middle or right mouse button.
Every event that is not first or last will have event.button === 0 (left button).
https://codesandbox.io/s/strange-surf-g4xsy
Following the topic started in the #45.
@dbismut please, see this example: https://codesandbox.io/s/eloquent-yalow-0kv87
I am using draggable
attribute together with onDragOver
to capture the index of the element over which I'm dragging. That breaks the spring animation. If you set draggable={false}
the springs are working correctly, but then onDragOver
doesn't.
Repro: https://codesandbox.io/embed/react-use-gesture-template-zk203
(Note console.log(origin)
always outputs [0, 0]
)
Not sure if I understand this property, but I'd expect origin to be the coordinates where the pinch started, and possibly see it drift if the pinch gesture is not perfectly symmetrical. Is that the right assumption?
Edit: I'm realizing now that it's because I'm using a trackpad, not touching the actual screen.
Is there a way to get the cursor position during pinch? This would be useful in order to zoom in on the right spot.
Read #109 for more
useDrag
, usePinch
, etc.Hi!
I've made an exemple on sandbox but there is one thing I can't figure out: set boudaries with acceleration enable. I think this would be a good addition to your exemples.
https://codesandbox.io/s/blissful-lichterman-d4r2s?fontsize=14
See your codesandbox here: https://codesandbox.io/s/n9vo1my91p
If you give it a forceful drag, it will skip a slide because it appears the cancel
method is getting called twice (it happens that the slide++ is embedded in the cancel
call).
I've been digging around the issues and examples looking for solutions for discrete draggable components and drop targets, but haven't been able to find much (aside from some related conversation in #60 about the draggable
attribute — apologies if this issue is too close to duplicating that issue). There are some great examples using list reordering, but I'm curious if this library is capable of handling (or if there are plans to handle) events like onDragOver
, onDrop
— or features that could emulate this behaviour?
(One option I can think of is detecting a draggable element's final position during onDrag({{last}})
and comparing it to my drop target's position to determine a 'dropped' interaction, but this doesn't really account for events like onDragOver
for the drop target for example.)
Any insight would be much appreciated!
EDIT: A quick sandbox to better illustrate my situation, albeit without behaviour for dropping the draggable component on the drop target — https://codesandbox.io/embed/fragrant-field-0ybht
I managed to fix react-sping on IE11 by using react-spring/web.cjs
, but react-use-gesture
doesn't have .cjs
module and it's using arrow functions which are not supported in IE11.
Could you make '.cjs` bundle also, or transpile arrow functions?
In the current implementation there is a small bug regarding the mousemove
event handler - this.handleMouseMoveRaf
doesn't exist, it should be this.handleMouseMove
https://github.com/drcmda/react-with-gesture/blob/master/src/index.js#L41
<Gesture
onAction={(e) => {
console.log(e.xy); // type check failed. but value existed.
console.log(e.xDelta); // type check passed. but value is undefined.
console.log(e.yDelta); // type check passed. but value is undefined.
}>
//...
GestureState
does not describe actual value passed to event handler.
"react-use-gesture": "^5.1.1"
import { useGesture } from "react-use-gesture";
ERROR in /home/src/app/view/app.view.tsx
./src/app/view/app.view.tsx
[tsl] ERROR in /home/src/app/view/app.view.tsx(23,10)
TS2305: Module '"../../../node_modules/react-use-gesture/web.cjs"' has no exported member 'useGesture'.
ℹ 「wdm」: Failed to compile.
I'd love to know the best way to trigger an event based on a gesture.
For ex: let's say someone using the demo drags the box to the right and releases it to "accept" that item. Where and how would you actually call an acceptItem
function?
Happy to refile elsewhere if this isn't the right place. Thanks!
The event listeners for mousedown
and touchdown
are being called twice when they are not followed by a drag movement.
As consequence, problems arise with released/nullified synthetic events.
steps to reproduce it (or go to https://codesandbox.io/s/yjj0kp778x):
1- open any demo that uses useGesture
and the onAction
method.
2- add console.log(event.type)
to it
3- perform a single, fast click on the component.
bonus step: add event.persist()
to the handler in order to get rid of the warning message and be able to see that the event listener is being called twice for the same event.
Just curious, is there a reason why you assigned y
state to pageX
and not pageY
?
const newProps = { ...this.state, x: pageX, y: pageX, xDelta: 0, yDelta: 0, xInitial: pageX, yInitial: pageY, xPrev: pageX, yPrev: pageY, down: true }
This instance is in both handleMouseDown
and handleMouseMove
Is it possible to use react-use-gesture
in class components? All the examples use function components, and when I try passing a ref to useGesture
in componentdidMount
, I see errors in the console:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
Pretty sure my case is (2), breaking the Rules of Hooks, since I'm using React & react-dom 16.8.3, and I see that hooks may only be called from function components.
If there is a pattern for using react-use-gesture
is a class component, any guidance appreciated.
I'm trying to resize a flex element, based on the movement of the drag handle. But somehow, the element keeps shifting based on the click/grab location of the mouse.
In the example below, the drag handle (red) shifts when I click on the right side of that handle. If I click left, it doesn't move. This shift is also applied when I drag it. Drag by starting on the left side, and all goes fine. Drag by starting on the right side of the handle, and a shift is applied.
Any idea how this is caused, and if it can be fixed?
Any plans?
I'm using react-use-gesture and react-spring to create moveable divs like so: https://codesandbox.io/s/xmqwz
import React, { useState } from 'react'
import { render } from 'react-dom'
import { useGesture } from 'react-use-gesture'
import { useSprings, animated } from 'react-spring'
import './styles.css'
const addVectors = (v1, v2) => [v1[0] + v2[0], v1[1] + v2[1]]
const Moveables = ({ items }) => {
const [selectedItems, setSelectedItems] = useState([])
const toggleSelectedItem = item => {
const { id } = item
if (selectedItems.some(item => item.id === id)) {
setSelectedItems(selectedItems.filter(item => item.id !== id))
} else {
setSelectedItems(oldState => [...oldState, item])
}
}
const [springs, setSprings] = useSprings(items.length, index => ({
xy: items[index].position,
zIndex: 0
}))
const binds = useGesture({
onDrag: ({ args: [itemIndex], delta, temp = springs[itemIndex].xy.getValue() }) => {
const selectedItemIndexes = []
items.forEach(({ id }, index) => {
if (selectedItems.some(item => item.id === id)) {
selectedItemIndexes.push(index)
}
})
setSprings(index => {
const animationValues = selectedItemIndexes.includes(index)
? {
xy: addVectors(delta, temp),
zIndex: 1
}
: undefined
return animationValues
})
return temp
}
})
return (
<div className="content">
{springs.map(({ xy, zIndex }, index) => {
const item = items[index]
const { id } = item
return (
<animated.div
key={id}
style={{
zIndex,
transform: xy.interpolate((x, y) => `translate3d(${x}px, ${y}px, 0)`),
border: selectedItems.some(item => item.id === id) ? '4px solid lightgreen' : '0'
}}
{...binds(index)}
onDoubleClick={() => toggleSelectedItem(item)}>
{id}
</animated.div>
)
})}
</div>
)
}
render(
<Moveables
items={[
{
id: 0,
position: [0, 100]
},
{
id: 1,
position: [0, 200]
},
{
id: 2,
position: [0, 0]
}
]}
/>,
document.getElementById('root')
)
When double-clicking on a div, it will become selected. When dragging, all selected divs should move at the same time.
This already works great - except after selecting one div so that it's draggable, double-clicking on any other div in an attempt to select it will cause the first already-selected div to move.
How can I set this up so that click events don't trigger the onDrag gesture?
This is hard to reproduce, but sometimes direction
is [0, 0] although the mouse had moved. Here is a video of that: https://youtu.be/YfDXwAw9mr4
react-with-gesture
v4.0.2
<Wrapper>
{children.map((child, index) => (
<Gesture key={index}>
{(props) => (
...
)
<Gesture key={index}>
</Wrapper>
const Wrapper = styled.div`
position: relative;
display: flex;
overflow: hidden;
width: 100%;
height: 100%;
align-items: center;
justify-content: center;
`;
(if this is useful at all)
TypeError: Cannot convert undefined or null to object
import { useGesture } from 'react-use-gesture';
4 |
5 | export function Slider({ value, min, max, step, onChange }) {
> 6 | const [bind, { delta, down }] = useGesture();
7 |
8 | const { x } = useSpring({
....
is this happening because window
is undefined?
Curious, why are there are no handlers for basic swipe gestures?
I have one useGesture handler for multiple items. I use bind(itemIndex) for each item.
All handlers created by bind now seem to store the last itemIndex passed to bind, instead of unique for each one (like it was in 4.x).
Hey guys, your project rocks.
I noticed that you support a config value for { event: { passive: true } }
. However in the basic usage of useGesture
(and others), your package doesn't actually do the event binding. So the event binding ends up not being passive except for the few global bindings (for drag).
I think this is fine and once you return the bindings its outside this packages scope.
The good news is that since you already support binding to external dom/refs, this issue is already resolved! Using the domTarget
config will actually bring binding back into the hooks scope!
I fought this issue for a longer than I should have, and wish I'd have found an issue like this one while I was searching... So I'm creating it! :)
Feel free to close, but if we're looking for a resolution, I think we could add a note in the readme stating that the passive
config value will only apply to most of your bindings if you use { domTarget: myRef }
and React.useEffect(bind, [bind])
I could PR the issue to FAQ, but i'm not sure how F it is :)
Hi there,
My page is navigated using a big 'carousel' on the vertical axis, it's possible to navigate using the mousewheel and using dragging, I have recreated it in a CodeSandbox:
https://codesandbox.io/s/staging-bash-rge63.
I set the Y position based on the current delta, and cancel the gesture and navigate to the next position after a certain threshold. This works as expected for dragging, however, when I use the onWheel event, the cancel()
function doesn't seem to work. When logging canceled
, it always returns false, and the gesture will continue, thus scrolling to the end of the page, when you just want to move one section.
Am I doing something wrong? I assume it has something to do with the cancel
function..
I noticed that current version only works with real touch devices. It would be great if we could support at least some multi-touch gestures on trackpads, like moving around and pinch to zoom.
Here's an article that explains how to achieve that cross-browser: https://medium.com/@auchenberg/detecting-multi-touch-trackpad-gestures-in-javascript-a2505babb10e
Hey there!
first of, gigantic kudos! this lib is super useful. thank you!
i've noticed a case where the onWheel
event handler is not getting called while using bind()
as opposed to attaching an event listener via React (onWheel
) which does result in the handler being called.
is this a bug or am i doing something wrong?
if it's not i'll close it and be on my way to stackoverflow... :)
thanks again for the hard work! really enjoying making stuff with this tool.
Hi! I'm testing useGesture
with transient: true
to avoid re-rendering on drag with react-spring
and animated.div
.
In the onAction
callback, I'd like to set the spring coordinates to something that depends on the component state. However, onAction
never reads the updated state. Is this by design? Wouldn't there be a way to tell onAction
to recompute on re-render or on variable change similarly to React.useEffect
?
Here is a link to a demo that shows the difference between transient and non transient mode when it comes to reading state: https://codesandbox.io/s/oj9y335nr5
The blue square is supposed to rest on a different position based on the number of times it's been clicked, which only works properly in non-transient.
I hope this is clear enough!
Hi
I have made a quick demo
I am using a mouse and alot of times OnDrag does not detect that have have released the mouse button, which makes the content stick to the mouse.
I am missing something?
Otherwise great library.
Not sure you can do anything about it or not, but when you renamed your repo from 'react-with-gesture' to 'react-use-gesture', there was of course no npm outdated
or yarn outdated
indication that I needed to upgrade.
When listening for scroll events on the window and scrolling on a mobile device with a touch screen the scroll event will sometimes end and then start again and quickly finish. I think it might have something to do with the way the mobile browser resizes and/or handles overflow scroll on the window.
This is the code I'm using to listen for the scroll events:
const bind = useGesture({
onScroll : () => {
// ...code
},
onScrollStart: () => {
// ...code
},
onScrollEnd : () => {
// ...code
},
}, { domTarget: window })
useEffect(bind, [bind])
Clone this repo: https://github.com/rynoV/calumsieppert.me-v2
Yarn install and run:
yarn develop-mobile
(You might need to install the gatsby cli, I'm not sure)
Open ip address given by the command on a mobile device. (I was using an iphone 6 with safari and chrome)
Watch how the scroll events are triggered.
Hi I am trying to make use pinch to zoom on an element with some child element.
Here is an example I made with CRA. If you have one finger inside the red box one finger outside the box, pinch does not work.
import React from 'react';
import { useGesture } from "react-use-gesture";
import { useSpring, animated } from "react-spring";
import logo from './logo.svg';
import './App.css';
const App: React.FC = () => {
const [{ scale }, update] = useSpring(() => ({
scale: 1,
}));
const getScale = (momentum: number) => Math.max(0.1, ((scale.getValue() as number) + momentum));
const binding = useGesture({
onPinch: ({ vdva }) => {
update({ scale: getScale(vdva[0]) });
},
});
return (
<div {...binding()} className="App">
<animated.div style={{
transform: scale.interpolate(s => `scale(${s})`),
}}>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<div style={{width: 150, height: 150, backgroundColor: 'red'}}></div>
</header>
</animated.div>
</div>
);
}
export default App;
Hi,
this is not a real issue, just a quick tip for people who needs the listener to be added to a target outside of the component render, for example, window
:
const bind = useGesture(() => {})
const cb = bind()
window.addEventListener("mousedown", cb.onMouseDown, { passive: true })
window.addEventListener("touchstart", cb.onTouchStart, { passive: true })
This works fine.
The example https://codesandbox.io/embed/zq19y1xr9m does not seem to work with with the latest versions of react-spring and react; @see: https://codesandbox.io/s/j2nl5z3m45 . Any ideas?
On mobile browsers (at least on iOS: Safari, Chrome), I'm getting the default behavior of a swipe to the left triggering a back navigation (along with the gesture provided with this library). It would be great if this library exposed a way to prevent that default behavior.
I see you removed preventDefault
on the last release, I'm not sure what the issue was... but it would be great if there was some way to configure this (I'm not seeing one atm).
https://bundlephobia.com/result?p=react-use-gesture
I rely on bundlephobia for evaluating new packages. Got the following error:
MissingDependencyError
This package (or this version) uses@babel/runtime/helpers/esm/extends
and@babel/runtime/helpers/esm/objectWithoutPropertiesLoose
, but does not specify them either as a dependency or a peer dependency
ERROR in ./node_modules/react-use-gesture/web.js
Module not found: Error: Can't resolve '@babel/runtime/helpers/esm/extends' in '/Users/pavel/Developer/Epistema/MentWebClient/node_modules/react-use-gesture'
@ ./node_modules/react-use-gesture/web.js 2:0-58 61:9-17 70:9-17 86:9-17 101:9-17 235:8-16 236:8-16 237:10-18 238:9-17 239:9-17 301:13-21 389:23-31 392:10-18 421:29-37 440:9-17 481:25-33 486:31-39 496:9-17 528:25-33 533:31-39 543:9-17 573:25-33 578:31-39 588:9-17 618:23-31 637:23-31 640:16-24 713:23-31 716:10-18 740:29-37 753:9-17 793:25-33 798:31-39 810:9-17 834:9-17 851:29-37 864:9-17 912:20-28 941:19-27 942:16-24 943:20-28 957:20-28 1048:30-38
getting this error for "extends" and for "objectWithoutPropertiesLoose". I do have the "extends" file but its under @babel/runtime/helpers/extends
without esm
.
Im using typescript for this project, do you know how can I fix this issue?
thanks
Hey guys, I'm wondering if you guys would have a good idea how to do clamping w/ the onWheel event.
The problem I'm hitting is that a group of wheel events are being treated as a single gesture, which means, if I clamp the position at a boundary, but you continue to scroll, useGesture
thinks you're still scrolling into infinity. If you quickly change directions, you have to make up for whatever "distance" you covered scrolling up.
The current behavior makes sense for things like the current "Boundaries" example, where clamped dragging doesn't go back right away until the mouse is back within the bounds. But in scrolling, it doesn't quite feel right.
If you stop scrolling, for a second, the gesture ends and a new on starts and you're able to scroll immediately without the large invisible delta distance.
I guess I'd like to find a way to clamp the delta? I imagine I could force something to happen with memo
, but I figured you guys might have better ideas.
Thanks again for the awesome stuff!
Hi.
I used the code from this official demo as the basis of some work in a project, but the drag did not work at all.
So I copied the demo cleanly into a new create-react-app project (which is what I'm using) and the drag still does not work. If I change the useGesture hook to explicitly use onDrag
it sort of works. The picture scales down as I drag, but does not move until I get to a certain point and then it slides. I can see the numbers in transform() starting to go up, and as soon as I stop dragging they go down again.
I've been trying to work out what is going on for longer than I care to admit.
This is on Chrome Version 77.0.3865.90 (Official Build) (64-bit).
Here is my packages.json:
{
"name": "swipe-test",
"version": "0.1.0",
"private": true,
"dependencies": {
"lodash-es": "^4.17.15",
"react": "^16.10.0",
"react-dom": "^16.10.0",
"react-spring": "^8.0.27",
"react-use-gesture": "^6.0.1"
},
"devDependencies": {
"react-scripts": "3.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
I also tried downgrading to React v 16.9.0
I try to use your component to zoom an image in a canvas by scrolling as well as by pinching. Therefore I use useGesture
to add onWheel
and onPinch
listeners. However, when I pinch, both callbacks are called.
So I tried to only use the onWheel callback. It is enough to zoom my canvas whenever the user scrolls or pinches. However, since pinching gives way less onWheel events as the actual scrolling, I end up with a way slower zooming when I use pinching.
So I have three questions:
Many thanks in advance!
Hi! I understand this lib is made for handling gestures on mobile which translate to the mouse being clicked on desktop, but I was curious if there was a way to make it work onMouseMove like for a rollover?
If you go to the "Draggable List" example in mobile view, dragging items vertically causes the browser to scroll, is there a way to prevent that like preventDefault? I think would be nice to show in the example
Looks like version 4.0.7 introduced a default prop for window but my SSR rendering app is now crashing because there isn't a catch for when window is not defined. For the time being I just rolled back to version 4.0.5 and everything is working fine.
I'm migrating from hammerjs to react-use-gesture@next and one thing I'm missing is getting coordinates of center position of the pinch. For pinch we only have distance and angle but no center position between fingers. I would use it to set transformOrigin in CSS to make the scaling originate from actual central position of the pinch. I tried accessing event.touches but typescript complains that Property 'touches' does not exist on type 'MouseEvent | TouchEvent'.
so i can't even calculate it myself :)
By looking at hammerjs' source calculating center position should be easy: https://github.com/hammerjs/hammer.js/blob/563b5b1e4bfbb5796798dd286cd57b7c56f1eb9e/src/inputjs/get-center.js
Hey react-use-gesture contributors,
Can you help with this usePinch to redirect (redirect = react-router's this.props.history.push('/')) example?
I'm sure this isn't an issue, just with me not using it correctly.
I made this codesandbox for you.
Your response would help so much!
btw, I'm going to usePinch for this on every page of my app.
const binding = usePinch(({d}) => { (d < 40) ? this.props.history.push('/'):null})
return (
<div {...binding()}>
<img
className="alternative"
src="https://www.dl.dropboxusercontent.com/s/9ctrgn3angb8zz4/Screen%20Shot%202019-10-02%20at%2011.30.21%20AM.png?dl=1"
alt="error"
/>
</div>
);
}
export default Login
From the docs...
"If you use react-three-fiber, onWheel might prove useful to simulate scroll on canvas elements."
@drcmda Do you have an example of this yet? :)
Error: [Intervention] Unable to preventDefault inside passive event listener due to target being treated as passive.
occurs when adding pinch gesture
Example
const bindPinch = usePinch((event) => {
console.log(event);
}, {
event: {
passive: false,
},
});
My goal is - handle macbook touchpad pinch gesture, and prevent default behavior.
I modified a local version of react-with-gesture to return a simple distance between two fingers
const initialState = {
// [Distance, Scale]
pinch: [0, 1],
}
handleDown() {
const newProps {
{...}
pinch: [0, state.pinch[1]],
}
}
handleMove() {
// Get the current pinch distance from touch event
const pinchDistance = (() => {
if (event.touches && event.touches.length === 2) {
const [finger1, finger2] = event.touches;
return Math.hypot(
finger1.pageX - finger2.pageX,
finger1.pageY - finger2.pageY
);
}
return 0;
})();
// Compare to previous pinch distance, find delta
const pinchDelta = pinchDistance - state.pinch[0];
{...}
return {
{...},
pinch: [pinchDistance, pinchScale]
}
}
I'm using this for calculating a pinch/zoom scale(x)
factor. While I'd like to see this lib support two finger events, what I've got feels really specific to my usecase.
What would/should the api look like if it had handlers/events specific to two finger gestures?
Hoping someone can help me with this - not sure if I'm missing how to implement with the existing functionality, or if this is a valid use case to warrant additional functionality.
Basically I'm using touch events to control the height of a drawer being slid up and down the y axis. The local
delta works perfectly for this, as the height is kept stable across multiple touch events (similar to this example from the README). However, in addition to touch events controlling the height of the drawer, there's also a collapse/uncollapse button that allows the user to fully set the height of the drawer to 0/100 at the click of a button. Ideally, I would want to keep the history provided by the local
delta, but additionally be able to reset or update that history to a particular value, when the user clicks this button.
Let me know if that makes sense or if I can help clarify at all. Thanks!
I was building a carousel that should be draggable with a mouse, while on touch devices it just uses native horizontal scroll behaviour. So <Gesture />
shouldn't listen for touch events in this case. I've opened a PR that implements those options for both touch and mouse.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.