svelte-gestures
Advanced tools
Comparing version 1.0.1 to 1.0.2
# Changelog | ||
## 1.0.2 | ||
- **Default swipe distance** decreased from `100px` to `60px`. | ||
- API events added to the documentation. | ||
- `pointerup` in the lib core is now replaced with `lostpointercapture`. It should fix cases where pointerup event has never been called, because of triggering context menu on touch dektop, changing browser tab in middle of swipe etc. | ||
- Custom Gesture implementation example added to the documentation. | ||
## 1.0.1 | ||
- Improved documentation | ||
## 1.0.0 | ||
* Initial release | ||
- Initial release |
const DEFAULT_DELAY = 300; | ||
const DEFAULT_MIN_SWIPE_DISTANCE = 100; // in pixels | ||
const DEFAULT_MIN_SWIPE_DISTANCE = 60; // in pixels | ||
@@ -32,2 +32,3 @@ function addEventListener(node, event, handler) { | ||
onDownCallback === null || onDownCallback === void 0 ? void 0 : onDownCallback(activeEvents, event); | ||
const pointerId = event.pointerId; | ||
const removePointermoveHandler = addEventListener(node, 'pointermove', event => { | ||
@@ -40,12 +41,14 @@ activeEvents = activeEvents.map(activeEvent => { | ||
}); | ||
const removePointerupHandler = addEventListener(node, 'pointerup', event => { | ||
activeEvents = removeEvent(event, activeEvents); | ||
const removeLostpointercaptureHandler = addEventListener(node, 'lostpointercapture', event => { | ||
if (pointerId === event.pointerId) { | ||
activeEvents = removeEvent(event, activeEvents); | ||
if (!activeEvents.length) { | ||
removePointermoveHandler(); | ||
removePointerupHandler(); | ||
if (!activeEvents.length) { | ||
removePointermoveHandler(); | ||
removeLostpointercaptureHandler(); | ||
} | ||
dispatch(node, gestureName, event, activeEvents, 'up'); | ||
onUpCallback === null || onUpCallback === void 0 ? void 0 : onUpCallback(activeEvents, event); | ||
} | ||
dispatch(node, gestureName, event, activeEvents, 'up'); | ||
onUpCallback === null || onUpCallback === void 0 ? void 0 : onUpCallback(activeEvents, event); // onMoveCallback?.(activeEvents, event); | ||
}); | ||
@@ -52,0 +55,0 @@ } |
@@ -1,1 +0,1 @@ | ||
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).svelteGestures={})}(this,(function(t){"use strict";const n=300;function e(t,n,e){return t.addEventListener(n,e),()=>t.removeEventListener(n,e)}function i(t,n,e,i,o){t.dispatchEvent(new CustomEvent(`${n}${o}`,{detail:{event:e,pointersCount:i.length}}))}function o(t,n,o,l,c){n.style.touchAction="none";let u=[];const a=e(n,"pointerdown",(function(a){u.push(a),i(n,t,a,u,"down"),null==l||l(u,a);const r=e(n,"pointermove",(e=>{u=u.map((t=>e.pointerId===t.pointerId?e:t)),i(n,t,e,u,"move"),null==o||o(u,e)})),s=e(n,"pointerup",(e=>{u=function(t,n){return n.filter((n=>t.pointerId!==n.pointerId))}(e,u),u.length||(r(),s()),i(n,t,e,u,"up"),null==c||c(u,e)}))}));return{destroy:()=>{a()}}}function l(t){return Math.hypot(t[0].clientX-t[1].clientX,t[0].clientY-t[1].clientY)}function c(t){const n={left:{top:360,bottom:180},right:{top:0,bottom:180}},e=t[1].clientX-t[0].clientX,i=t[0].clientY-t[1].clientY,o=Math.atan(e/i)/(Math.PI/180),l=e>0?n.right:n.left;return o+(i>0?l.top:l.bottom)}t.DEFAULT_DELAY=n,t.DEFAULT_MIN_SWIPE_DISTANCE=100,t.pan=function(t,e={delay:n}){let i;return o("pan",t,(function(n,o){if(1===n.length&&Date.now()-i>e.delay){const n=t.getBoundingClientRect(),e=Math.round(o.clientX-n.left),i=Math.round(o.clientY-n.top);e>=0&&i>=0&&e<=n.width&&i<=n.height&&t.dispatchEvent(new CustomEvent("pan",{detail:{x:e,y:i}}))}}),(function(){i=Date.now()}),null)},t.pinch=function(t){const n="pinch";let e=null,i=0;return o(n,t,(function(o){if(2===o.length){const c=l(o);if(null!==e&&c!==e){const e=c/i;t.dispatchEvent(new CustomEvent(n,{detail:{scale:e}}))}e=c}}),(function(t){2===t.length&&(i=l(t))}),(function(t){1===t.length&&(e=null)}))},t.rotate=function(t){const n="rotate";let e=null,i=0;return o(n,t,(function(o){if(2===o.length){const l=c(o);if(null!==e&&l!==e){let e=l-i;e>180&&(e-=360),t.dispatchEvent(new CustomEvent(n,{detail:{rotation:e}}))}e=l}}),(function(t){2===t.length&&(t=t.sort(((t,n)=>t.clientX-n.clientX)),i=c(t))}),(function(t){1===t.length&&(e=null)}))},t.setPointerControls=o,t.swipe=function(t,e={timeframe:n,minSwipeDistance:100}){const i="swipe";let l,c,u;return o(i,t,null,(function(t,n){c=n.clientX,u=n.clientY,l=Date.now()}),(function(n,o){if(0===n.length&&Date.now()-l<e.timeframe){const n=o.clientX-c,l=o.clientY-u,a=Math.abs(n),r=Math.abs(l);let s=null;a>=2*r&&a>e.minSwipeDistance?s=n>0?"right":"left":r>=2*a&&r>e.minSwipeDistance&&(s=l>0?"bottom":"top"),s&&t.dispatchEvent(new CustomEvent(i,{detail:{direction:s}}))}}))},t.tap=function(t,e={timeframe:n}){let i,l,c;return o("tap",t,null,(function(t,n){l=n.clientX,c=n.clientY,i=Date.now()}),(function(n,o){if(Math.abs(o.clientX-l)<4&&Math.abs(o.clientY-c)<4&&Date.now()-i<e.timeframe){const n=t.getBoundingClientRect(),e=Math.round(o.clientX-n.left),i=Math.round(o.clientY-n.top);t.dispatchEvent(new CustomEvent("tap",{detail:{x:e,y:i}}))}}))},Object.defineProperty(t,"__esModule",{value:!0})})); | ||
!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t=t||self).svelteGestures={})}(this,(function(t){"use strict";const n=300;function e(t,n,e){return t.addEventListener(n,e),()=>t.removeEventListener(n,e)}function i(t,n,e,i,o){t.dispatchEvent(new CustomEvent(`${n}${o}`,{detail:{event:e,pointersCount:i.length}}))}function o(t,n,o,l,c){n.style.touchAction="none";let u=[];const r=e(n,"pointerdown",(function(r){u.push(r),i(n,t,r,u,"down"),null==l||l(u,r);const a=r.pointerId,s=e(n,"pointermove",(e=>{u=u.map((t=>e.pointerId===t.pointerId?e:t)),i(n,t,e,u,"move"),null==o||o(u,e)})),f=e(n,"lostpointercapture",(e=>{a===e.pointerId&&(u=function(t,n){return n.filter((n=>t.pointerId!==n.pointerId))}(e,u),u.length||(s(),f()),i(n,t,e,u,"up"),null==c||c(u,e))}))}));return{destroy:()=>{r()}}}function l(t){return Math.hypot(t[0].clientX-t[1].clientX,t[0].clientY-t[1].clientY)}function c(t){const n={left:{top:360,bottom:180},right:{top:0,bottom:180}},e=t[1].clientX-t[0].clientX,i=t[0].clientY-t[1].clientY,o=Math.atan(e/i)/(Math.PI/180),l=e>0?n.right:n.left;return o+(i>0?l.top:l.bottom)}t.DEFAULT_DELAY=n,t.DEFAULT_MIN_SWIPE_DISTANCE=60,t.pan=function(t,e={delay:n}){let i;return o("pan",t,(function(n,o){if(1===n.length&&Date.now()-i>e.delay){const n=t.getBoundingClientRect(),e=Math.round(o.clientX-n.left),i=Math.round(o.clientY-n.top);e>=0&&i>=0&&e<=n.width&&i<=n.height&&t.dispatchEvent(new CustomEvent("pan",{detail:{x:e,y:i}}))}}),(function(){i=Date.now()}),null)},t.pinch=function(t){const n="pinch";let e=null,i=0;return o(n,t,(function(o){if(2===o.length){const c=l(o);if(null!==e&&c!==e){const e=c/i;t.dispatchEvent(new CustomEvent(n,{detail:{scale:e}}))}e=c}}),(function(t){2===t.length&&(i=l(t))}),(function(t){1===t.length&&(e=null)}))},t.rotate=function(t){const n="rotate";let e=null,i=0;return o(n,t,(function(o){if(2===o.length){const l=c(o);if(null!==e&&l!==e){let e=l-i;e>180&&(e-=360),t.dispatchEvent(new CustomEvent(n,{detail:{rotation:e}}))}e=l}}),(function(t){2===t.length&&(t=t.sort(((t,n)=>t.clientX-n.clientX)),i=c(t))}),(function(t){1===t.length&&(e=null)}))},t.setPointerControls=o,t.swipe=function(t,e={timeframe:n,minSwipeDistance:60}){const i="swipe";let l,c,u;return o(i,t,null,(function(t,n){c=n.clientX,u=n.clientY,l=Date.now()}),(function(n,o){if(0===n.length&&Date.now()-l<e.timeframe){const n=o.clientX-c,l=o.clientY-u,r=Math.abs(n),a=Math.abs(l);let s=null;r>=2*a&&r>e.minSwipeDistance?s=n>0?"right":"left":a>=2*r&&a>e.minSwipeDistance&&(s=l>0?"bottom":"top"),s&&t.dispatchEvent(new CustomEvent(i,{detail:{direction:s}}))}}))},t.tap=function(t,e={timeframe:n}){let i,l,c;return o("tap",t,null,(function(t,n){l=n.clientX,c=n.clientY,i=Date.now()}),(function(n,o){if(Math.abs(o.clientX-l)<4&&Math.abs(o.clientY-c)<4&&Date.now()-i<e.timeframe){const n=t.getBoundingClientRect(),e=Math.round(o.clientX-n.left),i=Math.round(o.clientY-n.top);t.dispatchEvent(new CustomEvent("tap",{detail:{x:e,y:i}}))}}))},Object.defineProperty(t,"__esModule",{value:!0})})); |
export declare const DEFAULT_DELAY = 300; | ||
export declare const DEFAULT_MIN_SWIPE_DISTANCE = 100; | ||
export declare type GestureName = 'pan' | 'pinch' | 'tap' | 'swipe' | 'rotate'; | ||
export declare function setPointerControls(gestureName: GestureName, node: HTMLElement, onMoveCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void, onDownCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void, onUpCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void): { | ||
export declare const DEFAULT_MIN_SWIPE_DISTANCE = 60; | ||
export declare function setPointerControls(gestureName: string, node: HTMLElement, onMoveCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void, onDownCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void, onUpCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void): { | ||
destroy: () => void; | ||
}; | ||
//# sourceMappingURL=shared.d.ts.map |
@@ -5,3 +5,3 @@ { | ||
"repository": "https://github.com/Rezi/svelte-gestures", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"main": "dist/index.umd.min.js", | ||
@@ -8,0 +8,0 @@ "module": "dist/index.es.js", |
128
README.md
@@ -11,2 +11,8 @@ # svelte-gestures | ||
## API events | ||
Except main event, each resogniser triggers, three more events with names composed from action name (`pan` | `pinch` | `tap` | `swipe` | `rotate`) and event type (`up` | `down` | `move`). | ||
For example `pan` action has for example `panup`, `pandown`, `panmove`. It dispatches `event.detail` with following property `{ event: PointerEvent, pointersCount: number }`. First is native pointer event, second is number of active pointers. | ||
## Pan | ||
@@ -32,3 +38,7 @@ | ||
<div use:pan={{delay:300}} on:pan={handler} style="width:500px;height:500px;border:1px solid black;"> | ||
<div | ||
use:pan="{{delay:300}}" | ||
on:pan="{handler}" | ||
style="width:500px;height:500px;border:1px solid black;" | ||
> | ||
pan: {x} {y} | ||
@@ -54,3 +64,7 @@ </div> | ||
<div use:pinch on:pinch={handler} style="width:500px;height:500px;border:1px solid black;"> | ||
<div | ||
use:pinch | ||
on:pinch="{handler}" | ||
style="width:500px;height:500px;border:1px solid black;" | ||
> | ||
pinch scale: {scale} | ||
@@ -60,3 +74,2 @@ </div> | ||
## Rotate | ||
@@ -80,3 +93,7 @@ | ||
<div use:rotate on:rotate={handler} style="width:500px;height:500px;border:1px solid black;"> | ||
<div | ||
use:rotate | ||
on:rotate="{handler}" | ||
style="width:500px;height:500px;border:1px solid black;" | ||
> | ||
rotation: {rotation} | ||
@@ -88,5 +105,5 @@ </div> | ||
Swipe action fires `swipe` event: `event.detail.direction`. It accepts props as parameter: `{ timeframe: number; minSwipeDistance: number }` with default values 300ms and 100px. Swipe is fired if preset distance in propper direction is done in preset time. | ||
Swipe action fires `swipe` event: `event.detail.direction`. It accepts props as parameter: `{ timeframe: number; minSwipeDistance: number }` with default values 300ms and 60px. Swipe is fired if preset distance in propper direction is done in preset time. | ||
`event.detail.direction` represents direction of swipe: 'top' | 'right' | 'bottom' | 'left' | ||
`event.detail.direction` represents direction of swipe: 'top' | 'right' | 'bottom' | 'left' | ||
@@ -105,3 +122,3 @@ [> repl Swipe demo](https://svelte.dev/repl/f696ca27e6374f2cab1691727409a31d?version=3.38.2) | ||
<div use:swipe={{ timeframe: 300, minSwipeDistance: 100 }} on:swipe={handler} style="width:500px;height:500px;border:1px solid black;"> | ||
<div use:swipe={{ timeframe: 300, minSwipeDistance: 60 }} on:swipe={handler} style="width:500px;height:500px;border:1px solid black;"> | ||
direction: {direction} | ||
@@ -122,3 +139,3 @@ </div> | ||
import { tap } from 'svelte-gestures'; | ||
let x; | ||
@@ -138,4 +155,97 @@ let y; | ||
# Custom gestures | ||
You are encouraged to define your own custom gestures. There is a `setPointerControls` function exposed by the `svelte-gestures`. It handle all the events registration/deregistration needed for handling gestures; you just need to pass callbacks in it. | ||
here are all the arguments of `setPointerControls`: | ||
```typescript | ||
gestureName: string, | ||
node: HTMLElement, | ||
onMoveCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void, | ||
onDownCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void, | ||
onUpCallback: (activeEvents: PointerEvent[], event: PointerEvent) => void | ||
``` | ||
See how doubletap custome gesture is implemented: | ||
[> repl Custom gesture (doubletap) demo](https://svelte.dev/repl/c56082d9d056460d80e53cd71efddefe?version=3.38.2) | ||
```html | ||
<script> | ||
import { setPointerControls, DEFAULT_DELAY } from 'svelte-gestures'; | ||
let dx; | ||
let dy; | ||
function doubletapHandler(event) { | ||
dx = event.detail.x; | ||
dy = event.detail.y; | ||
} | ||
function doubletap( | ||
node: HTMLElement, | ||
parameters: { timeframe: number } = { timeframe: DEFAULT_DELAY } | ||
): { destroy: () => void } { | ||
const gestureName = 'doubletap'; | ||
let startTime: number; | ||
let clientX: number; | ||
let clientY: number; | ||
let tapCount = 0; | ||
let spread = 20; | ||
let timeout; | ||
function onUp(activeEvents: PointerEvent[], event: PointerEvent) { | ||
if ( | ||
Math.abs(event.clientX - clientX) < spread && | ||
Math.abs(event.clientY - clientY) < spread && | ||
Date.now() - startTime < parameters.timeframe | ||
) { | ||
if (!tapCount) { | ||
tapCount++; | ||
} else { | ||
const rect = node.getBoundingClientRect(); | ||
const x = Math.round(event.clientX - rect.left); | ||
const y = Math.round(event.clientY - rect.top); | ||
node.dispatchEvent( | ||
new CustomEvent(gestureName, { | ||
detail: { x, y }, | ||
}) | ||
); | ||
clearTimeout(timeout); | ||
tapCount = 0; | ||
} | ||
} | ||
} | ||
function onDown(activeEvents: PointerEvent[], event: PointerEvent) { | ||
if (!tapCount) { | ||
clientX = event.clientX; | ||
clientY = event.clientY; | ||
startTime = Date.now(); | ||
} | ||
timeout = setTimeout(() => { | ||
tapCount = 0; | ||
}, parameters.timeframe); | ||
} | ||
return setPointerControls(gestureName, node, null, onDown, onUp); | ||
} | ||
</script> | ||
<div | ||
use:doubletap | ||
on:doubletap="{doubletapHandler}" | ||
style="width:500px;height:500px;border:1px solid black;" | ||
> | ||
double tap me {dx} {dy} | ||
</div> | ||
``` | ||
## License | ||
[MIT](LICENSE) | ||
[MIT](LICENSE) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
24080
287
243