diff --git a/README.md b/README.md index af57797..8e56a3d 100644 --- a/README.md +++ b/README.md @@ -261,6 +261,8 @@ const reeller = new Reeller({ drag: { speed: 1, multiplier: 1, + axis: 'y', + invertAxis: true, changeDirection: true, inertiaMultiplier: 0.2, }, @@ -270,18 +272,20 @@ const reeller = new Reeller({ The following options of DragPlugin is available: -| Option | Type | Default | Description | -| :------------------- | :---------------------------------: | :----------: | :------------------------------------------------------------------------ | -| `speed` | `number` | `1` | Inertia duration in seconds. | -| `multiplier` | `number` | `1` | Drag movement multiplier. | -| `threshold` | `number` | `50` | Minimum release velocity in px/s required to start inertia. | -| `inertiaMultiplier` | `number` | `0.2` | Inertia distance multiplier. | -| `activationDistance` | `number` | `3` | Minimum pointer movement in px before drag starts. | -| `maxVelocity` | `number` | `3000` | Maximum release velocity in px/s used for inertia calculation. | -| `ease` | `string` | `'expo.out'` | Timing function. See [gsap easing](https://greensock.com/docs/v3/Eases). | -| `changeDirection` | `boolean` | `false` | Change autoplay direction to match the last drag direction after release. | -| `target` | `string` \| `HTMLElement` \| `null` | `null` | Drag target element or selector. Defaults to the Reeller container. | -| `preventDefault` | `boolean` | `true` | Prevent default pointer behaviour while dragging. | +| Option | Type | Default | Description | +| :------------------- | :---------------------------------: | :----------: | :-------------------------------------------------------------------------- | +| `speed` | `number` | `1` | Inertia duration in seconds. | +| `multiplier` | `number` | `1` | Drag movement multiplier. | +| `threshold` | `number` | `50` | Minimum release velocity in px/s required to start inertia. | +| `inertiaMultiplier` | `number` | `0.2` | Inertia distance multiplier. | +| `activationDistance` | `number` | `3` | Minimum pointer movement in px before drag starts. | +| `maxVelocity` | `number` | `3000` | Maximum release velocity in px/s used for inertia calculation. | +| `ease` | `string` | `'expo.out'` | Timing function. See [gsap easing](https://greensock.com/docs/v3/Eases). | +| `changeDirection` | `boolean` | `false` | Change autoplay direction to match the last drag direction after release. | +| `axis` | `'x' \| 'y'` | `'x'` | Pointer axis used for drag. Set `'y'` when the reel is rotated by `-90deg`. | +| `invertAxis` | `boolean` | `false` | Invert drag direction on the selected axis. Useful with rotated reels. | +| `target` | `string` \| `HTMLElement` \| `null` | `null` | Drag target element or selector. Defaults to the Reeller container. | +| `preventDefault` | `boolean` | `true` | Prevent default pointer behaviour while dragging. | ## Filler diff --git a/src/plugin/DragPlugin.js b/src/plugin/DragPlugin.js index 70678ec..f3c6ac5 100644 --- a/src/plugin/DragPlugin.js +++ b/src/plugin/DragPlugin.js @@ -9,6 +9,8 @@ export default class DragPlugin { * @property {number} [maxVelocity] Maximum release velocity in px/s. * @property {string} [ease] Timing function. * @property {boolean} [changeDirection] Change autoplay direction after drag. + * @property {'x'|'y'} [axis] Pointer axis used for drag. + * @property {boolean} [invertAxis] Invert drag direction on the selected axis. * @property {string|HTMLElement|null} [target] Drag target element or selector. * @property {boolean} [preventDefault] Prevent default pointer behaviour while dragging. */ @@ -34,6 +36,8 @@ export default class DragPlugin { maxVelocity: 3000, ease: 'expo.out', changeDirection: false, + axis: 'x', + invertAxis: false, target: null, preventDefault: true, }; @@ -74,25 +78,36 @@ export default class DragPlugin { /** * Move timeline by drag delta. * - * @param {number} deltaX Horizontal movement delta in pixels. + * @param {number} delta Movement delta in pixels. */ - applyDelta(deltaX) { + applyDelta(delta) { const trackWidth = this.getTrackWidth(); - if (!trackWidth || !deltaX) return; + if (!trackWidth || !delta) return; - const timeDelta = (deltaX * this.options.multiplier * this.reeller.options.speed) / trackWidth; + const timeDelta = (delta * this.options.multiplier * this.reeller.options.speed) / trackWidth; this.tl.totalTime(this.tl.totalTime() + timeDelta); } + /** + * Return pointer position for active drag axis. + * + * @param {PointerEvent} event Pointer event. + * @return {number} Pointer position. + */ + getPointerPosition(event) { + const position = this.options.axis === 'y' ? event.clientY : event.clientX; + return this.options.invertAxis ? -position : position; + } + /** * Save point for release velocity calculation. * - * @param {number} x Pointer x position. + * @param {number} position Pointer position. */ - pushSample(x) { + pushSample(position) { const time = performance.now(); - this.samples.push({x, time}); + this.samples.push({position, time}); while (this.samples.length > 5 || time - this.samples[0].time > 120) { this.samples.shift(); @@ -112,7 +127,7 @@ export default class DragPlugin { const deltaTime = last.time - first.time; if (!deltaTime) return 0; - return ((last.x - first.x) / deltaTime) * 1000; + return ((last.position - first.position) / deltaTime) * 1000; } /** @@ -143,7 +158,7 @@ export default class DragPlugin { this.tl.pause(); this.samples = []; - this.pushSample(this.lastX); + this.pushSample(this.lastPos); } /** @@ -215,8 +230,8 @@ export default class DragPlugin { resetPointer() { this.pointerId = null; this.dragging = false; - this.startX = 0; - this.lastX = 0; + this.startPos = 0; + this.lastPos = 0; this.dragDirection = 0; this.samples = []; } @@ -243,10 +258,12 @@ export default class DragPlugin { if (this.pointerId !== null) return; if (event.pointerType === 'mouse' && event.button !== 0) return; + const position = this.getPointerPosition(event); + this.stopInertia(); this.pointerId = event.pointerId; - this.startX = event.clientX; - this.lastX = event.clientX; + this.startPos = position; + this.lastPos = position; this.samples = []; if (this.target.setPointerCapture) { @@ -257,21 +274,23 @@ export default class DragPlugin { this.onPointerMove = (event) => { if (event.pointerId !== this.pointerId) return; + const position = this.getPointerPosition(event); + if (!this.dragging) { - if (Math.abs(event.clientX - this.startX) < this.options.activationDistance) return; + if (Math.abs(position - this.startPos) < this.options.activationDistance) return; this.beginDrag(); } if (this.options.preventDefault) event.preventDefault(); - const deltaX = event.clientX - this.lastX; - this.lastX = event.clientX; + const delta = position - this.lastPos; + this.lastPos = position; - if (!deltaX) return; + if (!delta) return; - this.dragDirection = Math.sign(deltaX); - this.pushSample(event.clientX); - this.applyDelta(deltaX); + this.dragDirection = Math.sign(delta); + this.pushSample(position); + this.applyDelta(delta); }; this.onPointerUp = (event) => { @@ -294,7 +313,7 @@ export default class DragPlugin { if (this.options.preventDefault) event.preventDefault(); - this.pushSample(event.clientX); + this.pushSample(this.getPointerPosition(event)); const velocity = this.getVelocity(); this.saveDirection(velocity);