Fix HammerJS Pan config bug

This commit is contained in:
Gautier Fauchart
2016-01-10 20:07:34 +00:00
parent c2e753f780
commit f13eab910c
5 changed files with 174 additions and 77 deletions

View File

@ -9,7 +9,8 @@ export class MenuContentGesture extends SlideEdgeGesture {
super(targetEl, util.extend({
direction: (menu.side === 'left' || menu.side === 'right') ? 'x' : 'y',
edge: menu.side,
threshold: menu.threshold || 75
threshold: 0,
maxEdgeStart: menu.maxEdgeStart || 75
}, options));
this.menu = menu;
@ -53,7 +54,7 @@ export class MenuContentGesture extends SlideEdgeGesture {
export class TargetGesture extends MenuContentGesture {
constructor(menu: Menu) {
super(menu, menu.getNativeElement(), {
threshold: 0
maxEdgeStart: 0
});
}
}

View File

@ -96,7 +96,7 @@ import * as gestures from './menu-gestures';
'id',
'side',
'type',
'threshold'
'maxEdgeStart'
],
defaultInputs: {
'side': 'left',

View File

@ -42,7 +42,7 @@ export class Gesture {
}
listen() {
this.hammertime = Hammer(this.element, this._options);
this.hammertime = new Hammer(this.element, this._options);
}
unlisten() {

View File

@ -1,10 +1,10 @@
/*! Hammer.JS - v2.0.4 - 2014-09-28
/*! Hammer.JS - v2.0.6 - 2015-12-23
* http://hammerjs.github.io/
*
* Copyright (c) 2014 Jorik Tangelder;
* Licensed under the MIT license */
* Copyright (c) 2015 Jorik Tangelder;
* Licensed under the license */
var VENDOR_PREFIXES = ['', 'webkit', 'moz', 'MS', 'ms', 'o'];
var VENDOR_PREFIXES = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
var TEST_ELEMENT = document.createElement('div');
var TYPE_FUNCTION = 'function';
@ -69,15 +69,69 @@ function each(obj, iterator, context) {
}
}
/**
* wrap a method with a deprecation warning and stack trace
* @param {Function} method
* @param {String} name
* @param {String} message
* @returns {Function} A new function wrapping the supplied method.
*/
function deprecate(method, name, message) {
var deprecationMessage = 'DEPRECATED METHOD: ' + name + '\n' + message + ' AT \n';
return function() {
var e = new Error('get-stack-trace');
var stack = e && e.stack ? e.stack.replace(/^[^\(]+?[\n$]/gm, '')
.replace(/^\s+at\s+/gm, '')
.replace(/^Object.<anonymous>\s*\(/gm, '{anonymous}()@') : 'Unknown Stack Trace';
var log = window.console && (window.console.warn || window.console.log);
if (log) {
log.call(window.console, deprecationMessage, stack);
}
return method.apply(this, arguments);
};
}
/**
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} target
* @param {...Object} objects_to_assign
* @returns {Object} target
*/
var assign;
if (typeof Object.assign !== 'function') {
assign = function assign(target) {
if (target === undefined || target === null) {
throw new TypeError('Cannot convert undefined or null to object');
}
var output = Object(target);
for (var index = 1; index < arguments.length; index++) {
var source = arguments[index];
if (source !== undefined && source !== null) {
for (var nextKey in source) {
if (source.hasOwnProperty(nextKey)) {
output[nextKey] = source[nextKey];
}
}
}
}
return output;
};
} else {
assign = Object.assign;
}
/**
* extend object.
* means that properties in dest will be overwritten by the ones in src.
* @param {Object} dest
* @param {Object} src
* @param {Boolean} [merge]
* @param {Boolean=false} [merge]
* @returns {Object} dest
*/
function extend(dest, src, merge) {
var extend = deprecate(function extend(dest, src, merge) {
var keys = Object.keys(src);
var i = 0;
while (i < keys.length) {
@ -87,7 +141,7 @@ function extend(dest, src, merge) {
i++;
}
return dest;
}
}, 'extend', 'Use `assign`.');
/**
* merge the values from src in the dest.
@ -96,9 +150,9 @@ function extend(dest, src, merge) {
* @param {Object} src
* @returns {Object} dest
*/
function merge(dest, src) {
var merge = deprecate(function merge(dest, src) {
return extend(dest, src, true);
}
}, 'merge', 'Use `assign`.');
/**
* simple class inheritance
@ -115,7 +169,7 @@ function inherit(child, base, properties) {
childP._super = baseP;
if (properties) {
extend(childP, properties);
assign(childP, properties);
}
}
@ -163,7 +217,6 @@ function ifUndefined(val1, val2) {
*/
function addEventListeners(target, types, handler) {
each(splitStr(types), function(type) {
//console.debug('hammer addEventListener', type, target.tagName);
target.addEventListener(type, handler, false);
});
}
@ -176,7 +229,6 @@ function addEventListeners(target, types, handler) {
*/
function removeEventListeners(target, types, handler) {
each(splitStr(types), function(type) {
//console.debug('hammer removeEventListener', type, target.tagName);
target.removeEventListener(type, handler, false);
});
}
@ -320,8 +372,8 @@ function uniqueId() {
* @returns {DocumentView|Window}
*/
function getWindowForElement(element) {
var doc = element.ownerDocument;
return (doc.defaultView || doc.parentWindow);
var doc = element.ownerDocument || element;
return (doc.defaultView || doc.parentWindow || window);
}
var MOBILE_REGEX = /mobile|tablet|ip(ad|hone|od)|android/i;
@ -392,7 +444,6 @@ Input.prototype = {
* bind the events
*/
init: function() {
//console.debug('hammer Input init')
this.evEl && addEventListeners(this.element, this.evEl, this.domHandler);
this.evTarget && addEventListeners(this.target, this.evTarget, this.domHandler);
this.evWin && addEventListeners(getWindowForElement(this.element), this.evWin, this.domHandler);
@ -501,9 +552,17 @@ function computeInputData(manager, input) {
computeDeltaXY(session, input);
input.offsetDirection = getDirection(input.deltaX, input.deltaY);
var overallVelocity = getVelocity(input.deltaTime, input.deltaX, input.deltaY);
input.overallVelocityX = overallVelocity.x;
input.overallVelocityY = overallVelocity.y;
input.overallVelocity = (abs(overallVelocity.x) > abs(overallVelocity.y)) ? overallVelocity.x : overallVelocity.y;
input.scale = firstMultiple ? getScale(firstMultiple.pointers, pointers) : 1;
input.rotation = firstMultiple ? getRotation(firstMultiple.pointers, pointers) : 0;
input.maxPointers = !session.prevInput ? input.pointers.length : ((input.pointers.length >
session.prevInput.maxPointers) ? input.pointers.length : session.prevInput.maxPointers);
computeIntervalInputData(session, input);
// find the correct target
@ -547,8 +606,8 @@ function computeIntervalInputData(session, input) {
velocity, velocityX, velocityY, direction;
if (input.eventType != INPUT_CANCEL && (deltaTime > COMPUTE_INTERVAL || last.velocity === undefined)) {
var deltaX = last.deltaX - input.deltaX;
var deltaY = last.deltaY - input.deltaY;
var deltaX = input.deltaX - last.deltaX;
var deltaY = input.deltaY - last.deltaY;
var v = getVelocity(deltaTime, deltaX, deltaY);
velocityX = v.x;
@ -653,9 +712,9 @@ function getDirection(x, y) {
}
if (abs(x) >= abs(y)) {
return x > 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
return x < 0 ? DIRECTION_LEFT : DIRECTION_RIGHT;
}
return y > 0 ? DIRECTION_UP : DIRECTION_DOWN;
return y < 0 ? DIRECTION_UP : DIRECTION_DOWN;
}
/**
@ -698,7 +757,7 @@ function getAngle(p1, p2, props) {
* @return {Number} rotation
*/
function getRotation(start, end) {
return getAngle(end[1], end[0], PROPS_CLIENT_XY) - getAngle(start[1], start[0], PROPS_CLIENT_XY);
return getAngle(end[1], end[0], PROPS_CLIENT_XY) + getAngle(start[1], start[0], PROPS_CLIENT_XY);
}
/**
@ -791,7 +850,7 @@ var POINTER_ELEMENT_EVENTS = 'pointerdown';
var POINTER_WINDOW_EVENTS = 'pointermove pointerup pointercancel';
// IE10 has prefixed support, and case-sensitive
if (window.MSPointerEvent) {
if (window.MSPointerEvent && !window.PointerEvent) {
POINTER_ELEMENT_EVENTS = 'MSPointerDown';
POINTER_WINDOW_EVENTS = 'MSPointerMove MSPointerUp MSPointerCancel';
}
@ -1115,7 +1174,7 @@ TouchAction.prototype = {
value = this.compute();
}
if (NATIVE_TOUCH_ACTION) {
if (NATIVE_TOUCH_ACTION && this.manager.element.style) {
this.manager.element.style[PREFIXED_TOUCH_ACTION] = value;
}
this.actions = value.toLowerCase().trim();
@ -1166,6 +1225,23 @@ TouchAction.prototype = {
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
if (hasNone) {
//do not prevent defaults if this is a tap gesture
var isTapPointer = input.pointers.length === 1;
var isTapMovement = input.distance < 2;
var isTapTouchTime = input.deltaTime < 250;
if (isTapPointer && isTapMovement && isTapTouchTime) {
return;
}
}
if (hasPanX && hasPanY) {
// `pan-x pan-y` means browser handles all scrolling/panning, do not prevent
return;
}
if (hasNone ||
(hasPanY && direction & DIRECTION_HORIZONTAL) ||
(hasPanX && direction & DIRECTION_VERTICAL)) {
@ -1197,9 +1273,12 @@ function cleanTouchActions(actions) {
var hasPanX = inStr(actions, TOUCH_ACTION_PAN_X);
var hasPanY = inStr(actions, TOUCH_ACTION_PAN_Y);
// pan-x and pan-y can be combined
// if both pan-x and pan-y are set (different recognizers
// for different directions, e.g. horizontal pan but vertical swipe?)
// we need none (as otherwise with pan-x pan-y combined none of these
// recognizers will work, since the browser would handle all panning
if (hasPanX && hasPanY) {
return TOUCH_ACTION_PAN_X + ' ' + TOUCH_ACTION_PAN_Y;
return TOUCH_ACTION_NONE;
}
// pan-x OR pan-y
@ -1257,10 +1336,11 @@ var STATE_FAILED = 32;
* @param {Object} options
*/
function Recognizer(options) {
this.options = assign({}, this.defaults, options || {});
this.id = uniqueId();
this.manager = null;
this.options = merge(options || {}, this.defaults);
// default is enable true
this.options.enable = ifUndefined(this.options.enable, true);
@ -1284,7 +1364,7 @@ Recognizer.prototype = {
* @return {Recognizer}
*/
set: function(options) {
extend(this.options, options);
assign(this.options, options);
// also update the touchAction, in case something changed about the directions/enabled state
this.manager && this.manager.touchAction.update();
@ -1388,20 +1468,24 @@ Recognizer.prototype = {
var self = this;
var state = this.state;
function emit(withState) {
self.manager.emit(self.options.event + (withState ? stateStr(state) : ''), input);
function emit(event) {
self.manager.emit(event, input);
}
// 'panstart' and 'panmove'
if (state < STATE_ENDED) {
emit(true);
emit(self.options.event + stateStr(state));
}
emit(); // simple 'eventName' events
emit(self.options.event); // simple 'eventName' events
if (input.additionalEvent) { // additional event(panleft, panright, pinchin, pinchout...)
emit(input.additionalEvent);
}
// panend and pancancel
if (state >= STATE_ENDED) {
emit(true);
emit(self.options.event + stateStr(state));
}
},
@ -1441,7 +1525,7 @@ Recognizer.prototype = {
recognize: function(inputData) {
// make a new copy of the inputData
// so we can change the inputData without messing up the other recognizers
var inputDataClone = extend({}, inputData);
var inputDataClone = assign({}, inputData);
// is is enabled and allow recognizing?
if (!boolOrFn(this.options.enable, [this, inputDataClone])) {
@ -1666,14 +1750,15 @@ inherit(PanRecognizer, AttrRecognizer, {
},
emit: function(input) {
this.pX = input.deltaX;
this.pY = input.deltaY;
var direction = directionStr(input.direction);
if (direction) {
this.manager.emit(this.options.event + direction, input);
}
if (direction) {
input.additionalEvent = this.options.event + direction;
}
this._super.emit.call(this, input);
}
});
@ -1709,11 +1794,11 @@ inherit(PinchRecognizer, AttrRecognizer, {
},
emit: function(input) {
this._super.emit.call(this, input);
if (input.scale !== 1) {
var inOut = input.scale < 1 ? 'in' : 'out';
this.manager.emit(this.options.event + inOut, input);
input.additionalEvent = this.options.event + inOut;
}
this._super.emit.call(this, input);
}
});
@ -1738,8 +1823,8 @@ inherit(PressRecognizer, Recognizer, {
defaults: {
event: 'press',
pointers: 1,
time: 500, // minimal time of the pointer to be pressed
threshold: 5 // a minimal movement is ok, but keep it low
time: 251, // minimal time of the pointer to be pressed
threshold: 9 // a minimal movement is ok, but keep it low
},
getTouchAction: function() {
@ -1837,7 +1922,7 @@ inherit(SwipeRecognizer, AttrRecognizer, {
defaults: {
event: 'swipe',
threshold: 10,
velocity: 0.65,
velocity: 0.3,
direction: DIRECTION_HORIZONTAL | DIRECTION_VERTICAL,
pointers: 1
},
@ -1851,21 +1936,22 @@ inherit(SwipeRecognizer, AttrRecognizer, {
var velocity;
if (direction & (DIRECTION_HORIZONTAL | DIRECTION_VERTICAL)) {
velocity = input.velocity;
velocity = input.overallVelocity;
} else if (direction & DIRECTION_HORIZONTAL) {
velocity = input.velocityX;
velocity = input.overallVelocityX;
} else if (direction & DIRECTION_VERTICAL) {
velocity = input.velocityY;
velocity = input.overallVelocityY;
}
return this._super.attrTest.call(this, input) &&
direction & input.direction &&
direction & input.offsetDirection &&
input.distance > this.options.threshold &&
input.maxPointers == this.options.pointers &&
abs(velocity) > this.options.velocity && input.eventType & INPUT_END;
},
emit: function(input) {
var direction = directionStr(input.direction);
var direction = directionStr(input.offsetDirection);
if (direction) {
this.manager.emit(this.options.event + direction, input);
}
@ -1908,7 +1994,7 @@ inherit(TapRecognizer, Recognizer, {
taps: 1,
interval: 300, // max time between the multi-tap taps
time: 250, // max time of the pointer to be down (like finger on the screen)
threshold: 2, // a minimal movement is ok, but keep it low
threshold: 9, // a minimal movement is ok, but keep it low
posThreshold: 10 // a multi-tap can be a bit off the initial position
},
@ -1982,7 +2068,7 @@ inherit(TapRecognizer, Recognizer, {
},
emit: function() {
if (this.state == STATE_RECOGNIZED ) {
if (this.state == STATE_RECOGNIZED) {
this._input.tapCount = this.count;
this.manager.emit(this.options.event, this._input);
}
@ -1990,7 +2076,7 @@ inherit(TapRecognizer, Recognizer, {
});
/**
* Simple way to create an manager with a default set of recognizers.
* Simple way to create a manager with a default set of recognizers.
* @param {HTMLElement} element
* @param {Object} [options]
* @constructor
@ -2004,7 +2090,7 @@ function Hammer(element, options) {
/**
* @const {string}
*/
Hammer.VERSION = '2.0.4';
Hammer.VERSION = '2.0.6';
/**
* default settings
@ -2056,12 +2142,12 @@ Hammer.defaults = {
*/
preset: [
// RecognizerClass, options, [recognizeWith, ...], [requireFailure, ...]
[RotateRecognizer, { enable: false }],
[PinchRecognizer, { enable: false }, ['rotate']],
[SwipeRecognizer,{ direction: DIRECTION_HORIZONTAL }],
[PanRecognizer, { direction: DIRECTION_HORIZONTAL }, ['swipe']],
[RotateRecognizer, {enable: false}],
[PinchRecognizer, {enable: false}, ['rotate']],
[SwipeRecognizer, {direction: DIRECTION_HORIZONTAL}],
[PanRecognizer, {direction: DIRECTION_HORIZONTAL}, ['swipe']],
[TapRecognizer],
[TapRecognizer, { event: 'doubletap', taps: 2 }, ['tap']],
[TapRecognizer, {event: 'doubletap', taps: 2}, ['tap']],
[PressRecognizer]
],
@ -2128,9 +2214,8 @@ var FORCED_STOP = 2;
* @constructor
*/
function Manager(element, options) {
options = options || {};
this.options = assign({}, Hammer.defaults, options || {});
this.options = merge(options, Hammer.defaults);
this.options.inputTarget = this.options.inputTarget || element;
this.handlers = {};
@ -2143,7 +2228,7 @@ function Manager(element, options) {
toggleCssProps(this, true);
each(options.recognizers, function(item) {
each(this.options.recognizers, function(item) {
var recognizer = this.add(new (item[0])(item[1]));
item[2] && recognizer.recognizeWith(item[2]);
item[3] && recognizer.requireFailure(item[3]);
@ -2157,7 +2242,7 @@ Manager.prototype = {
* @returns {Manager}
*/
set: function(options) {
extend(this.options, options);
assign(this.options, options);
// Options that need a little more setup
if (options.touchAction) {
@ -2291,11 +2376,19 @@ Manager.prototype = {
return this;
}
var recognizers = this.recognizers;
recognizer = this.get(recognizer);
recognizers.splice(inArray(recognizers, recognizer), 1);
this.touchAction.update();
// let's make sure this recognizer exists
if (recognizer) {
var recognizers = this.recognizers;
var index = inArray(recognizers, recognizer);
if (index !== -1) {
recognizers.splice(index, 1);
this.touchAction.update();
}
}
return this;
},
@ -2326,7 +2419,7 @@ Manager.prototype = {
if (!handler) {
delete handlers[event];
} else {
handlers[event].splice(inArray(handlers[event], handler), 1);
handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
}
});
return this;
@ -2382,6 +2475,9 @@ Manager.prototype = {
*/
function toggleCssProps(manager, add) {
var element = manager.element;
if (!element.style) {
return;
}
each(manager.options.cssProps, function(value, name) {
element.style[prefixed(element.style, name)] = add ? value : '';
});
@ -2399,7 +2495,7 @@ function triggerDomEvent(event, data) {
data.target.dispatchEvent(gestureEvent);
}
extend(Hammer, {
assign(Hammer, {
INPUT_START: INPUT_START,
INPUT_MOVE: INPUT_MOVE,
INPUT_END: INPUT_END,
@ -2446,12 +2542,12 @@ extend(Hammer, {
each: each,
merge: merge,
extend: extend,
assign: assign,
inherit: inherit,
bindFn: bindFn,
prefixed: prefixed
});
// attach to window for angular2 gesture listeners
window.Hammer = Hammer;
export {Hammer};

View File

@ -7,12 +7,12 @@ export class SlideEdgeGesture extends SlideGesture {
constructor(element: Element, opts: Object = {}) {
defaults(opts, {
edge: 'left',
threshold: 50
maxEdgeStart: 50
});
super(element, opts);
// Can check corners through use of eg 'left top'
this.edges = opts.edge.split(' ');
this.threshold = opts.threshold;
this.maxEdgeStart = opts.maxEdgeStart;
}
canStart(ev) {
@ -31,10 +31,10 @@ export class SlideEdgeGesture extends SlideGesture {
_checkEdge(edge, pos) {
switch (edge) {
case 'left': return pos.x <= this._d.left + this.threshold;
case 'right': return pos.x >= this._d.width - this.threshold;
case 'top': return pos.y <= this._d.top + this.threshold;
case 'bottom': return pos.y >= this._d.height - this.threshold;
case 'left': return pos.x <= this._d.left + this.maxEdgeStart;
case 'right': return pos.x >= this._d.width - this.maxEdgeStart;
case 'top': return pos.y <= this._d.top + this.maxEdgeStart;
case 'bottom': return pos.y >= this._d.height - this.maxEdgeStart;
}
}