123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /* global jQuery */
- (function($) {
- 'use strict';
- var options = {
- propagateSupportedGesture: false
- };
- function init(plot) {
- plot.hooks.processOptions.push(initTouchNavigation);
- }
- function initTouchNavigation(plot, options) {
- var gestureState = {
- twoTouches: false,
- currentTapStart: { x: 0, y: 0 },
- currentTapEnd: { x: 0, y: 0 },
- prevTap: { x: 0, y: 0 },
- currentTap: { x: 0, y: 0 },
- interceptedLongTap: false,
- isUnsupportedGesture: false,
- prevTapTime: null,
- tapStartTime: null,
- longTapTriggerId: null
- },
- maxDistanceBetweenTaps = 20,
- maxIntervalBetweenTaps = 500,
- maxLongTapDistance = 20,
- minLongTapDuration = 1500,
- pressedTapDuration = 125,
- mainEventHolder;
- function interpretGestures(e) {
- var o = plot.getOptions();
- if (!o.pan.active && !o.zoom.active) {
- return;
- }
- updateOnMultipleTouches(e);
- mainEventHolder.dispatchEvent(new CustomEvent('touchevent', { detail: e }));
- if (isPinchEvent(e)) {
- executeAction(e, 'pinch');
- } else {
- executeAction(e, 'pan');
- if (!wasPinchEvent(e)) {
- if (isDoubleTap(e)) {
- executeAction(e, 'doubleTap');
- }
- executeAction(e, 'tap');
- executeAction(e, 'longTap');
- }
- }
- }
- function executeAction(e, gesture) {
- switch (gesture) {
- case 'pan':
- pan[e.type](e);
- break;
- case 'pinch':
- pinch[e.type](e);
- break;
- case 'doubleTap':
- doubleTap.onDoubleTap(e);
- break;
- case 'longTap':
- longTap[e.type](e);
- break;
- case 'tap':
- tap[e.type](e);
- break;
- }
- }
- function bindEvents(plot, eventHolder) {
- mainEventHolder = eventHolder[0];
- eventHolder[0].addEventListener('touchstart', interpretGestures, false);
- eventHolder[0].addEventListener('touchmove', interpretGestures, false);
- eventHolder[0].addEventListener('touchend', interpretGestures, false);
- }
- function shutdown(plot, eventHolder) {
- eventHolder[0].removeEventListener('touchstart', interpretGestures);
- eventHolder[0].removeEventListener('touchmove', interpretGestures);
- eventHolder[0].removeEventListener('touchend', interpretGestures);
- if (gestureState.longTapTriggerId) {
- clearTimeout(gestureState.longTapTriggerId);
- gestureState.longTapTriggerId = null;
- }
- }
- var pan = {
- touchstart: function(e) {
- updatePrevForDoubleTap();
- updateCurrentForDoubleTap(e);
- updateStateForLongTapStart(e);
- mainEventHolder.dispatchEvent(new CustomEvent('panstart', { detail: e }));
- },
- touchmove: function(e) {
- preventEventBehaviors(e);
- updateCurrentForDoubleTap(e);
- updateStateForLongTapEnd(e);
- if (!gestureState.isUnsupportedGesture) {
- mainEventHolder.dispatchEvent(new CustomEvent('pandrag', { detail: e }));
- }
- },
- touchend: function(e) {
- preventEventBehaviors(e);
- if (wasPinchEvent(e)) {
- mainEventHolder.dispatchEvent(new CustomEvent('pinchend', { detail: e }));
- mainEventHolder.dispatchEvent(new CustomEvent('panstart', { detail: e }));
- } else if (noTouchActive(e)) {
- mainEventHolder.dispatchEvent(new CustomEvent('panend', { detail: e }));
- }
- }
- };
- var pinch = {
- touchstart: function(e) {
- mainEventHolder.dispatchEvent(new CustomEvent('pinchstart', { detail: e }));
- },
- touchmove: function(e) {
- preventEventBehaviors(e);
- gestureState.twoTouches = isPinchEvent(e);
- if (!gestureState.isUnsupportedGesture) {
- mainEventHolder.dispatchEvent(new CustomEvent('pinchdrag', { detail: e }));
- }
- },
- touchend: function(e) {
- preventEventBehaviors(e);
- }
- };
- var doubleTap = {
- onDoubleTap: function(e) {
- preventEventBehaviors(e);
- mainEventHolder.dispatchEvent(new CustomEvent('doubletap', { detail: e }));
- }
- };
- var longTap = {
- touchstart: function(e) {
- longTap.waitForLongTap(e);
- },
- touchmove: function(e) {
- },
- touchend: function(e) {
- if (gestureState.longTapTriggerId) {
- clearTimeout(gestureState.longTapTriggerId);
- gestureState.longTapTriggerId = null;
- }
- },
- isLongTap: function(e) {
- var currentTime = new Date().getTime(),
- tapDuration = currentTime - gestureState.tapStartTime;
- if (tapDuration >= minLongTapDuration && !gestureState.interceptedLongTap) {
- if (distance(gestureState.currentTapStart.x, gestureState.currentTapStart.y, gestureState.currentTapEnd.x, gestureState.currentTapEnd.y) < maxLongTapDistance) {
- gestureState.interceptedLongTap = true;
- return true;
- }
- }
- return false;
- },
- waitForLongTap: function(e) {
- var longTapTrigger = function() {
- if (longTap.isLongTap(e)) {
- mainEventHolder.dispatchEvent(new CustomEvent('longtap', { detail: e }));
- }
- gestureState.longTapTriggerId = null;
- };
- if (!gestureState.longTapTriggerId) {
- gestureState.longTapTriggerId = setTimeout(longTapTrigger, minLongTapDuration);
- }
- }
- };
- var tap = {
- touchstart: function(e) {
- gestureState.tapStartTime = new Date().getTime();
- },
- touchmove: function(e) {
- },
- touchend: function(e) {
- if (tap.isTap(e)) {
- mainEventHolder.dispatchEvent(new CustomEvent('tap', { detail: e }));
- preventEventBehaviors(e);
- }
- },
- isTap: function(e) {
- var currentTime = new Date().getTime(),
- tapDuration = currentTime - gestureState.tapStartTime;
- if (tapDuration <= pressedTapDuration) {
- if (distance(gestureState.currentTapStart.x, gestureState.currentTapStart.y, gestureState.currentTapEnd.x, gestureState.currentTapEnd.y) < maxLongTapDistance) {
- return true;
- }
- }
- return false;
- }
- };
- if (options.pan.enableTouch === true || options.zoom.enableTouch) {
- plot.hooks.bindEvents.push(bindEvents);
- plot.hooks.shutdown.push(shutdown);
- };
- function updatePrevForDoubleTap() {
- gestureState.prevTap = {
- x: gestureState.currentTap.x,
- y: gestureState.currentTap.y
- };
- };
- function updateCurrentForDoubleTap(e) {
- gestureState.currentTap = {
- x: e.touches[0].pageX,
- y: e.touches[0].pageY
- };
- }
- function updateStateForLongTapStart(e) {
- gestureState.tapStartTime = new Date().getTime();
- gestureState.interceptedLongTap = false;
- gestureState.currentTapStart = {
- x: e.touches[0].pageX,
- y: e.touches[0].pageY
- };
- gestureState.currentTapEnd = {
- x: e.touches[0].pageX,
- y: e.touches[0].pageY
- };
- };
- function updateStateForLongTapEnd(e) {
- gestureState.currentTapEnd = {
- x: e.touches[0].pageX,
- y: e.touches[0].pageY
- };
- };
- function isDoubleTap(e) {
- var currentTime = new Date().getTime(),
- intervalBetweenTaps = currentTime - gestureState.prevTapTime;
- if (intervalBetweenTaps >= 0 && intervalBetweenTaps < maxIntervalBetweenTaps) {
- if (distance(gestureState.prevTap.x, gestureState.prevTap.y, gestureState.currentTap.x, gestureState.currentTap.y) < maxDistanceBetweenTaps) {
- e.firstTouch = gestureState.prevTap;
- e.secondTouch = gestureState.currentTap;
- return true;
- }
- }
- gestureState.prevTapTime = currentTime;
- return false;
- }
- function preventEventBehaviors(e) {
- if (!gestureState.isUnsupportedGesture) {
- e.preventDefault();
- if (!plot.getOptions().propagateSupportedGesture) {
- e.stopPropagation();
- }
- }
- }
- function distance(x1, y1, x2, y2) {
- return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
- }
- function noTouchActive(e) {
- return (e.touches && e.touches.length === 0);
- }
- function wasPinchEvent(e) {
- return (gestureState.twoTouches && e.touches.length === 1);
- }
- function updateOnMultipleTouches(e) {
- if (e.touches.length >= 3) {
- gestureState.isUnsupportedGesture = true;
- } else {
- gestureState.isUnsupportedGesture = false;
- }
- }
- function isPinchEvent(e) {
- if (e.touches && e.touches.length >= 2) {
- if (e.touches[0].target === plot.getEventHolder() &&
- e.touches[1].target === plot.getEventHolder()) {
- return true;
- }
- }
- return false;
- }
- }
- $.plot.plugins.push({
- init: init,
- options: options,
- name: 'navigateTouch',
- version: '0.3'
- });
- })(jQuery);
|