pinch-slider
Version:
a slider for images which can be zoomed or pinched
249 lines (219 loc) • 8.69 kB
JavaScript
/* AlloyFinger v0.1.3
* By dntzhang
* Github: https://github.com/AlloyTeam/AlloyFinger
*/
; (function () {
function getLen(v) {
return Math.sqrt(v.x * v.x + v.y * v.y);
}
function dot(v1, v2) {
return v1.x * v2.x + v1.y * v2.y;
}
function getAngle(v1, v2) {
var mr = getLen(v1) * getLen(v2);
if (mr === 0) return 0;
var r = dot(v1, v2) / mr;
if (r > 1) r = 1;
return Math.acos(r);
}
function cross(v1, v2) {
return v1.x * v2.y - v2.x * v1.y;
}
function getRotateAngle(v1, v2) {
var angle = getAngle(v1, v2);
if (cross(v1, v2) > 0) {
angle *= -1;
}
return angle * 180 / Math.PI;
}
var HandlerAdmin = function(el) {
this.handlers = [];
this.el = el;
};
HandlerAdmin.prototype.add = function(handler) {
this.handlers.push(handler);
}
HandlerAdmin.prototype.del = function(handler) {
for(var i=this.handlers.length; i>=0; i--) {
if(this.handlers[i] === handler) {
this.handlers.splice(i, 1);
}
}
}
HandlerAdmin.prototype.dispatch = function() {
for(var i=0,len=this.handlers.length; i<len; i++) {
this.handlers[i].apply(this.el, arguments);
}
}
function wrapFunc(el, handler) {
var handlerAdmin = new HandlerAdmin(el);
handlerAdmin.add(handler);
return handlerAdmin;
}
var AlloyFinger = function (el, option) {
this.element = typeof el == 'string' ? document.querySelector(el) : el;
this.element.addEventListener("touchstart", this.start.bind(this), false);
this.element.addEventListener("touchmove", this.move.bind(this), false);
this.element.addEventListener("touchend", this.end.bind(this), false);
this.element.addEventListener("touchcancel", this.cancel.bind(this), false);
this.preV = { x: null, y: null };
this.pinchStartLen = null;
this.scale = 1;
this.isDoubleTap = false;
var noop = function () { };
this.rotate = wrapFunc(this.element, option.rotate || noop);
this.touchStart = wrapFunc(this.element, option.touchStart || noop);
this.multipointStart = wrapFunc(this.element, option.multipointStart || noop);
this.multipointEnd = wrapFunc(this.element, option.multipointEnd || noop);
this.pinch = wrapFunc(this.element, option.pinch || noop);
this.swipe = wrapFunc(this.element, option.swipe || noop);
this.tap = wrapFunc(this.element, option.tap || noop);
this.doubleTap = wrapFunc(this.element, option.doubleTap || noop);
this.longTap = wrapFunc(this.element, option.longTap || noop);
this.singleTap = wrapFunc(this.element, option.singleTap || noop);
this.pressMove = wrapFunc(this.element, option.pressMove || noop);
this.touchMove = wrapFunc(this.element, option.touchMove || noop);
this.touchEnd = wrapFunc(this.element, option.touchEnd || noop);
this.touchCancel = wrapFunc(this.element, option.touchCancel || noop);
this.delta = null;
this.last = null;
this.now = null;
this.tapTimeout = null;
this.touchTimeout = null;
this.longTapTimeout = null;
this.swipeTimeout = null;
this.x1 = this.x2 = this.y1 = this.y2 = null;
this.preTapPosition = { x: null, y: null };
};
AlloyFinger.prototype = {
start: function (evt) {
if (!evt.touches) return;
this.now = Date.now();
this.x1 = evt.touches[0].pageX;
this.y1 = evt.touches[0].pageY;
this.delta = this.now - (this.last || this.now);
this.touchStart.dispatch(evt);
if (this.preTapPosition.x !== null) {
this.isDoubleTap = (this.delta > 0 && this.delta <= 250 && Math.abs(this.preTapPosition.x - this.x1) < 30 && Math.abs(this.preTapPosition.y - this.y1) < 30);
}
this.preTapPosition.x = this.x1;
this.preTapPosition.y = this.y1;
this.last = this.now;
var preV = this.preV,
len = evt.touches.length;
if (len > 1) {
this._cancelLongTap();
var v = { x: evt.touches[1].pageX - this.x1, y: evt.touches[1].pageY - this.y1 };
preV.x = v.x;
preV.y = v.y;
this.pinchStartLen = getLen(preV);
this.multipointStart.dispatch(evt);
}
this.longTapTimeout = setTimeout(function () {
this.longTap.dispatch(evt);
}.bind(this), 750);
},
move: function (evt) {
if (!evt.touches) return;
var preV = this.preV,
len = evt.touches.length,
currentX = evt.touches[0].pageX,
currentY = evt.touches[0].pageY;
this.isDoubleTap = false;
if (len > 1) {
var v = { x: evt.touches[1].pageX - currentX, y: evt.touches[1].pageY - currentY };
if (preV.x !== null) {
if (this.pinchStartLen > 0) {
evt.scale = getLen(v) / this.pinchStartLen;
this.pinch.dispatch(evt);
}
evt.angle = getRotateAngle(v, preV);
this.rotate.dispatch(evt);
}
preV.x = v.x;
preV.y = v.y;
} else {
if (this.x2 !== null) {
evt.deltaX = currentX - this.x2;
evt.deltaY = currentY - this.y2;
} else {
evt.deltaX = 0;
evt.deltaY = 0;
}
this.pressMove.dispatch(evt);
}
this.touchMove.dispatch(evt);
this._cancelLongTap();
this.x2 = currentX;
this.y2 = currentY;
if (evt.touches.length > 1) {
this._cancelLongTap();
evt.preventDefault();
}
},
end: function (evt) {
if (!evt.changedTouches) return;
this._cancelLongTap();
var self = this;
if (evt.touches.length < 2) {
this.multipointEnd.dispatch(evt);
}
this.touchEnd.dispatch(evt);
//swipe
if ((this.x2 && Math.abs(this.x1 - this.x2) > 30) ||
(this.y2 && Math.abs(this.preV.y - this.y2) > 30)) {
evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2);
this.swipeTimeout = setTimeout(function () {
self.swipe.dispatch(evt);
}, 0)
} else {
this.tapTimeout = setTimeout(function () {
self.tap.dispatch(evt);
// trigger double tap immediately
if (self.isDoubleTap) {
self.doubleTap.dispatch(evt);
clearTimeout(self.touchTimeout);
self.isDoubleTap = false;
} else {
self.touchTimeout = setTimeout(function () {
self.singleTap.dispatch(evt);
}, 250);
}
}, 0)
}
this.preV.x = 0;
this.preV.y = 0;
this.scale = 1;
this.pinchStartLen = null;
this.x1 = this.x2 = this.y1 = this.y2 = null;
},
cancel: function (evt) {
clearTimeout(this.touchTimeout);
clearTimeout(this.tapTimeout);
clearTimeout(this.longTapTimeout);
clearTimeout(this.swipeTimeout);
this.touchCancel.dispatch(evt);
},
_cancelLongTap: function () {
clearTimeout(this.longTapTimeout);
},
_swipeDirection: function (x1, x2, y1, y2) {
return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
},
on: function(evt, handler) {
if(this[evt]) {
this[evt].add(handler);
}
},
off: function(evt, handler) {
if(this[evt]) {
this[evt].del(handler);
}
}
};
if (typeof module !== 'undefined' && typeof exports === 'object') {
module.exports = AlloyFinger;
} else {
window.AlloyFinger = AlloyFinger;
}
})();