UNPKG

11.2 kBMarkdownView Raw
1[![React Swipeable — Formidable, We build the modern web](https://raw.githubusercontent.com/FormidableLabs/react-swipeable/main/react-swipeable-Hero.png)](https://formidable.com/open-source/)
2
3React swipe event handler hook
4
5[![npm downloads](https://img.shields.io/npm/dm/react-swipeable.svg)](https://www.npmjs.com/package/react-swipeable) [![npm version](https://img.shields.io/npm/v/react-swipeable.svg)](https://www.npmjs.com/package/react-swipeable) [![build status](https://github.com/FormidableLabs/react-swipeable/actions/workflows/ci.yml/badge.svg)](https://github.com/FormidableLabs/react-swipeable/actions) [![gzip size](https://badgen.net/bundlephobia/minzip/react-swipeable)](https://bundlephobia.com/result?p=react-swipeable) [![maintenance status](https://img.shields.io/badge/maintenance-active-green.svg)](https://github.com/FormidableLabs/react-swipeable#maintenance-status)
6
7[![Edit react-swipeable image carousel](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/github/FormidableLabs/react-swipeable/tree/main/examples?file=/app/SimpleCarousel/Carousel.tsx)
8
9### [Github Pages Demo](http://formidablelabs.github.io/react-swipeable/)
10
11### Api
12
13Use the hook and set your swipe(d) handlers.
14
15```jsx
16const handlers = useSwipeable({
17 onSwiped: (eventData) => console.log("User Swiped!", eventData),
18 ...config,
19});
20return <div {...handlers}> You can swipe here </div>;
21```
22
23Spread `handlers` onto the element you wish to track swipes on.
24
25## Props / Config Options
26
27### Event handler props
28
29```js
30{
31 onSwiped, // After any swipe (SwipeEventData) => void
32 onSwipedLeft, // After LEFT swipe (SwipeEventData) => void
33 onSwipedRight, // After RIGHT swipe (SwipeEventData) => void
34 onSwipedUp, // After UP swipe (SwipeEventData) => void
35 onSwipedDown, // After DOWN swipe (SwipeEventData) => void
36 onSwipeStart, // Start of swipe (SwipeEventData) => void *see details*
37 onSwiping, // During swiping (SwipeEventData) => void
38 onTap, // After a tap ({ event }) => void
39
40 // Pass through callbacks, event provided: ({ event }) => void
41 onTouchStartOrOnMouseDown, // Called for `touchstart` and `mousedown`
42 onTouchEndOrOnMouseUp, // Called for `touchend` and `mouseup`
43}
44```
45
46#### Details
47- `onSwipeStart` - called only once per swipe at the start and before the first `onSwiping` callback
48 - The `first` property of the `SwipeEventData` will be `true`
49
50### Configuration props and default values
51
52```js
53{
54 delta: 10, // min distance(px) before a swipe starts. *See Notes*
55 preventScrollOnSwipe: false, // prevents scroll during swipe (*See Details*)
56 trackTouch: true, // track touch input
57 trackMouse: false, // track mouse input
58 rotationAngle: 0, // set a rotation angle
59 swipeDuration: Infinity, // allowable duration of a swipe (ms). *See Notes*
60 touchEventOptions: { passive: true }, // options for touch listeners (*See Details*)
61}
62```
63
64#### delta
65
66`delta` can be either a `number` or an `object` specifying different deltas for each direction, [`left`, `right`, `up`, `down`], direction values are optional and will default to `10`;
67
68```js
69{
70 delta: { up: 20, down: 20 } // up and down ">= 20", left and right default to ">= 10"
71}
72```
73
74#### swipeDuration
75A swipe lasting more than `swipeDuration`, in milliseconds, will **not** be considered a swipe.
76- It will also **not** trigger any callbacks and the swipe event will stop being tracked
77- **Defaults** to `Infinity` for backwards compatibility, a sensible duration could be something like `250`
78 - Feature mimicked from `use-gesture` [swipe.duration](https://use-gesture.netlify.app/docs/options/#swipeduration)
79
80```js
81{
82 swipeDuration: 250 // only swipes under 250ms will trigger callbacks
83}
84```
85
86#### touchEventOptions
87
88Allows the user to set the options for the touch event listeners( currently only `passive` option ).
89 - `touchstart`, `touchmove`, and `touchend` event listeners
90 - **Defaults** to `{ passive: true }`
91 - this provides users full control of if/when they want to set [passive](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options)
92 - https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options
93 - `preventScrollOnSwipe` option **supersedes** `touchEventOptions.passive` for `touchmove` event listener
94 - See `preventScrollOnSwipe` for [more details](#preventscrollonswipe-details)
95
96## Swipe Event Data
97
98All Event Handlers are called with the below event data, `SwipeEventData`.
99
100```js
101{
102 event, // source event
103 initial, // initial swipe [x,y]
104 first, // true for the first event of a tracked swipe
105 deltaX, // x offset (current.x - initial.x)
106 deltaY, // y offset (current.y - initial.y)
107 absX, // absolute deltaX
108 absY, // absolute deltaY
109 velocity, // √(absX^2 + absY^2) / time - "absolute velocity" (speed)
110 vxvy, // [ deltaX/time, deltaY/time] - velocity per axis
111 dir, // direction of swipe (Left|Right|Up|Down)
112}
113```
114
115**None of the props/config options are required.**
116
117### Hook details
118
119- Hook use requires **react >= 16.8.3**
120- The props contained in `handlers` are currently `ref` and `onMouseDown`
121 - Please spread `handlers` as the props contained in it could change as react changes event listening capabilities
122
123### `preventScrollOnSwipe` details
124
125This prop prevents scroll during swipe in most cases. Use this to **stop scrolling** in the browser while a user swipes.
126
127Swipeable will call `e.preventDefault()` internally in an attempt to stop the browser's [touchmove](https://developer.mozilla.org/en-US/docs/Web/Events/touchmove) event default action (mostly scrolling).
128
129**NOTE:** `preventScrollOnSwipe` option **supersedes** `touchEventOptions.passive` for the `touchmove` event listener
130
131**Example scenario:**
132> If a user is swiping right with props `{ onSwipedRight: userSwipedRight, preventScrollOnSwipe: true }` then `e.preventDefault()` will be called, but if the user was swiping left then `e.preventDefault()` would **not** be called.
133
134`e.preventDefault()` is only called when:
135 - `preventScrollOnSwipe: true`
136 - `trackTouch: true`
137 - the users current swipe has an associated `onSwiping` or `onSwiped` handler/prop
138
139Please experiment with the [example app](http://formidablelabs.github.io/react-swipeable/) to test `preventScrollOnSwipe`.
140
141#### passive listener details
142Swipeable adds the passive event listener option, by default, to **internal uses** of touch `addEventListener`'s. We set the `passive` option to `false` only when `preventScrollOnSwipe` is `true` and only to `touchmove`. Other listeners will retain `passive: true`.
143
144**When `preventScrollOnSwipe` is:**
145 - `true` => `el.addEventListener('touchmove', cb, { passive: false })`
146 - `false` => `el.addEventListener('touchmove', cb, { passive: true })`
147
148Here is more information on react's long running passive [event issue](https://github.com/facebook/react/issues/6436).
149
150We previously had issues with chrome lighthouse performance deducting points for not having passive option set so it is now on by default except in the case mentioned above.
151
152If, however, you really **need** _all_ of the listeners to be passive (for performance reasons or otherwise), you can prevent all scrolling on the swipeable container by using the `touch-action` css property instead, [see below for an example](#how-to-use-touch-action-to-prevent-scrolling).
153
154### Version 7 Updates and migration
155
156If upgrading from v6 refer to the release notes and the [migration doc](./migration.md).
157
158## FAQs
159
160### How can I add a swipe listener to the `document`?
161Example by [@merrywhether #180](https://github.com/FormidableLabs/react-swipeable/issues/180#issuecomment-649677983)
162
163##### Example codesandbox with swipeable on document and nested swipe
164https://codesandbox.io/s/react-swipeable-document-swipe-example-1yvr2v
165
166```js
167const { ref } = useSwipeable({
168 ...
169}) as { ref: RefCallback<Document> };
170
171useEffect(() => {
172 ref(document);
173 // Clean up swipeable event listeners
174 return () => ref({});
175});
176```
177**Note:** Issues can arise if you forget to clean up listeners - [#332](https://github.com/FormidableLabs/react-swipeable/issues/322)
178
179### How to share `ref` from `useSwipeable`?
180
181**Example ref passthrough, [more details #189](https://github.com/FormidableLabs/react-swipeable/issues/189#issuecomment-656302682):**
182```js
183const MyComponent = () => {
184 const handlers = useSwipeable({ onSwiped: () => console.log('swiped') })
185
186 // setup ref for your usage
187 const myRef = React.useRef();
188
189 const refPassthrough = (el) => {
190 // call useSwipeable ref prop with el
191 handlers.ref(el);
192
193 // set myRef el so you can access it yourself
194 myRef.current = el;
195 }
196
197 return (<div {...handlers} ref={refPassthrough} />
198}
199```
200
201### How to use `touch-action` to prevent scrolling?
202
203Sometimes you don't want the `body` of your page to scroll along with the user manipulating or swiping an item. Or you might want all of the internal event listeners to be passive and performant.
204
205You can prevent scrolling via [preventScrollOnSwipe](#preventscrollonswipe-details), which calls `event.preventDefault()` during `onTouchMove`. **But** there may be a simpler, more effective solution, which has to do with a simple CSS property.
206
207`touch-action` is a CSS property that sets how an element's region can be manipulated by a touchscreen user. See the [documentation for `touch-action`](https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action) to determine which property value to use for your particular use case.
208
209#### Static example
210```js
211const handlers = useSwipeable({
212 onSwiped: (eventData) => console.log("User Swiped!", eventData),
213 ...config,
214});
215
216return <div {...handlers} style={{ touchAction: 'pan-y' }}>Swipe here</div>;
217```
218This explanation and example borrowed from `use-gesture`'s [wonderful docs](https://use-gesture.netlify.app/docs/extras/#touch-action).
219
220#### Dynamic example
221```js
222const MySwipeableComponent = props => {
223 const [stopScroll, setStopScroll] = useState(false);
224
225 const handlers = useSwipeable({
226 onSwipeStart: () => setStopScroll(true),
227 onSwiped: () => setStopScroll(false)
228 });
229
230 return <div {...handlers} style={{ touchAction: stopScroll ? 'none' : 'auto' }}>Swipe here</div>;
231};
232```
233
234This is a somewhat contrived example as the final outcome would be similar to the static example. However, there may be cases where you want to determine when the user can scroll based on the user's swiping action along with any number of variables from state and props.
235
236## License
237
238[MIT]((./LICENSE))
239
240## Contributing
241
242Please see our [contributions guide](./CONTRIBUTING.md).
243
244### Maintainers
245[Project Maintenance](./CONTRIBUTING.md#project-maintainers)
246
247## Maintenance Status
248
249**Active:** Formidable is actively working on this project, and we expect to continue for work for the foreseeable future. Bug reports, feature requests and pull requests are welcome.