TouchInteractionSupport.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. define("dojox/geo/openlayers/TouchInteractionSupport", ["dojo/_base/kernel",
  2. "dojo/_base/declare",
  3. "dojo/_base/connect",
  4. "dojo/_base/html",
  5. "dojo/_base/lang",
  6. "dojo/_base/event",
  7. "dojo/_base/window"], function(dojo, declare, connect, html, lang, event, window){
  8. return declare("dojox.geo.openlayers.TouchInteractionSupport", null, {
  9. // summary:
  10. // class to handle touch interactions on a OpenLayers.Map widget
  11. // tags:
  12. // private
  13. _map : null,
  14. _centerTouchLocation : null,
  15. _touchMoveListener : null,
  16. _touchEndListener : null,
  17. _initialFingerSpacing : null,
  18. _initialScale : null,
  19. _tapCount : null,
  20. _tapThreshold : null,
  21. _lastTap : null,
  22. constructor : function(/* OpenLayers.Map */map){
  23. // summary:
  24. // Constructs a new TouchInteractionSupport instance
  25. // map: OpenLayers.Map
  26. // the Map widget this class provides touch navigation for.
  27. this._map = map;
  28. this._centerTouchLocation = new OpenLayers.LonLat(0, 0);
  29. var div = this._map.div;
  30. // install touch listeners
  31. connect.connect(div, "touchstart", this, this._touchStartHandler);
  32. connect.connect(div, "touchmove", this, this._touchMoveHandler);
  33. connect.connect(div, "touchend", this, this._touchEndHandler);
  34. this._tapCount = 0;
  35. this._lastTap = {
  36. x : 0,
  37. y : 0
  38. };
  39. this._tapThreshold = 100; // square distance in pixels
  40. },
  41. _getTouchBarycenter : function(touchEvent){
  42. // summary:
  43. // returns the midpoint of the two first fingers (or the first finger location if only one)
  44. // touchEvent: Event
  45. // a touch event
  46. // returns: dojox.gfx.Point
  47. // the midpoint
  48. // tags:
  49. // private
  50. var touches = touchEvent.touches;
  51. var firstTouch = touches[0];
  52. var secondTouch = null;
  53. if (touches.length > 1) {
  54. secondTouch = touches[1];
  55. } else {
  56. secondTouch = touches[0];
  57. }
  58. var marginBox = html.marginBox(this._map.div);
  59. var middleX = (firstTouch.pageX + secondTouch.pageX) / 2.0 - marginBox.l;
  60. var middleY = (firstTouch.pageY + secondTouch.pageY) / 2.0 - marginBox.t;
  61. return {
  62. x : middleX,
  63. y : middleY
  64. };
  65. },
  66. _getFingerSpacing : function(touchEvent){
  67. // summary:
  68. // computes the distance between the first two fingers
  69. // touchEvent: Event
  70. // a touch event
  71. // returns: float
  72. // a distance. -1 if less that 2 fingers
  73. // tags:
  74. // private
  75. var touches = touchEvent.touches;
  76. var spacing = -1;
  77. if (touches.length >= 2) {
  78. var dx = (touches[1].pageX - touches[0].pageX);
  79. var dy = (touches[1].pageY - touches[0].pageY);
  80. spacing = Math.sqrt(dx * dx + dy * dy);
  81. }
  82. return spacing;
  83. },
  84. _isDoubleTap : function(touchEvent){
  85. // summary:
  86. // checks whether the specified touchStart event is a double tap
  87. // (i.e. follows closely a previous touchStart at approximately the same location)
  88. // touchEvent: Event
  89. // a touch event
  90. // returns: boolean
  91. // true if this event is considered a double tap
  92. // tags:
  93. // private
  94. var isDoubleTap = false;
  95. var touches = touchEvent.touches;
  96. if ((this._tapCount > 0) && touches.length == 1) {
  97. // test distance from last tap
  98. var dx = (touches[0].pageX - this._lastTap.x);
  99. var dy = (touches[0].pageY - this._lastTap.y);
  100. var distance = dx * dx + dy * dy;
  101. if (distance < this._tapThreshold) {
  102. isDoubleTap = true;
  103. } else {
  104. this._tapCount = 0;
  105. }
  106. }
  107. this._tapCount++;
  108. this._lastTap.x = touches[0].pageX;
  109. this._lastTap.y = touches[0].pageY;
  110. setTimeout(lang.hitch(this, function(){
  111. this._tapCount = 0;
  112. }), 300);
  113. return isDoubleTap;
  114. },
  115. _doubleTapHandler : function(touchEvent){
  116. // summary:
  117. // action performed on the map when a double tap was triggered
  118. // touchEvent: Event
  119. // a touch event
  120. // tags:
  121. // private
  122. // perform a basic 2x zoom on touch
  123. var touches = touchEvent.touches;
  124. var marginBox = html.marginBox(this._map.div);
  125. var offX = touches[0].pageX - marginBox.l;
  126. var offY = touches[0].pageY - marginBox.t;
  127. // clicked map point before zooming
  128. var mapPoint = this._map.getLonLatFromPixel(new OpenLayers.Pixel(offX, offY));
  129. // zoom increment power
  130. this._map.setCenter(new OpenLayers.LonLat(mapPoint.lon, mapPoint.lat), this._map.getZoom() + 1);
  131. },
  132. _touchStartHandler : function(touchEvent){
  133. // summary:
  134. // action performed on the map when a touch start was triggered
  135. // touchEvent: Event
  136. // a touch event
  137. // tags:
  138. // private
  139. event.stop(touchEvent);
  140. // test double tap
  141. if (this._isDoubleTap(touchEvent)) {
  142. this._doubleTapHandler(touchEvent);
  143. return;
  144. }
  145. // compute map midpoint between fingers
  146. var middlePoint = this._getTouchBarycenter(touchEvent);
  147. this._centerTouchLocation = this._map.getLonLatFromPixel(new OpenLayers.Pixel(middlePoint.x, middlePoint.y));
  148. // store initial finger spacing to compute zoom later
  149. this._initialFingerSpacing = this._getFingerSpacing(touchEvent);
  150. // store initial map scale
  151. this._initialScale = this._map.getScale();
  152. // install touch move and up listeners (if not done by other fingers before)
  153. if (!this._touchMoveListener)
  154. this._touchMoveListener = connect.connect(window.global, "touchmove", this, this._touchMoveHandler);
  155. if (!this._touchEndListener)
  156. this._touchEndListener = connect.connect(window.global, "touchend", this, this._touchEndHandler);
  157. },
  158. _touchEndHandler : function(touchEvent){
  159. // summary:
  160. // action performed on the map when a touch end was triggered
  161. // touchEvent: Event
  162. // a touch event
  163. // tags:
  164. // private
  165. event.stop(touchEvent);
  166. var touches = touchEvent.touches;
  167. if (touches.length == 0) {
  168. // disconnect listeners only when all fingers are up
  169. if (this._touchMoveListener) {
  170. connect.disconnect(this._touchMoveListener);
  171. this._touchMoveListener = null;
  172. }
  173. if (this._touchEndListener) {
  174. connect.disconnect(this._touchEndListener);
  175. this._touchEndListener = null;
  176. }
  177. } else {
  178. // recompute touch center
  179. var middlePoint = this._getTouchBarycenter(touchEvent);
  180. this._centerTouchLocation = this._map.getLonLatFromPixel(new OpenLayers.Pixel(middlePoint.x, middlePoint.y));
  181. }
  182. },
  183. _touchMoveHandler : function(touchEvent){
  184. // summary:
  185. // action performed on the map when a touch move was triggered
  186. // touchEvent: Event
  187. // a touch event
  188. // tags:
  189. // private
  190. // prevent browser interaction
  191. event.stop(touchEvent);
  192. var middlePoint = this._getTouchBarycenter(touchEvent);
  193. // compute map offset
  194. var mapPoint = this._map.getLonLatFromPixel(new OpenLayers.Pixel(middlePoint.x, middlePoint.y));
  195. var mapOffsetLon = mapPoint.lon - this._centerTouchLocation.lon;
  196. var mapOffsetLat = mapPoint.lat - this._centerTouchLocation.lat;
  197. // compute scale factor
  198. var scaleFactor = 1;
  199. var touches = touchEvent.touches;
  200. if (touches.length >= 2) {
  201. var fingerSpacing = this._getFingerSpacing(touchEvent);
  202. scaleFactor = fingerSpacing / this._initialFingerSpacing;
  203. // weird openlayer bug : setting several times the same scale value lead to visual zoom...
  204. this._map.zoomToScale(this._initialScale / scaleFactor);
  205. }
  206. // adjust map center on barycenter
  207. var currentMapCenter = this._map.getCenter();
  208. this._map.setCenter(new OpenLayers.LonLat(currentMapCenter.lon - mapOffsetLon, currentMapCenter.lat
  209. - mapOffsetLat));
  210. }
  211. });
  212. });