UNPKG

joicomponents

Version:

Design patterns to build native web components

139 lines (128 loc) 4.42 kB
const selectListener = Symbol("selectstartListener"); const startListener = Symbol("pointerDownListener"); const moveListener = Symbol("pointerMoveListener"); const stopListener = Symbol("pointerUpListener"); const start = Symbol("start"); const move = Symbol("move"); const end = Symbol("end"); const swipe = Symbol("swipe"); const cachedEvents = Symbol("cachedEvents"); function flingAngle(x = 0, y = 0) { return ((Math.atan2(y, -x) * 180 / Math.PI)+270)%360; } function findLastEventOlderThan(events, timeTest) { for (let i = events.length - 1; i >= 0; i--) { if (events[i].timeStamp < timeTest) return events[i]; } return null; } /** * !!! Dependency: pointerevents !!! * * Mixin that translates a sequence of one pointerdown and several pointermove events into one or more swipe events. * * The "swipe" event only occurs if the pointermove events have: * - moved a minimum 50px * - in one direction * - during the last 200ms. * * The minimum distance and duration can be changed using these properties on the element * .flingSettings.minDistance = 50; * .flingSettings.minDuration = 200; * * swipe.detail * .x * .y * .distX * .distY * .diagonalPx * .durationMs * .speedPxMs * .pointerevent: e * .xSpeedPxMs * .ySpeedPxMs * .angle * * The angle starts at 12 o'clock and counts clockwise from 0 to 360 degrees. * up/north: 0 * right/east: 90 * down/south: 180 * left/west: 270 * * !!! Dependency: pointerevents !!! * !!! for Safari and older browsers use PEP: https://github.com/jquery/PEP !!! * * @param Base * @returns {SwipeGesture} */ export const SwipeGesture = function (Base) { return class extends Base { constructor() { super(); this[selectListener] = e => e.preventDefault() && false; this[startListener] = (e) => this[start](e); this[moveListener] = (e) => this[move](e); this[stopListener] = (e) => this[end](e); this[cachedEvents] = undefined; this.swipeSettings = {minDistance: 50, minDuration: 200}; } connectedCallback() { if (super.connectedCallback) super.connectedCallback(); this.addEventListener("selectstart", this[selectListener]); this.addEventListener("pointerdown", this[startListener]); } disconnectedCallback() { if (super.disconnectedCallback) super.disconnectedCallback(); this.removeEventListener("selectstart", this[startListener]); this.removeEventListener("pointerdown", this[startListener]); } [start](e) { this.setPointerCapture(e.pointerId); this.addEventListener("pointermove", this[moveListener]); this.addEventListener("pointerup", this[stopListener]); this.addEventListener("pointercancel", this[stopListener]); this[cachedEvents] = [e]; } [move](e) { const swipeStart = findLastEventOlderThan(this[cachedEvents], e.timeStamp - this.swipeSettings.minDuration); this[cachedEvents].push(e); if (!swipeStart) return; const startX = swipeStart.x; const startY = swipeStart.y; const stopX = e.x; const stopY = e.y; const distX = stopX - startX; const distY = stopY - startY; const diagonalPx = Math.sqrt(distX * distX + distY * distY); if (diagonalPx < this.swipeSettings.minDistance) return; this[cachedEvents] = [e]; //reset the cachedEvents. const durationMs = e.timeStamp - swipeStart.timeStamp; this.dispatchEvent(new CustomEvent("swipe", { bubbles: true, composed: true, detail: { startX, startY, stopX, stopY, distX, distY, diagonalPx, durationMs, speedPxMs: diagonalPx / durationMs, xSpeedPxMs: distX / durationMs, ySpeedPxMs: distY / durationMs, angle: flingAngle(distX, distY) } })); } [end](e) { this[cachedEvents] = undefined; this.removeEventListener("pointermove", this[moveListener]); this.removeEventListener("pointerup", this[stopListener]); this.removeEventListener("pointercancel", this[stopListener]); this.releasePointerCapture(e.pointerId); } } };