UNPKG

mmenu-js

Version:

The best javascript plugin for app look-alike on- and off-canvas menus with sliding submenus for your website and webapp.

277 lines (234 loc) 8.47 kB
import * as support from './_support'; import * as options from './_defaults'; import * as settings from './_settings'; import { percentage2number } from './_helpers'; import { extend } from '../helpers'; export default class DragEvents { /** The draggable area. */ surface: HTMLElement; /** How far from the sides the gesture can start. */ area: dragArea; /** Tresholds for gestures. */ treshold: dragTreshold; /** Where the gesture started. */ startPosition: dragCoordinates; /** The dragged x- and y-distances since the start. */ distance: dragCoordinates; /** The dragged x- and y-distances since the last event. */ movement: dragCoordinates; /** The axis of the gesture. */ axis: 'x' | 'y'; /** The state of the gesture. */ state: number; /** * Create the gestures. * @param {HTMLElement} surface The surface for the gesture. * @param {object} area Restriction where on the surface the gesture can be started. * @param {object} treshold Treshold for the gestures. */ constructor( surface: HTMLElement, area?: dragArea, treshold?: dragTreshold ) { this.surface = surface; this.area = extend(area, options.area); this.treshold = extend(treshold, options.treshold); // Set the mouse/touch events. if (!this.surface['mmHasDragEvents']) { this.surface.addEventListener( support.touch ? 'touchstart' : 'mousedown', this.start.bind(this) ); this.surface.addEventListener( support.touch ? 'touchend' : 'mouseup', this.stop.bind(this) ); this.surface.addEventListener( support.touch ? 'touchleave' : 'mouseleave', this.stop.bind(this) ); this.surface.addEventListener( support.touch ? 'touchmove' : 'mousemove', this.move.bind(this) ); } this.surface['mmHasDragEvents'] = true; } /** * Starting the touch gesture. * @param {Event} event The touch event. */ start(event) { /** The widht of the surface. */ var width = this.surface.clientWidth; /** The height of the surface. */ var height = this.surface.clientHeight; // Check if the gesture started below the area.top. var top = percentage2number(this.area.top, height); if (typeof top == 'number') { if (event.pageY < top) { return; } } // Check if the gesture started before the area.right. var right = percentage2number(this.area.right, width); if (typeof right == 'number') { right = width - right; if (event.pageX > right) { return; } } // Check if the gesture started above the area.bottom. var bottom = percentage2number(this.area.bottom, height); if (typeof bottom == 'number') { bottom = height - bottom; if (event.pageY > bottom) { return; } } // Check if the gesture started after the area.left. var left = percentage2number(this.area.left, width); if (typeof left == 'number') { if (event.pageX < left) { return; } } // Store the start x- and y-position. this.startPosition = { x: event.pageX, y: event.pageY }; // Set the state of the gesture to "watching". this.state = settings.state.watching; } /** * Stopping the touch gesture. * @param {Event} event The touch event. */ stop(event) { // Dispatch the "dragEnd" events. if (this.state == settings.state.dragging) { /** The direction. */ const dragDirection = this._dragDirection(); /** The event information. */ const detail = this._eventDetail(dragDirection); this._dispatchEvents('drag*End', detail); // Dispatch the "swipe" events. if (Math.abs(this.movement[this.axis]) > this.treshold.swipe) { /** The direction. */ const swipeDirection = this._swipeDirection(); detail.direction = swipeDirection; this._dispatchEvents('swipe*', detail); } } // Set the state of the gesture to "inactive". this.state = settings.state.inactive; } /** * Doing the touch gesture. * @param {Event} event The touch event. */ move(event) { switch (this.state) { case settings.state.watching: case settings.state.dragging: this.movement = { x: event.movementX, y: event.movementY }; this.distance = { x: event.pageX - this.startPosition.x, y: event.pageY - this.startPosition.y }; this.axis = Math.abs(this.distance.x) > Math.abs(this.distance.y) ? 'x' : 'y'; /** The direction. */ const dragDirection = this._dragDirection(); /** The event information. */ const detail = this._eventDetail(dragDirection); // Watching for the gesture to go past the treshold. if (this.state == settings.state.watching) { if ( Math.abs(this.distance[this.axis]) > this.treshold.start ) { this._dispatchEvents('drag*Start', detail); // Set the state of the gesture to "inactive". this.state = settings.state.dragging; } } // Dispatch the "drag" events. if (this.state == settings.state.dragging) { this._dispatchEvents('drag*Move', detail); } break; } } /** * Get the event details. * @param {string} direction Direction for the event (up, right, down, left). * @return {object} The event details. */ _eventDetail(direction: string) { var distX = this.distance.x; var distY = this.distance.y; if (this.axis == 'x') { distX -= distX > 0 ? this.treshold.start : 0 - this.treshold.start; } if (this.axis == 'y') { distY -= distY > 0 ? this.treshold.start : 0 - this.treshold.start; } return { axis: this.axis, direction: direction, movementX: this.movement.x, movementY: this.movement.y, distanceX: distX, distanceY: distY }; } /** * Dispatch the events * @param {string} eventName The name for the events to dispatch. * @param {object} detail The event details. */ _dispatchEvents(eventName: string, detail) { /** General event, e.g. "drag" */ var event = new CustomEvent(eventName.replace('*', ''), { detail }); this.surface.dispatchEvent(event); /** Axis event, e.g. "dragX" */ var axis = new CustomEvent( eventName.replace('*', this.axis.toUpperCase()), { detail } ); this.surface.dispatchEvent(axis); /** Direction event, e.g. "dragLeft" */ var direction = new CustomEvent( eventName.replace('*', detail.direction), { detail } ); this.surface.dispatchEvent(direction); } /** * Get the dragging direction. * @return {string} The direction in which the user is dragging. */ _dragDirection() { return settings.directionNames[this.axis][ this.distance[this.axis] > 0 ? 0 : 1 ]; } /** * Get the dragging direction. * @return {string} The direction in which the user is dragging. */ _swipeDirection() { return settings.directionNames[this.axis][ this.movement[this.axis] > 0 ? 0 : 1 ]; } }