alloy-finger.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. /* AlloyFinger v0.1.7
  2. * By dntzhang
  3. * Github: https://github.com/AlloyTeam/AlloyFinger
  4. */
  5. ; (function () {
  6. function getLen(v) {
  7. return Math.sqrt(v.x * v.x + v.y * v.y);
  8. }
  9. function dot(v1, v2) {
  10. return v1.x * v2.x + v1.y * v2.y;
  11. }
  12. function getAngle(v1, v2) {
  13. var mr = getLen(v1) * getLen(v2);
  14. if (mr === 0) return 0;
  15. var r = dot(v1, v2) / mr;
  16. if (r > 1) r = 1;
  17. return Math.acos(r);
  18. }
  19. function cross(v1, v2) {
  20. return v1.x * v2.y - v2.x * v1.y;
  21. }
  22. function getRotateAngle(v1, v2) {
  23. var angle = getAngle(v1, v2);
  24. if (cross(v1, v2) > 0) {
  25. angle *= -1;
  26. }
  27. return angle * 180 / Math.PI;
  28. }
  29. var HandlerAdmin = function(el) {
  30. this.handlers = [];
  31. this.el = el;
  32. };
  33. HandlerAdmin.prototype.add = function(handler) {
  34. this.handlers.push(handler);
  35. }
  36. HandlerAdmin.prototype.del = function(handler) {
  37. if(!handler) this.handlers = [];
  38. for(var i=this.handlers.length; i>=0; i--) {
  39. if(this.handlers[i] === handler) {
  40. this.handlers.splice(i, 1);
  41. }
  42. }
  43. }
  44. HandlerAdmin.prototype.dispatch = function() {
  45. for(var i=0,len=this.handlers.length; i<len; i++) {
  46. var handler = this.handlers[i];
  47. if(typeof handler === 'function') handler.apply(this.el, arguments);
  48. }
  49. }
  50. function wrapFunc(el, handler) {
  51. var handlerAdmin = new HandlerAdmin(el);
  52. handlerAdmin.add(handler);
  53. return handlerAdmin;
  54. }
  55. var AlloyFinger = function (el, option) {
  56. this.element = typeof el == 'string' ? document.querySelector(el) : el;
  57. this.start = this.start.bind(this);
  58. this.move = this.move.bind(this);
  59. this.end = this.end.bind(this);
  60. this.cancel = this.cancel.bind(this);
  61. this.element.addEventListener("touchstart", this.start, false);
  62. this.element.addEventListener("touchmove", this.move, false);
  63. this.element.addEventListener("touchend", this.end, false);
  64. this.element.addEventListener("touchcancel", this.cancel, false);
  65. this.preV = { x: null, y: null };
  66. this.pinchStartLen = null;
  67. this.zoom = 1;
  68. this.isDoubleTap = false;
  69. var noop = function () { };
  70. this.rotate = wrapFunc(this.element, option.rotate || noop);
  71. this.touchStart = wrapFunc(this.element, option.touchStart || noop);
  72. this.multipointStart = wrapFunc(this.element, option.multipointStart || noop);
  73. this.multipointEnd = wrapFunc(this.element, option.multipointEnd || noop);
  74. this.pinch = wrapFunc(this.element, option.pinch || noop);
  75. this.swipe = wrapFunc(this.element, option.swipe || noop);
  76. this.tap = wrapFunc(this.element, option.tap || noop);
  77. this.doubleTap = wrapFunc(this.element, option.doubleTap || noop);
  78. this.longTap = wrapFunc(this.element, option.longTap || noop);
  79. this.singleTap = wrapFunc(this.element, option.singleTap || noop);
  80. this.pressMove = wrapFunc(this.element, option.pressMove || noop);
  81. this.touchMove = wrapFunc(this.element, option.touchMove || noop);
  82. this.touchEnd = wrapFunc(this.element, option.touchEnd || noop);
  83. this.touchCancel = wrapFunc(this.element, option.touchCancel || noop);
  84. this.delta = null;
  85. this.last = null;
  86. this.now = null;
  87. this.tapTimeout = null;
  88. this.singleTapTimeout = null;
  89. this.longTapTimeout = null;
  90. this.swipeTimeout = null;
  91. this.x1 = this.x2 = this.y1 = this.y2 = null;
  92. this.preTapPosition = { x: null, y: null };
  93. };
  94. AlloyFinger.prototype = {
  95. start: function (evt) {
  96. if (!evt.touches) return;
  97. this.now = Date.now();
  98. this.x1 = evt.touches[0].pageX;
  99. this.y1 = evt.touches[0].pageY;
  100. this.delta = this.now - (this.last || this.now);
  101. this.touchStart.dispatch(evt);
  102. if (this.preTapPosition.x !== null) {
  103. this.isDoubleTap = (this.delta > 0 && this.delta <= 250 && Math.abs(this.preTapPosition.x - this.x1) < 30 && Math.abs(this.preTapPosition.y - this.y1) < 30);
  104. }
  105. this.preTapPosition.x = this.x1;
  106. this.preTapPosition.y = this.y1;
  107. this.last = this.now;
  108. var preV = this.preV,
  109. len = evt.touches.length;
  110. if (len > 1) {
  111. this._cancelLongTap();
  112. this._cancelSingleTap();
  113. var v = { x: evt.touches[1].pageX - this.x1, y: evt.touches[1].pageY - this.y1 };
  114. preV.x = v.x;
  115. preV.y = v.y;
  116. this.pinchStartLen = getLen(preV);
  117. this.multipointStart.dispatch(evt);
  118. }
  119. this.longTapTimeout = setTimeout(function () {
  120. this.longTap.dispatch(evt);
  121. }.bind(this), 750);
  122. },
  123. move: function (evt) {
  124. if (!evt.touches) return;
  125. var preV = this.preV,
  126. len = evt.touches.length,
  127. currentX = evt.touches[0].pageX,
  128. currentY = evt.touches[0].pageY;
  129. this.isDoubleTap = false;
  130. if (len > 1) {
  131. var v = { x: evt.touches[1].pageX - currentX, y: evt.touches[1].pageY - currentY };
  132. if (preV.x !== null) {
  133. if (this.pinchStartLen > 0) {
  134. evt.zoom = getLen(v) / this.pinchStartLen;
  135. this.pinch.dispatch(evt);
  136. }
  137. evt.angle = getRotateAngle(v, preV);
  138. this.rotate.dispatch(evt);
  139. }
  140. preV.x = v.x;
  141. preV.y = v.y;
  142. } else {
  143. if (this.x2 !== null) {
  144. evt.deltaX = currentX - this.x2;
  145. evt.deltaY = currentY - this.y2;
  146. } else {
  147. evt.deltaX = 0;
  148. evt.deltaY = 0;
  149. }
  150. this.pressMove.dispatch(evt);
  151. }
  152. this.touchMove.dispatch(evt);
  153. this._cancelLongTap();
  154. this.x2 = currentX;
  155. this.y2 = currentY;
  156. if (len > 1) {
  157. evt.preventDefault();
  158. }
  159. },
  160. end: function (evt) {
  161. if (!evt.changedTouches) return;
  162. this._cancelLongTap();
  163. var self = this;
  164. if (evt.touches.length < 2) {
  165. this.multipointEnd.dispatch(evt);
  166. }
  167. //swipe
  168. if ((this.x2 && Math.abs(this.x1 - this.x2) > 30) ||
  169. (this.y2 && Math.abs(this.y1 - this.y2) > 30)) {
  170. evt.direction = this._swipeDirection(this.x1, this.x2, this.y1, this.y2);
  171. this.swipeTimeout = setTimeout(function () {
  172. self.swipe.dispatch(evt);
  173. }, 0)
  174. } else {
  175. this.tapTimeout = setTimeout(function () {
  176. self.tap.dispatch(evt);
  177. // trigger double tap immediately
  178. if (self.isDoubleTap) {
  179. self.doubleTap.dispatch(evt);
  180. clearTimeout(self.singleTapTimeout);
  181. self.isDoubleTap = false;
  182. }
  183. }, 0)
  184. if (!self.isDoubleTap) {
  185. self.singleTapTimeout = setTimeout(function () {
  186. self.singleTap.dispatch(evt);
  187. }, 250);
  188. }
  189. }
  190. this.touchEnd.dispatch(evt);
  191. this.preV.x = 0;
  192. this.preV.y = 0;
  193. this.zoom = 1;
  194. this.pinchStartLen = null;
  195. this.x1 = this.x2 = this.y1 = this.y2 = null;
  196. },
  197. cancel: function (evt) {
  198. clearTimeout(this.singleTapTimeout);
  199. clearTimeout(this.tapTimeout);
  200. clearTimeout(this.longTapTimeout);
  201. clearTimeout(this.swipeTimeout);
  202. this.touchCancel.dispatch(evt);
  203. },
  204. _cancelLongTap: function () {
  205. clearTimeout(this.longTapTimeout);
  206. },
  207. _cancelSingleTap: function () {
  208. clearTimeout(this.singleTapTimeout);
  209. },
  210. _swipeDirection: function (x1, x2, y1, y2) {
  211. return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
  212. },
  213. on: function(evt, handler) {
  214. if(this[evt]) {
  215. this[evt].add(handler);
  216. }
  217. },
  218. off: function(evt, handler) {
  219. if(this[evt]) {
  220. this[evt].del(handler);
  221. }
  222. },
  223. destroy: function() {
  224. if(this.singleTapTimeout) clearTimeout(this.singleTapTimeout);
  225. if(this.tapTimeout) clearTimeout(this.tapTimeout);
  226. if(this.longTapTimeout) clearTimeout(this.longTapTimeout);
  227. if(this.swipeTimeout) clearTimeout(this.swipeTimeout);
  228. this.element.removeEventListener("touchstart", this.start);
  229. this.element.removeEventListener("touchmove", this.move);
  230. this.element.removeEventListener("touchend", this.end);
  231. this.element.removeEventListener("touchcancel", this.cancel);
  232. this.rotate.del();
  233. this.touchStart.del();
  234. this.multipointStart.del();
  235. this.multipointEnd.del();
  236. this.pinch.del();
  237. this.swipe.del();
  238. this.tap.del();
  239. this.doubleTap.del();
  240. this.longTap.del();
  241. this.singleTap.del();
  242. this.pressMove.del();
  243. this.touchMove.del();
  244. this.touchEnd.del();
  245. this.touchCancel.del();
  246. this.preV = this.pinchStartLen = this.zoom = this.isDoubleTap = this.delta = this.last = this.now = this.tapTimeout = this.singleTapTimeout = this.longTapTimeout = this.swipeTimeout = this.x1 = this.x2 = this.y1 = this.y2 = this.preTapPosition = this.rotate = this.touchStart = this.multipointStart = this.multipointEnd = this.pinch = this.swipe = this.tap = this.doubleTap = this.longTap = this.singleTap = this.pressMove = this.touchMove = this.touchEnd = this.touchCancel = null;
  247. return null;
  248. }
  249. };
  250. if (typeof module !== 'undefined' && typeof exports === 'object') {
  251. module.exports = AlloyFinger;
  252. } else {
  253. window.AlloyFinger = AlloyFinger;
  254. }
  255. })();