ScreenSpaceEventHandler.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. import AssociativeArray from './AssociativeArray.js';
  2. import Cartesian2 from './Cartesian2.js';
  3. import defaultValue from './defaultValue.js';
  4. import defined from './defined.js';
  5. import destroyObject from './destroyObject.js';
  6. import DeveloperError from './DeveloperError.js';
  7. import FeatureDetection from './FeatureDetection.js';
  8. import getTimestamp from './getTimestamp.js';
  9. import KeyboardEventModifier from './KeyboardEventModifier.js';
  10. import ScreenSpaceEventType from './ScreenSpaceEventType.js';
  11. function getPosition(screenSpaceEventHandler, event, result) {
  12. var element = screenSpaceEventHandler._element;
  13. if (element === document) {
  14. result.x = event.clientX;
  15. result.y = event.clientY;
  16. return result;
  17. }
  18. var rect = element.getBoundingClientRect();
  19. result.x = event.clientX - rect.left;
  20. result.y = event.clientY - rect.top;
  21. return result;
  22. }
  23. function getInputEventKey(type, modifier) {
  24. var key = type;
  25. if (defined(modifier)) {
  26. key += '+' + modifier;
  27. }
  28. return key;
  29. }
  30. function getModifier(event) {
  31. if (event.shiftKey) {
  32. return KeyboardEventModifier.SHIFT;
  33. } else if (event.ctrlKey) {
  34. return KeyboardEventModifier.CTRL;
  35. } else if (event.altKey) {
  36. return KeyboardEventModifier.ALT;
  37. }
  38. return undefined;
  39. }
  40. var MouseButton = {
  41. LEFT : 0,
  42. MIDDLE : 1,
  43. RIGHT : 2
  44. };
  45. function registerListener(screenSpaceEventHandler, domType, element, callback) {
  46. function listener(e) {
  47. callback(screenSpaceEventHandler, e);
  48. }
  49. element.addEventListener(domType, listener, false);
  50. screenSpaceEventHandler._removalFunctions.push(function() {
  51. element.removeEventListener(domType, listener, false);
  52. });
  53. }
  54. function registerListeners(screenSpaceEventHandler) {
  55. var element = screenSpaceEventHandler._element;
  56. // some listeners may be registered on the document, so we still get events even after
  57. // leaving the bounds of element.
  58. // this is affected by the existence of an undocumented disableRootEvents property on element.
  59. var alternateElement = !defined(element.disableRootEvents) ? document : element;
  60. if (FeatureDetection.supportsPointerEvents()) {
  61. registerListener(screenSpaceEventHandler, 'pointerdown', element, handlePointerDown);
  62. registerListener(screenSpaceEventHandler, 'pointerup', element, handlePointerUp);
  63. registerListener(screenSpaceEventHandler, 'pointermove', element, handlePointerMove);
  64. registerListener(screenSpaceEventHandler, 'pointercancel', element, handlePointerUp);
  65. } else {
  66. registerListener(screenSpaceEventHandler, 'mousedown', element, handleMouseDown);
  67. registerListener(screenSpaceEventHandler, 'mouseup', alternateElement, handleMouseUp);
  68. registerListener(screenSpaceEventHandler, 'mousemove', alternateElement, handleMouseMove);
  69. registerListener(screenSpaceEventHandler, 'touchstart', element, handleTouchStart);
  70. registerListener(screenSpaceEventHandler, 'touchend', alternateElement, handleTouchEnd);
  71. registerListener(screenSpaceEventHandler, 'touchmove', alternateElement, handleTouchMove);
  72. registerListener(screenSpaceEventHandler, 'touchcancel', alternateElement, handleTouchEnd);
  73. }
  74. registerListener(screenSpaceEventHandler, 'dblclick', element, handleDblClick);
  75. // detect available wheel event
  76. var wheelEvent;
  77. if ('onwheel' in element) {
  78. // spec event type
  79. wheelEvent = 'wheel';
  80. } else if (document.onmousewheel !== undefined) {
  81. // legacy event type
  82. wheelEvent = 'mousewheel';
  83. } else {
  84. // older Firefox
  85. wheelEvent = 'DOMMouseScroll';
  86. }
  87. registerListener(screenSpaceEventHandler, wheelEvent, element, handleWheel);
  88. }
  89. function unregisterListeners(screenSpaceEventHandler) {
  90. var removalFunctions = screenSpaceEventHandler._removalFunctions;
  91. for (var i = 0; i < removalFunctions.length; ++i) {
  92. removalFunctions[i]();
  93. }
  94. }
  95. var mouseDownEvent = {
  96. position : new Cartesian2()
  97. };
  98. function gotTouchEvent(screenSpaceEventHandler) {
  99. screenSpaceEventHandler._lastSeenTouchEvent = getTimestamp();
  100. }
  101. function canProcessMouseEvent(screenSpaceEventHandler) {
  102. return (getTimestamp() - screenSpaceEventHandler._lastSeenTouchEvent) > ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds;
  103. }
  104. function checkPixelTolerance(startPosition, endPosition, pixelTolerance) {
  105. var xDiff = startPosition.x - endPosition.x;
  106. var yDiff = startPosition.y - endPosition.y;
  107. var totalPixels = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
  108. return totalPixels < pixelTolerance;
  109. }
  110. function handleMouseDown(screenSpaceEventHandler, event) {
  111. if (!canProcessMouseEvent(screenSpaceEventHandler)) {
  112. return;
  113. }
  114. var button = event.button;
  115. screenSpaceEventHandler._buttonDown[button] = true;
  116. var screenSpaceEventType;
  117. if (button === MouseButton.LEFT) {
  118. screenSpaceEventType = ScreenSpaceEventType.LEFT_DOWN;
  119. } else if (button === MouseButton.MIDDLE) {
  120. screenSpaceEventType = ScreenSpaceEventType.MIDDLE_DOWN;
  121. } else if (button === MouseButton.RIGHT) {
  122. screenSpaceEventType = ScreenSpaceEventType.RIGHT_DOWN;
  123. } else {
  124. return;
  125. }
  126. var position = getPosition(screenSpaceEventHandler, event, screenSpaceEventHandler._primaryPosition);
  127. Cartesian2.clone(position, screenSpaceEventHandler._primaryStartPosition);
  128. Cartesian2.clone(position, screenSpaceEventHandler._primaryPreviousPosition);
  129. var modifier = getModifier(event);
  130. var action = screenSpaceEventHandler.getInputAction(screenSpaceEventType, modifier);
  131. if (defined(action)) {
  132. Cartesian2.clone(position, mouseDownEvent.position);
  133. action(mouseDownEvent);
  134. event.preventDefault();
  135. }
  136. }
  137. var mouseUpEvent = {
  138. position : new Cartesian2()
  139. };
  140. var mouseClickEvent = {
  141. position : new Cartesian2()
  142. };
  143. function cancelMouseEvent(screenSpaceEventHandler, screenSpaceEventType, clickScreenSpaceEventType, event) {
  144. var modifier = getModifier(event);
  145. var action = screenSpaceEventHandler.getInputAction(screenSpaceEventType, modifier);
  146. var clickAction = screenSpaceEventHandler.getInputAction(clickScreenSpaceEventType, modifier);
  147. if (defined(action) || defined(clickAction)) {
  148. var position = getPosition(screenSpaceEventHandler, event, screenSpaceEventHandler._primaryPosition);
  149. if (defined(action)) {
  150. Cartesian2.clone(position, mouseUpEvent.position);
  151. action(mouseUpEvent);
  152. }
  153. if (defined(clickAction)) {
  154. var startPosition = screenSpaceEventHandler._primaryStartPosition;
  155. if (checkPixelTolerance(startPosition, position, screenSpaceEventHandler._clickPixelTolerance)) {
  156. Cartesian2.clone(position, mouseClickEvent.position);
  157. clickAction(mouseClickEvent);
  158. }
  159. }
  160. }
  161. }
  162. function handleMouseUp(screenSpaceEventHandler, event) {
  163. if (!canProcessMouseEvent(screenSpaceEventHandler)) {
  164. return;
  165. }
  166. var button = event.button;
  167. if (button !== MouseButton.LEFT && button !== MouseButton.MIDDLE && button !== MouseButton.RIGHT){
  168. return;
  169. }
  170. if(screenSpaceEventHandler._buttonDown[MouseButton.LEFT]){
  171. cancelMouseEvent(screenSpaceEventHandler, ScreenSpaceEventType.LEFT_UP, ScreenSpaceEventType.LEFT_CLICK, event);
  172. screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = false;
  173. }
  174. if(screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE]){
  175. cancelMouseEvent(screenSpaceEventHandler, ScreenSpaceEventType.MIDDLE_UP, ScreenSpaceEventType.MIDDLE_CLICK, event);
  176. screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE] = false;
  177. }
  178. if(screenSpaceEventHandler._buttonDown[MouseButton.RIGHT]){
  179. cancelMouseEvent(screenSpaceEventHandler, ScreenSpaceEventType.RIGHT_UP, ScreenSpaceEventType.RIGHT_CLICK, event);
  180. screenSpaceEventHandler._buttonDown[MouseButton.RIGHT] = false;
  181. }
  182. }
  183. var mouseMoveEvent = {
  184. startPosition : new Cartesian2(),
  185. endPosition : new Cartesian2()
  186. };
  187. function handleMouseMove(screenSpaceEventHandler, event) {
  188. if (!canProcessMouseEvent(screenSpaceEventHandler)) {
  189. return;
  190. }
  191. var modifier = getModifier(event);
  192. var position = getPosition(screenSpaceEventHandler, event, screenSpaceEventHandler._primaryPosition);
  193. var previousPosition = screenSpaceEventHandler._primaryPreviousPosition;
  194. var action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.MOUSE_MOVE, modifier);
  195. if (defined(action)) {
  196. Cartesian2.clone(previousPosition, mouseMoveEvent.startPosition);
  197. Cartesian2.clone(position, mouseMoveEvent.endPosition);
  198. action(mouseMoveEvent);
  199. }
  200. Cartesian2.clone(position, previousPosition);
  201. if (screenSpaceEventHandler._buttonDown[MouseButton.LEFT] ||
  202. screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE] ||
  203. screenSpaceEventHandler._buttonDown[MouseButton.RIGHT]) {
  204. event.preventDefault();
  205. }
  206. }
  207. var mouseDblClickEvent = {
  208. position : new Cartesian2()
  209. };
  210. function handleDblClick(screenSpaceEventHandler, event) {
  211. var button = event.button;
  212. var screenSpaceEventType;
  213. if (button === MouseButton.LEFT) {
  214. screenSpaceEventType = ScreenSpaceEventType.LEFT_DOUBLE_CLICK;
  215. } else {
  216. return;
  217. }
  218. var modifier = getModifier(event);
  219. var action = screenSpaceEventHandler.getInputAction(screenSpaceEventType, modifier);
  220. if (defined(action)) {
  221. getPosition(screenSpaceEventHandler, event, mouseDblClickEvent.position);
  222. action(mouseDblClickEvent);
  223. }
  224. }
  225. function handleWheel(screenSpaceEventHandler, event) {
  226. // currently this event exposes the delta value in terms of
  227. // the obsolete mousewheel event type. so, for now, we adapt the other
  228. // values to that scheme.
  229. var delta;
  230. // standard wheel event uses deltaY. sign is opposite wheelDelta.
  231. // deltaMode indicates what unit it is in.
  232. if (defined(event.deltaY)) {
  233. var deltaMode = event.deltaMode;
  234. if (deltaMode === event.DOM_DELTA_PIXEL) {
  235. delta = -event.deltaY;
  236. } else if (deltaMode === event.DOM_DELTA_LINE) {
  237. delta = -event.deltaY * 40;
  238. } else {
  239. // DOM_DELTA_PAGE
  240. delta = -event.deltaY * 120;
  241. }
  242. } else if (event.detail > 0) {
  243. // old Firefox versions use event.detail to count the number of clicks. The sign
  244. // of the integer is the direction the wheel is scrolled.
  245. delta = event.detail * -120;
  246. } else {
  247. delta = event.wheelDelta;
  248. }
  249. if (!defined(delta)) {
  250. return;
  251. }
  252. var modifier = getModifier(event);
  253. var action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.WHEEL, modifier);
  254. if (defined(action)) {
  255. action(delta);
  256. event.preventDefault();
  257. }
  258. }
  259. function handleTouchStart(screenSpaceEventHandler, event) {
  260. gotTouchEvent(screenSpaceEventHandler);
  261. var changedTouches = event.changedTouches;
  262. var i;
  263. var length = changedTouches.length;
  264. var touch;
  265. var identifier;
  266. var positions = screenSpaceEventHandler._positions;
  267. for (i = 0; i < length; ++i) {
  268. touch = changedTouches[i];
  269. identifier = touch.identifier;
  270. positions.set(identifier, getPosition(screenSpaceEventHandler, touch, new Cartesian2()));
  271. }
  272. fireTouchEvents(screenSpaceEventHandler, event);
  273. var previousPositions = screenSpaceEventHandler._previousPositions;
  274. for (i = 0; i < length; ++i) {
  275. touch = changedTouches[i];
  276. identifier = touch.identifier;
  277. previousPositions.set(identifier, Cartesian2.clone(positions.get(identifier)));
  278. }
  279. }
  280. function handleTouchEnd(screenSpaceEventHandler, event) {
  281. gotTouchEvent(screenSpaceEventHandler);
  282. var changedTouches = event.changedTouches;
  283. var i;
  284. var length = changedTouches.length;
  285. var touch;
  286. var identifier;
  287. var positions = screenSpaceEventHandler._positions;
  288. for (i = 0; i < length; ++i) {
  289. touch = changedTouches[i];
  290. identifier = touch.identifier;
  291. positions.remove(identifier);
  292. }
  293. fireTouchEvents(screenSpaceEventHandler, event);
  294. var previousPositions = screenSpaceEventHandler._previousPositions;
  295. for (i = 0; i < length; ++i) {
  296. touch = changedTouches[i];
  297. identifier = touch.identifier;
  298. previousPositions.remove(identifier);
  299. }
  300. }
  301. var touchStartEvent = {
  302. position : new Cartesian2()
  303. };
  304. var touch2StartEvent = {
  305. position1 : new Cartesian2(),
  306. position2 : new Cartesian2()
  307. };
  308. var touchEndEvent = {
  309. position : new Cartesian2()
  310. };
  311. var touchClickEvent = {
  312. position : new Cartesian2()
  313. };
  314. var touchHoldEvent = {
  315. position : new Cartesian2()
  316. };
  317. function fireTouchEvents(screenSpaceEventHandler, event) {
  318. var modifier = getModifier(event);
  319. var positions = screenSpaceEventHandler._positions;
  320. var numberOfTouches = positions.length;
  321. var action;
  322. var clickAction;
  323. var pinching = screenSpaceEventHandler._isPinching;
  324. if (numberOfTouches !== 1 && screenSpaceEventHandler._buttonDown[MouseButton.LEFT]) {
  325. // transitioning from single touch, trigger UP and might trigger CLICK
  326. screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = false;
  327. if(defined(screenSpaceEventHandler._touchHoldTimer)) {
  328. clearTimeout(screenSpaceEventHandler._touchHoldTimer);
  329. screenSpaceEventHandler._touchHoldTimer = undefined;
  330. }
  331. action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.LEFT_UP, modifier);
  332. if (defined(action)) {
  333. Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchEndEvent.position);
  334. action(touchEndEvent);
  335. }
  336. if (numberOfTouches === 0 && !screenSpaceEventHandler._isTouchHolding) {
  337. // releasing single touch, check for CLICK
  338. clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.LEFT_CLICK, modifier);
  339. if (defined(clickAction)) {
  340. var startPosition = screenSpaceEventHandler._primaryStartPosition;
  341. var endPosition = screenSpaceEventHandler._previousPositions.values[0];
  342. if(checkPixelTolerance(startPosition, endPosition, screenSpaceEventHandler._clickPixelTolerance)) {
  343. Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchClickEvent.position);
  344. clickAction(touchClickEvent);
  345. }
  346. }
  347. }
  348. screenSpaceEventHandler._isTouchHolding = false;
  349. // Otherwise don't trigger CLICK, because we are adding more touches.
  350. }
  351. if (numberOfTouches === 0 && pinching) {
  352. // transitioning from pinch, trigger PINCH_END
  353. screenSpaceEventHandler._isPinching = false;
  354. action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.PINCH_END, modifier);
  355. if (defined(action)) {
  356. action();
  357. }
  358. }
  359. if (numberOfTouches === 1 && !pinching) {
  360. // transitioning to single touch, trigger DOWN
  361. var position = positions.values[0];
  362. Cartesian2.clone(position, screenSpaceEventHandler._primaryPosition);
  363. Cartesian2.clone(position, screenSpaceEventHandler._primaryStartPosition);
  364. Cartesian2.clone(position, screenSpaceEventHandler._primaryPreviousPosition);
  365. screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = true;
  366. action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.LEFT_DOWN, modifier);
  367. if (defined(action)) {
  368. Cartesian2.clone(position, touchStartEvent.position);
  369. action(touchStartEvent);
  370. }
  371. screenSpaceEventHandler._touchHoldTimer = setTimeout(function() {
  372. if(!screenSpaceEventHandler.isDestroyed()) {
  373. screenSpaceEventHandler._touchHoldTimer = undefined;
  374. screenSpaceEventHandler._isTouchHolding = true;
  375. clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.RIGHT_CLICK, modifier);
  376. if (defined(clickAction)) {
  377. var startPosition = screenSpaceEventHandler._primaryStartPosition;
  378. var endPosition = screenSpaceEventHandler._previousPositions.values[0];
  379. if(checkPixelTolerance(startPosition, endPosition, screenSpaceEventHandler._holdPixelTolerance)) {
  380. Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchHoldEvent.position);
  381. clickAction(touchHoldEvent);
  382. }
  383. }
  384. }
  385. }, ScreenSpaceEventHandler.touchHoldDelayMilliseconds);
  386. event.preventDefault();
  387. }
  388. if (numberOfTouches === 2 && !pinching) {
  389. // transitioning to pinch, trigger PINCH_START
  390. screenSpaceEventHandler._isPinching = true;
  391. action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.PINCH_START, modifier);
  392. if (defined(action)) {
  393. Cartesian2.clone(positions.values[0], touch2StartEvent.position1);
  394. Cartesian2.clone(positions.values[1], touch2StartEvent.position2);
  395. action(touch2StartEvent);
  396. // Touch-enabled devices, in particular iOS can have many default behaviours for
  397. // "pinch" events, which can still be executed unless we prevent them here.
  398. event.preventDefault();
  399. }
  400. }
  401. }
  402. function handleTouchMove(screenSpaceEventHandler, event) {
  403. gotTouchEvent(screenSpaceEventHandler);
  404. var changedTouches = event.changedTouches;
  405. var i;
  406. var length = changedTouches.length;
  407. var touch;
  408. var identifier;
  409. var positions = screenSpaceEventHandler._positions;
  410. for (i = 0; i < length; ++i) {
  411. touch = changedTouches[i];
  412. identifier = touch.identifier;
  413. var position = positions.get(identifier);
  414. if (defined(position)) {
  415. getPosition(screenSpaceEventHandler, touch, position);
  416. }
  417. }
  418. fireTouchMoveEvents(screenSpaceEventHandler, event);
  419. var previousPositions = screenSpaceEventHandler._previousPositions;
  420. for (i = 0; i < length; ++i) {
  421. touch = changedTouches[i];
  422. identifier = touch.identifier;
  423. Cartesian2.clone(positions.get(identifier), previousPositions.get(identifier));
  424. }
  425. }
  426. var touchMoveEvent = {
  427. startPosition : new Cartesian2(),
  428. endPosition : new Cartesian2()
  429. };
  430. var touchPinchMovementEvent = {
  431. distance : {
  432. startPosition : new Cartesian2(),
  433. endPosition : new Cartesian2()
  434. },
  435. angleAndHeight : {
  436. startPosition : new Cartesian2(),
  437. endPosition : new Cartesian2()
  438. }
  439. };
  440. function fireTouchMoveEvents(screenSpaceEventHandler, event) {
  441. var modifier = getModifier(event);
  442. var positions = screenSpaceEventHandler._positions;
  443. var previousPositions = screenSpaceEventHandler._previousPositions;
  444. var numberOfTouches = positions.length;
  445. var action;
  446. if (numberOfTouches === 1 && screenSpaceEventHandler._buttonDown[MouseButton.LEFT]) {
  447. // moving single touch
  448. var position = positions.values[0];
  449. Cartesian2.clone(position, screenSpaceEventHandler._primaryPosition);
  450. var previousPosition = screenSpaceEventHandler._primaryPreviousPosition;
  451. action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.MOUSE_MOVE, modifier);
  452. if (defined(action)) {
  453. Cartesian2.clone(previousPosition, touchMoveEvent.startPosition);
  454. Cartesian2.clone(position, touchMoveEvent.endPosition);
  455. action(touchMoveEvent);
  456. }
  457. Cartesian2.clone(position, previousPosition);
  458. event.preventDefault();
  459. } else if (numberOfTouches === 2 && screenSpaceEventHandler._isPinching) {
  460. // moving pinch
  461. action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.PINCH_MOVE, modifier);
  462. if (defined(action)) {
  463. var position1 = positions.values[0];
  464. var position2 = positions.values[1];
  465. var previousPosition1 = previousPositions.values[0];
  466. var previousPosition2 = previousPositions.values[1];
  467. var dX = position2.x - position1.x;
  468. var dY = position2.y - position1.y;
  469. var dist = Math.sqrt(dX * dX + dY * dY) * 0.25;
  470. var prevDX = previousPosition2.x - previousPosition1.x;
  471. var prevDY = previousPosition2.y - previousPosition1.y;
  472. var prevDist = Math.sqrt(prevDX * prevDX + prevDY * prevDY) * 0.25;
  473. var cY = (position2.y + position1.y) * 0.125;
  474. var prevCY = (previousPosition2.y + previousPosition1.y) * 0.125;
  475. var angle = Math.atan2(dY, dX);
  476. var prevAngle = Math.atan2(prevDY, prevDX);
  477. Cartesian2.fromElements(0.0, prevDist, touchPinchMovementEvent.distance.startPosition);
  478. Cartesian2.fromElements(0.0, dist, touchPinchMovementEvent.distance.endPosition);
  479. Cartesian2.fromElements(prevAngle, prevCY, touchPinchMovementEvent.angleAndHeight.startPosition);
  480. Cartesian2.fromElements(angle, cY, touchPinchMovementEvent.angleAndHeight.endPosition);
  481. action(touchPinchMovementEvent);
  482. }
  483. }
  484. }
  485. function handlePointerDown(screenSpaceEventHandler, event) {
  486. event.target.setPointerCapture(event.pointerId);
  487. if (event.pointerType === 'touch') {
  488. var positions = screenSpaceEventHandler._positions;
  489. var identifier = event.pointerId;
  490. positions.set(identifier, getPosition(screenSpaceEventHandler, event, new Cartesian2()));
  491. fireTouchEvents(screenSpaceEventHandler, event);
  492. var previousPositions = screenSpaceEventHandler._previousPositions;
  493. previousPositions.set(identifier, Cartesian2.clone(positions.get(identifier)));
  494. } else {
  495. handleMouseDown(screenSpaceEventHandler, event);
  496. }
  497. }
  498. function handlePointerUp(screenSpaceEventHandler, event) {
  499. if (event.pointerType === 'touch') {
  500. var positions = screenSpaceEventHandler._positions;
  501. var identifier = event.pointerId;
  502. positions.remove(identifier);
  503. fireTouchEvents(screenSpaceEventHandler, event);
  504. var previousPositions = screenSpaceEventHandler._previousPositions;
  505. previousPositions.remove(identifier);
  506. } else {
  507. handleMouseUp(screenSpaceEventHandler, event);
  508. }
  509. }
  510. function handlePointerMove(screenSpaceEventHandler, event) {
  511. if (event.pointerType === 'touch') {
  512. var positions = screenSpaceEventHandler._positions;
  513. var identifier = event.pointerId;
  514. var position = positions.get(identifier);
  515. if(!defined(position)){
  516. return;
  517. }
  518. getPosition(screenSpaceEventHandler, event, position);
  519. fireTouchMoveEvents(screenSpaceEventHandler, event);
  520. var previousPositions = screenSpaceEventHandler._previousPositions;
  521. Cartesian2.clone(positions.get(identifier), previousPositions.get(identifier));
  522. } else {
  523. handleMouseMove(screenSpaceEventHandler, event);
  524. }
  525. }
  526. /**
  527. * Handles user input events. Custom functions can be added to be executed on
  528. * when the user enters input.
  529. *
  530. * @alias ScreenSpaceEventHandler
  531. *
  532. * @param {Canvas} [element=document] The element to add events to.
  533. *
  534. * @constructor
  535. */
  536. function ScreenSpaceEventHandler(element) {
  537. this._inputEvents = {};
  538. this._buttonDown = {
  539. LEFT: false,
  540. MIDDLE: false,
  541. RIGHT: false
  542. };
  543. this._isPinching = false;
  544. this._isTouchHolding = false;
  545. this._lastSeenTouchEvent = -ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds;
  546. this._primaryStartPosition = new Cartesian2();
  547. this._primaryPosition = new Cartesian2();
  548. this._primaryPreviousPosition = new Cartesian2();
  549. this._positions = new AssociativeArray();
  550. this._previousPositions = new AssociativeArray();
  551. this._removalFunctions = [];
  552. this._touchHoldTimer = undefined;
  553. // TODO: Revisit when doing mobile development. May need to be configurable
  554. // or determined based on the platform?
  555. this._clickPixelTolerance = 5;
  556. this._holdPixelTolerance = 25;
  557. this._element = defaultValue(element, document);
  558. registerListeners(this);
  559. }
  560. /**
  561. * Set a function to be executed on an input event.
  562. *
  563. * @param {Function} action Function to be executed when the input event occurs.
  564. * @param {Number} type The ScreenSpaceEventType of input event.
  565. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a <code>type</code>
  566. * event occurs.
  567. *
  568. * @see ScreenSpaceEventHandler#getInputAction
  569. * @see ScreenSpaceEventHandler#removeInputAction
  570. */
  571. ScreenSpaceEventHandler.prototype.setInputAction = function(action, type, modifier) {
  572. //>>includeStart('debug', pragmas.debug);
  573. if (!defined(action)) {
  574. throw new DeveloperError('action is required.');
  575. }
  576. if (!defined(type)) {
  577. throw new DeveloperError('type is required.');
  578. }
  579. //>>includeEnd('debug');
  580. var key = getInputEventKey(type, modifier);
  581. this._inputEvents[key] = action;
  582. };
  583. /**
  584. * Returns the function to be executed on an input event.
  585. *
  586. * @param {Number} type The ScreenSpaceEventType of input event.
  587. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a <code>type</code>
  588. * event occurs.
  589. *
  590. * @see ScreenSpaceEventHandler#setInputAction
  591. * @see ScreenSpaceEventHandler#removeInputAction
  592. */
  593. ScreenSpaceEventHandler.prototype.getInputAction = function(type, modifier) {
  594. //>>includeStart('debug', pragmas.debug);
  595. if (!defined(type)) {
  596. throw new DeveloperError('type is required.');
  597. }
  598. //>>includeEnd('debug');
  599. var key = getInputEventKey(type, modifier);
  600. return this._inputEvents[key];
  601. };
  602. /**
  603. * Removes the function to be executed on an input event.
  604. *
  605. * @param {Number} type The ScreenSpaceEventType of input event.
  606. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a <code>type</code>
  607. * event occurs.
  608. *
  609. * @see ScreenSpaceEventHandler#getInputAction
  610. * @see ScreenSpaceEventHandler#setInputAction
  611. */
  612. ScreenSpaceEventHandler.prototype.removeInputAction = function(type, modifier) {
  613. //>>includeStart('debug', pragmas.debug);
  614. if (!defined(type)) {
  615. throw new DeveloperError('type is required.');
  616. }
  617. //>>includeEnd('debug');
  618. var key = getInputEventKey(type, modifier);
  619. delete this._inputEvents[key];
  620. };
  621. /**
  622. * Returns true if this object was destroyed; otherwise, false.
  623. * <br /><br />
  624. * If this object was destroyed, it should not be used; calling any function other than
  625. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  626. *
  627. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  628. *
  629. * @see ScreenSpaceEventHandler#destroy
  630. */
  631. ScreenSpaceEventHandler.prototype.isDestroyed = function() {
  632. return false;
  633. };
  634. /**
  635. * Removes listeners held by this object.
  636. * <br /><br />
  637. * Once an object is destroyed, it should not be used; calling any function other than
  638. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  639. * assign the return value (<code>undefined</code>) to the object as done in the example.
  640. *
  641. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  642. *
  643. *
  644. * @example
  645. * handler = handler && handler.destroy();
  646. *
  647. * @see ScreenSpaceEventHandler#isDestroyed
  648. */
  649. ScreenSpaceEventHandler.prototype.destroy = function() {
  650. unregisterListeners(this);
  651. return destroyObject(this);
  652. };
  653. /**
  654. * The amount of time, in milliseconds, that mouse events will be disabled after
  655. * receiving any touch events, such that any emulated mouse events will be ignored.
  656. * @type {Number}
  657. * @default 800
  658. */
  659. ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds = 800;
  660. /**
  661. * The amount of time, in milliseconds, before a touch on the screen becomes a
  662. * touch and hold.
  663. * @type {Number}
  664. * @default 1500
  665. */
  666. ScreenSpaceEventHandler.touchHoldDelayMilliseconds = 1500;
  667. export default ScreenSpaceEventHandler;