unipointer.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. /*!
  2. * Unipointer v2.3.0
  3. * base class for doing one thing with pointer event
  4. * MIT license
  5. */
  6. /*jshint browser: true, undef: true, unused: true, strict: true */
  7. ( function( window, factory ) {
  8. // universal module definition
  9. /* jshint strict: false */ /*global define, module, require */
  10. if ( typeof define == 'function' && define.amd ) {
  11. // AMD
  12. define( [
  13. 'ev-emitter/ev-emitter'
  14. ], function( EvEmitter ) {
  15. return factory( window, EvEmitter );
  16. });
  17. } else if ( typeof module == 'object' && module.exports ) {
  18. // CommonJS
  19. module.exports = factory(
  20. window,
  21. require('ev-emitter')
  22. );
  23. } else {
  24. // browser global
  25. window.Unipointer = factory(
  26. window,
  27. window.EvEmitter
  28. );
  29. }
  30. }( window, function factory( window, EvEmitter ) {
  31. 'use strict';
  32. function noop() {}
  33. function Unipointer() {}
  34. // inherit EvEmitter
  35. var proto = Unipointer.prototype = Object.create( EvEmitter.prototype );
  36. proto.bindStartEvent = function( elem ) {
  37. this._bindStartEvent( elem, true );
  38. };
  39. proto.unbindStartEvent = function( elem ) {
  40. this._bindStartEvent( elem, false );
  41. };
  42. /**
  43. * Add or remove start event
  44. * @param {Boolean} isAdd - remove if falsey
  45. */
  46. proto._bindStartEvent = function( elem, isAdd ) {
  47. // munge isAdd, default to true
  48. isAdd = isAdd === undefined ? true : isAdd;
  49. var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
  50. // default to mouse events
  51. var startEvent = 'mousedown';
  52. if ( window.PointerEvent ) {
  53. // Pointer Events
  54. startEvent = 'pointerdown';
  55. } else if ( 'ontouchstart' in window ) {
  56. // Touch Events. iOS Safari
  57. startEvent = 'touchstart';
  58. }
  59. elem[ bindMethod ]( startEvent, this );
  60. };
  61. // trigger handler methods for events
  62. proto.handleEvent = function( event ) {
  63. var method = 'on' + event.type;
  64. if ( this[ method ] ) {
  65. this[ method ]( event );
  66. }
  67. };
  68. // returns the touch that we're keeping track of
  69. proto.getTouch = function( touches ) {
  70. for ( var i=0; i < touches.length; i++ ) {
  71. var touch = touches[i];
  72. if ( touch.identifier == this.pointerIdentifier ) {
  73. return touch;
  74. }
  75. }
  76. };
  77. // ----- start event ----- //
  78. proto.onmousedown = function( event ) {
  79. // dismiss clicks from right or middle buttons
  80. var button = event.button;
  81. if ( button && ( button !== 0 && button !== 1 ) ) {
  82. return;
  83. }
  84. this._pointerDown( event, event );
  85. };
  86. proto.ontouchstart = function( event ) {
  87. this._pointerDown( event, event.changedTouches[0] );
  88. };
  89. proto.onpointerdown = function( event ) {
  90. this._pointerDown( event, event );
  91. };
  92. /**
  93. * pointer start
  94. * @param {Event} event
  95. * @param {Event or Touch} pointer
  96. */
  97. proto._pointerDown = function( event, pointer ) {
  98. // dismiss right click and other pointers
  99. // button = 0 is okay, 1-4 not
  100. if ( event.button || this.isPointerDown ) {
  101. return;
  102. }
  103. this.isPointerDown = true;
  104. // save pointer identifier to match up touch events
  105. this.pointerIdentifier = pointer.pointerId !== undefined ?
  106. // pointerId for pointer events, touch.indentifier for touch events
  107. pointer.pointerId : pointer.identifier;
  108. this.pointerDown( event, pointer );
  109. };
  110. proto.pointerDown = function( event, pointer ) {
  111. this._bindPostStartEvents( event );
  112. this.emitEvent( 'pointerDown', [ event, pointer ] );
  113. };
  114. // hash of events to be bound after start event
  115. var postStartEvents = {
  116. mousedown: [ 'mousemove', 'mouseup' ],
  117. touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
  118. pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
  119. };
  120. proto._bindPostStartEvents = function( event ) {
  121. if ( !event ) {
  122. return;
  123. }
  124. // get proper events to match start event
  125. var events = postStartEvents[ event.type ];
  126. // bind events to node
  127. events.forEach( function( eventName ) {
  128. window.addEventListener( eventName, this );
  129. }, this );
  130. // save these arguments
  131. this._boundPointerEvents = events;
  132. };
  133. proto._unbindPostStartEvents = function() {
  134. // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
  135. if ( !this._boundPointerEvents ) {
  136. return;
  137. }
  138. this._boundPointerEvents.forEach( function( eventName ) {
  139. window.removeEventListener( eventName, this );
  140. }, this );
  141. delete this._boundPointerEvents;
  142. };
  143. // ----- move event ----- //
  144. proto.onmousemove = function( event ) {
  145. this._pointerMove( event, event );
  146. };
  147. proto.onpointermove = function( event ) {
  148. if ( event.pointerId == this.pointerIdentifier ) {
  149. this._pointerMove( event, event );
  150. }
  151. };
  152. proto.ontouchmove = function( event ) {
  153. var touch = this.getTouch( event.changedTouches );
  154. if ( touch ) {
  155. this._pointerMove( event, touch );
  156. }
  157. };
  158. /**
  159. * pointer move
  160. * @param {Event} event
  161. * @param {Event or Touch} pointer
  162. * @private
  163. */
  164. proto._pointerMove = function( event, pointer ) {
  165. this.pointerMove( event, pointer );
  166. };
  167. // public
  168. proto.pointerMove = function( event, pointer ) {
  169. this.emitEvent( 'pointerMove', [ event, pointer ] );
  170. };
  171. // ----- end event ----- //
  172. proto.onmouseup = function( event ) {
  173. this._pointerUp( event, event );
  174. };
  175. proto.onpointerup = function( event ) {
  176. if ( event.pointerId == this.pointerIdentifier ) {
  177. this._pointerUp( event, event );
  178. }
  179. };
  180. proto.ontouchend = function( event ) {
  181. var touch = this.getTouch( event.changedTouches );
  182. if ( touch ) {
  183. this._pointerUp( event, touch );
  184. }
  185. };
  186. /**
  187. * pointer up
  188. * @param {Event} event
  189. * @param {Event or Touch} pointer
  190. * @private
  191. */
  192. proto._pointerUp = function( event, pointer ) {
  193. this._pointerDone();
  194. this.pointerUp( event, pointer );
  195. };
  196. // public
  197. proto.pointerUp = function( event, pointer ) {
  198. this.emitEvent( 'pointerUp', [ event, pointer ] );
  199. };
  200. // ----- pointer done ----- //
  201. // triggered on pointer up & pointer cancel
  202. proto._pointerDone = function() {
  203. this._pointerReset();
  204. this._unbindPostStartEvents();
  205. this.pointerDone();
  206. };
  207. proto._pointerReset = function() {
  208. // reset properties
  209. this.isPointerDown = false;
  210. delete this.pointerIdentifier;
  211. };
  212. proto.pointerDone = noop;
  213. // ----- pointer cancel ----- //
  214. proto.onpointercancel = function( event ) {
  215. if ( event.pointerId == this.pointerIdentifier ) {
  216. this._pointerCancel( event, event );
  217. }
  218. };
  219. proto.ontouchcancel = function( event ) {
  220. var touch = this.getTouch( event.changedTouches );
  221. if ( touch ) {
  222. this._pointerCancel( event, touch );
  223. }
  224. };
  225. /**
  226. * pointer cancel
  227. * @param {Event} event
  228. * @param {Event or Touch} pointer
  229. * @private
  230. */
  231. proto._pointerCancel = function( event, pointer ) {
  232. this._pointerDone();
  233. this.pointerCancel( event, pointer );
  234. };
  235. // public
  236. proto.pointerCancel = function( event, pointer ) {
  237. this.emitEvent( 'pointerCancel', [ event, pointer ] );
  238. };
  239. // ----- ----- //
  240. // utility function for getting x/y coords from event
  241. Unipointer.getPointerPoint = function( pointer ) {
  242. return {
  243. x: pointer.pageX,
  244. y: pointer.pageY
  245. };
  246. };
  247. // ----- ----- //
  248. return Unipointer;
  249. }));