ScreenSpaceEventHandler.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062
  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. if (FeatureDetection.isInternetExplorer()) {
  50. element.addEventListener(domType, listener, false);
  51. } else {
  52. element.addEventListener(domType, listener, {
  53. capture: false,
  54. passive: false,
  55. });
  56. }
  57. screenSpaceEventHandler._removalFunctions.push(function () {
  58. element.removeEventListener(domType, listener, false);
  59. });
  60. }
  61. function registerListeners(screenSpaceEventHandler) {
  62. var element = screenSpaceEventHandler._element;
  63. // some listeners may be registered on the document, so we still get events even after
  64. // leaving the bounds of element.
  65. // this is affected by the existence of an undocumented disableRootEvents property on element.
  66. var alternateElement = !defined(element.disableRootEvents)
  67. ? document
  68. : element;
  69. if (FeatureDetection.supportsPointerEvents()) {
  70. registerListener(
  71. screenSpaceEventHandler,
  72. "pointerdown",
  73. element,
  74. handlePointerDown
  75. );
  76. registerListener(
  77. screenSpaceEventHandler,
  78. "pointerup",
  79. element,
  80. handlePointerUp
  81. );
  82. registerListener(
  83. screenSpaceEventHandler,
  84. "pointermove",
  85. element,
  86. handlePointerMove
  87. );
  88. registerListener(
  89. screenSpaceEventHandler,
  90. "pointercancel",
  91. element,
  92. handlePointerUp
  93. );
  94. } else {
  95. registerListener(
  96. screenSpaceEventHandler,
  97. "mousedown",
  98. element,
  99. handleMouseDown
  100. );
  101. registerListener(
  102. screenSpaceEventHandler,
  103. "mouseup",
  104. alternateElement,
  105. handleMouseUp
  106. );
  107. registerListener(
  108. screenSpaceEventHandler,
  109. "mousemove",
  110. alternateElement,
  111. handleMouseMove
  112. );
  113. registerListener(
  114. screenSpaceEventHandler,
  115. "touchstart",
  116. element,
  117. handleTouchStart
  118. );
  119. registerListener(
  120. screenSpaceEventHandler,
  121. "touchend",
  122. alternateElement,
  123. handleTouchEnd
  124. );
  125. registerListener(
  126. screenSpaceEventHandler,
  127. "touchmove",
  128. alternateElement,
  129. handleTouchMove
  130. );
  131. registerListener(
  132. screenSpaceEventHandler,
  133. "touchcancel",
  134. alternateElement,
  135. handleTouchEnd
  136. );
  137. }
  138. registerListener(
  139. screenSpaceEventHandler,
  140. "dblclick",
  141. element,
  142. handleDblClick
  143. );
  144. // detect available wheel event
  145. var wheelEvent;
  146. if ("onwheel" in element) {
  147. // spec event type
  148. wheelEvent = "wheel";
  149. } else if (document.onmousewheel !== undefined) {
  150. // legacy event type
  151. wheelEvent = "mousewheel";
  152. } else {
  153. // older Firefox
  154. wheelEvent = "DOMMouseScroll";
  155. }
  156. registerListener(screenSpaceEventHandler, wheelEvent, element, handleWheel);
  157. }
  158. function unregisterListeners(screenSpaceEventHandler) {
  159. var removalFunctions = screenSpaceEventHandler._removalFunctions;
  160. for (var i = 0; i < removalFunctions.length; ++i) {
  161. removalFunctions[i]();
  162. }
  163. }
  164. var mouseDownEvent = {
  165. position: new Cartesian2(),
  166. };
  167. function gotTouchEvent(screenSpaceEventHandler) {
  168. screenSpaceEventHandler._lastSeenTouchEvent = getTimestamp();
  169. }
  170. function canProcessMouseEvent(screenSpaceEventHandler) {
  171. return (
  172. getTimestamp() - screenSpaceEventHandler._lastSeenTouchEvent >
  173. ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds
  174. );
  175. }
  176. function checkPixelTolerance(startPosition, endPosition, pixelTolerance) {
  177. var xDiff = startPosition.x - endPosition.x;
  178. var yDiff = startPosition.y - endPosition.y;
  179. var totalPixels = Math.sqrt(xDiff * xDiff + yDiff * yDiff);
  180. return totalPixels < pixelTolerance;
  181. }
  182. function handleMouseDown(screenSpaceEventHandler, event) {
  183. if (!canProcessMouseEvent(screenSpaceEventHandler)) {
  184. return;
  185. }
  186. var button = event.button;
  187. screenSpaceEventHandler._buttonDown[button] = true;
  188. var screenSpaceEventType;
  189. if (button === MouseButton.LEFT) {
  190. screenSpaceEventType = ScreenSpaceEventType.LEFT_DOWN;
  191. } else if (button === MouseButton.MIDDLE) {
  192. screenSpaceEventType = ScreenSpaceEventType.MIDDLE_DOWN;
  193. } else if (button === MouseButton.RIGHT) {
  194. screenSpaceEventType = ScreenSpaceEventType.RIGHT_DOWN;
  195. } else {
  196. return;
  197. }
  198. var position = getPosition(
  199. screenSpaceEventHandler,
  200. event,
  201. screenSpaceEventHandler._primaryPosition
  202. );
  203. Cartesian2.clone(position, screenSpaceEventHandler._primaryStartPosition);
  204. Cartesian2.clone(position, screenSpaceEventHandler._primaryPreviousPosition);
  205. var modifier = getModifier(event);
  206. var action = screenSpaceEventHandler.getInputAction(
  207. screenSpaceEventType,
  208. modifier
  209. );
  210. if (defined(action)) {
  211. Cartesian2.clone(position, mouseDownEvent.position);
  212. action(mouseDownEvent);
  213. event.preventDefault();
  214. }
  215. }
  216. var mouseUpEvent = {
  217. position: new Cartesian2(),
  218. };
  219. var mouseClickEvent = {
  220. position: new Cartesian2(),
  221. };
  222. function cancelMouseEvent(
  223. screenSpaceEventHandler,
  224. screenSpaceEventType,
  225. clickScreenSpaceEventType,
  226. event
  227. ) {
  228. var modifier = getModifier(event);
  229. var action = screenSpaceEventHandler.getInputAction(
  230. screenSpaceEventType,
  231. modifier
  232. );
  233. var clickAction = screenSpaceEventHandler.getInputAction(
  234. clickScreenSpaceEventType,
  235. modifier
  236. );
  237. if (defined(action) || defined(clickAction)) {
  238. var position = getPosition(
  239. screenSpaceEventHandler,
  240. event,
  241. screenSpaceEventHandler._primaryPosition
  242. );
  243. if (defined(action)) {
  244. Cartesian2.clone(position, mouseUpEvent.position);
  245. action(mouseUpEvent);
  246. }
  247. if (defined(clickAction)) {
  248. var startPosition = screenSpaceEventHandler._primaryStartPosition;
  249. if (
  250. checkPixelTolerance(
  251. startPosition,
  252. position,
  253. screenSpaceEventHandler._clickPixelTolerance
  254. )
  255. ) {
  256. Cartesian2.clone(position, mouseClickEvent.position);
  257. clickAction(mouseClickEvent);
  258. }
  259. }
  260. }
  261. }
  262. function handleMouseUp(screenSpaceEventHandler, event) {
  263. if (!canProcessMouseEvent(screenSpaceEventHandler)) {
  264. return;
  265. }
  266. var button = event.button;
  267. if (
  268. button !== MouseButton.LEFT &&
  269. button !== MouseButton.MIDDLE &&
  270. button !== MouseButton.RIGHT
  271. ) {
  272. return;
  273. }
  274. if (screenSpaceEventHandler._buttonDown[MouseButton.LEFT]) {
  275. cancelMouseEvent(
  276. screenSpaceEventHandler,
  277. ScreenSpaceEventType.LEFT_UP,
  278. ScreenSpaceEventType.LEFT_CLICK,
  279. event
  280. );
  281. screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = false;
  282. }
  283. if (screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE]) {
  284. cancelMouseEvent(
  285. screenSpaceEventHandler,
  286. ScreenSpaceEventType.MIDDLE_UP,
  287. ScreenSpaceEventType.MIDDLE_CLICK,
  288. event
  289. );
  290. screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE] = false;
  291. }
  292. if (screenSpaceEventHandler._buttonDown[MouseButton.RIGHT]) {
  293. cancelMouseEvent(
  294. screenSpaceEventHandler,
  295. ScreenSpaceEventType.RIGHT_UP,
  296. ScreenSpaceEventType.RIGHT_CLICK,
  297. event
  298. );
  299. screenSpaceEventHandler._buttonDown[MouseButton.RIGHT] = false;
  300. }
  301. }
  302. var mouseMoveEvent = {
  303. startPosition: new Cartesian2(),
  304. endPosition: new Cartesian2(),
  305. };
  306. function handleMouseMove(screenSpaceEventHandler, event) {
  307. if (!canProcessMouseEvent(screenSpaceEventHandler)) {
  308. return;
  309. }
  310. var modifier = getModifier(event);
  311. var position = getPosition(
  312. screenSpaceEventHandler,
  313. event,
  314. screenSpaceEventHandler._primaryPosition
  315. );
  316. var previousPosition = screenSpaceEventHandler._primaryPreviousPosition;
  317. var action = screenSpaceEventHandler.getInputAction(
  318. ScreenSpaceEventType.MOUSE_MOVE,
  319. modifier
  320. );
  321. if (defined(action)) {
  322. Cartesian2.clone(previousPosition, mouseMoveEvent.startPosition);
  323. Cartesian2.clone(position, mouseMoveEvent.endPosition);
  324. action(mouseMoveEvent);
  325. }
  326. Cartesian2.clone(position, previousPosition);
  327. if (
  328. screenSpaceEventHandler._buttonDown[MouseButton.LEFT] ||
  329. screenSpaceEventHandler._buttonDown[MouseButton.MIDDLE] ||
  330. screenSpaceEventHandler._buttonDown[MouseButton.RIGHT]
  331. ) {
  332. event.preventDefault();
  333. }
  334. }
  335. var mouseDblClickEvent = {
  336. position: new Cartesian2(),
  337. };
  338. function handleDblClick(screenSpaceEventHandler, event) {
  339. var button = event.button;
  340. var screenSpaceEventType;
  341. if (button === MouseButton.LEFT) {
  342. screenSpaceEventType = ScreenSpaceEventType.LEFT_DOUBLE_CLICK;
  343. } else {
  344. return;
  345. }
  346. var modifier = getModifier(event);
  347. var action = screenSpaceEventHandler.getInputAction(
  348. screenSpaceEventType,
  349. modifier
  350. );
  351. if (defined(action)) {
  352. getPosition(screenSpaceEventHandler, event, mouseDblClickEvent.position);
  353. action(mouseDblClickEvent);
  354. }
  355. }
  356. function handleWheel(screenSpaceEventHandler, event) {
  357. // currently this event exposes the delta value in terms of
  358. // the obsolete mousewheel event type. so, for now, we adapt the other
  359. // values to that scheme.
  360. var delta;
  361. // standard wheel event uses deltaY. sign is opposite wheelDelta.
  362. // deltaMode indicates what unit it is in.
  363. if (defined(event.deltaY)) {
  364. var deltaMode = event.deltaMode;
  365. if (deltaMode === event.DOM_DELTA_PIXEL) {
  366. delta = -event.deltaY;
  367. } else if (deltaMode === event.DOM_DELTA_LINE) {
  368. delta = -event.deltaY * 40;
  369. } else {
  370. // DOM_DELTA_PAGE
  371. delta = -event.deltaY * 120;
  372. }
  373. } else if (event.detail > 0) {
  374. // old Firefox versions use event.detail to count the number of clicks. The sign
  375. // of the integer is the direction the wheel is scrolled.
  376. delta = event.detail * -120;
  377. } else {
  378. delta = event.wheelDelta;
  379. }
  380. if (!defined(delta)) {
  381. return;
  382. }
  383. var modifier = getModifier(event);
  384. var action = screenSpaceEventHandler.getInputAction(
  385. ScreenSpaceEventType.WHEEL,
  386. modifier
  387. );
  388. if (defined(action)) {
  389. action(delta);
  390. event.preventDefault();
  391. }
  392. }
  393. function handleTouchStart(screenSpaceEventHandler, event) {
  394. gotTouchEvent(screenSpaceEventHandler);
  395. var changedTouches = event.changedTouches;
  396. var i;
  397. var length = changedTouches.length;
  398. var touch;
  399. var identifier;
  400. var positions = screenSpaceEventHandler._positions;
  401. for (i = 0; i < length; ++i) {
  402. touch = changedTouches[i];
  403. identifier = touch.identifier;
  404. positions.set(
  405. identifier,
  406. getPosition(screenSpaceEventHandler, touch, new Cartesian2())
  407. );
  408. }
  409. fireTouchEvents(screenSpaceEventHandler, event);
  410. var previousPositions = screenSpaceEventHandler._previousPositions;
  411. for (i = 0; i < length; ++i) {
  412. touch = changedTouches[i];
  413. identifier = touch.identifier;
  414. previousPositions.set(
  415. identifier,
  416. Cartesian2.clone(positions.get(identifier))
  417. );
  418. }
  419. }
  420. function handleTouchEnd(screenSpaceEventHandler, event) {
  421. gotTouchEvent(screenSpaceEventHandler);
  422. var changedTouches = event.changedTouches;
  423. var i;
  424. var length = changedTouches.length;
  425. var touch;
  426. var identifier;
  427. var positions = screenSpaceEventHandler._positions;
  428. for (i = 0; i < length; ++i) {
  429. touch = changedTouches[i];
  430. identifier = touch.identifier;
  431. positions.remove(identifier);
  432. }
  433. fireTouchEvents(screenSpaceEventHandler, event);
  434. var previousPositions = screenSpaceEventHandler._previousPositions;
  435. for (i = 0; i < length; ++i) {
  436. touch = changedTouches[i];
  437. identifier = touch.identifier;
  438. previousPositions.remove(identifier);
  439. }
  440. }
  441. var touchStartEvent = {
  442. position: new Cartesian2(),
  443. };
  444. var touch2StartEvent = {
  445. position1: new Cartesian2(),
  446. position2: new Cartesian2(),
  447. };
  448. var touchEndEvent = {
  449. position: new Cartesian2(),
  450. };
  451. var touchClickEvent = {
  452. position: new Cartesian2(),
  453. };
  454. var touchHoldEvent = {
  455. position: new Cartesian2(),
  456. };
  457. function fireTouchEvents(screenSpaceEventHandler, event) {
  458. var modifier = getModifier(event);
  459. var positions = screenSpaceEventHandler._positions;
  460. var numberOfTouches = positions.length;
  461. var action;
  462. var clickAction;
  463. var pinching = screenSpaceEventHandler._isPinching;
  464. if (
  465. numberOfTouches !== 1 &&
  466. screenSpaceEventHandler._buttonDown[MouseButton.LEFT]
  467. ) {
  468. // transitioning from single touch, trigger UP and might trigger CLICK
  469. screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = false;
  470. if (defined(screenSpaceEventHandler._touchHoldTimer)) {
  471. clearTimeout(screenSpaceEventHandler._touchHoldTimer);
  472. screenSpaceEventHandler._touchHoldTimer = undefined;
  473. }
  474. action = screenSpaceEventHandler.getInputAction(
  475. ScreenSpaceEventType.LEFT_UP,
  476. modifier
  477. );
  478. if (defined(action)) {
  479. Cartesian2.clone(
  480. screenSpaceEventHandler._primaryPosition,
  481. touchEndEvent.position
  482. );
  483. action(touchEndEvent);
  484. }
  485. if (numberOfTouches === 0 && !screenSpaceEventHandler._isTouchHolding) {
  486. // releasing single touch, check for CLICK
  487. clickAction = screenSpaceEventHandler.getInputAction(
  488. ScreenSpaceEventType.LEFT_CLICK,
  489. modifier
  490. );
  491. if (defined(clickAction)) {
  492. var startPosition = screenSpaceEventHandler._primaryStartPosition;
  493. var endPosition = screenSpaceEventHandler._previousPositions.values[0];
  494. if (
  495. checkPixelTolerance(
  496. startPosition,
  497. endPosition,
  498. screenSpaceEventHandler._clickPixelTolerance
  499. )
  500. ) {
  501. Cartesian2.clone(
  502. screenSpaceEventHandler._primaryPosition,
  503. touchClickEvent.position
  504. );
  505. clickAction(touchClickEvent);
  506. }
  507. }
  508. }
  509. screenSpaceEventHandler._isTouchHolding = false;
  510. // Otherwise don't trigger CLICK, because we are adding more touches.
  511. }
  512. if (numberOfTouches === 0 && pinching) {
  513. // transitioning from pinch, trigger PINCH_END
  514. screenSpaceEventHandler._isPinching = false;
  515. action = screenSpaceEventHandler.getInputAction(
  516. ScreenSpaceEventType.PINCH_END,
  517. modifier
  518. );
  519. if (defined(action)) {
  520. action();
  521. }
  522. }
  523. if (numberOfTouches === 1 && !pinching) {
  524. // transitioning to single touch, trigger DOWN
  525. var position = positions.values[0];
  526. Cartesian2.clone(position, screenSpaceEventHandler._primaryPosition);
  527. Cartesian2.clone(position, screenSpaceEventHandler._primaryStartPosition);
  528. Cartesian2.clone(
  529. position,
  530. screenSpaceEventHandler._primaryPreviousPosition
  531. );
  532. screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = true;
  533. action = screenSpaceEventHandler.getInputAction(
  534. ScreenSpaceEventType.LEFT_DOWN,
  535. modifier
  536. );
  537. if (defined(action)) {
  538. Cartesian2.clone(position, touchStartEvent.position);
  539. action(touchStartEvent);
  540. }
  541. screenSpaceEventHandler._touchHoldTimer = setTimeout(function () {
  542. if (!screenSpaceEventHandler.isDestroyed()) {
  543. screenSpaceEventHandler._touchHoldTimer = undefined;
  544. screenSpaceEventHandler._isTouchHolding = true;
  545. clickAction = screenSpaceEventHandler.getInputAction(
  546. ScreenSpaceEventType.RIGHT_CLICK,
  547. modifier
  548. );
  549. if (defined(clickAction)) {
  550. var startPosition = screenSpaceEventHandler._primaryStartPosition;
  551. var endPosition =
  552. screenSpaceEventHandler._previousPositions.values[0];
  553. if (
  554. checkPixelTolerance(
  555. startPosition,
  556. endPosition,
  557. screenSpaceEventHandler._holdPixelTolerance
  558. )
  559. ) {
  560. Cartesian2.clone(
  561. screenSpaceEventHandler._primaryPosition,
  562. touchHoldEvent.position
  563. );
  564. clickAction(touchHoldEvent);
  565. }
  566. }
  567. }
  568. }, ScreenSpaceEventHandler.touchHoldDelayMilliseconds);
  569. event.preventDefault();
  570. }
  571. if (numberOfTouches === 2 && !pinching) {
  572. // transitioning to pinch, trigger PINCH_START
  573. screenSpaceEventHandler._isPinching = true;
  574. action = screenSpaceEventHandler.getInputAction(
  575. ScreenSpaceEventType.PINCH_START,
  576. modifier
  577. );
  578. if (defined(action)) {
  579. Cartesian2.clone(positions.values[0], touch2StartEvent.position1);
  580. Cartesian2.clone(positions.values[1], touch2StartEvent.position2);
  581. action(touch2StartEvent);
  582. // Touch-enabled devices, in particular iOS can have many default behaviours for
  583. // "pinch" events, which can still be executed unless we prevent them here.
  584. event.preventDefault();
  585. }
  586. }
  587. }
  588. function handleTouchMove(screenSpaceEventHandler, event) {
  589. gotTouchEvent(screenSpaceEventHandler);
  590. var changedTouches = event.changedTouches;
  591. var i;
  592. var length = changedTouches.length;
  593. var touch;
  594. var identifier;
  595. var positions = screenSpaceEventHandler._positions;
  596. for (i = 0; i < length; ++i) {
  597. touch = changedTouches[i];
  598. identifier = touch.identifier;
  599. var position = positions.get(identifier);
  600. if (defined(position)) {
  601. getPosition(screenSpaceEventHandler, touch, position);
  602. }
  603. }
  604. fireTouchMoveEvents(screenSpaceEventHandler, event);
  605. var previousPositions = screenSpaceEventHandler._previousPositions;
  606. for (i = 0; i < length; ++i) {
  607. touch = changedTouches[i];
  608. identifier = touch.identifier;
  609. Cartesian2.clone(
  610. positions.get(identifier),
  611. previousPositions.get(identifier)
  612. );
  613. }
  614. }
  615. var touchMoveEvent = {
  616. startPosition: new Cartesian2(),
  617. endPosition: new Cartesian2(),
  618. };
  619. var touchPinchMovementEvent = {
  620. distance: {
  621. startPosition: new Cartesian2(),
  622. endPosition: new Cartesian2(),
  623. },
  624. angleAndHeight: {
  625. startPosition: new Cartesian2(),
  626. endPosition: new Cartesian2(),
  627. },
  628. };
  629. function fireTouchMoveEvents(screenSpaceEventHandler, event) {
  630. var modifier = getModifier(event);
  631. var positions = screenSpaceEventHandler._positions;
  632. var previousPositions = screenSpaceEventHandler._previousPositions;
  633. var numberOfTouches = positions.length;
  634. var action;
  635. if (
  636. numberOfTouches === 1 &&
  637. screenSpaceEventHandler._buttonDown[MouseButton.LEFT]
  638. ) {
  639. // moving single touch
  640. var position = positions.values[0];
  641. Cartesian2.clone(position, screenSpaceEventHandler._primaryPosition);
  642. var previousPosition = screenSpaceEventHandler._primaryPreviousPosition;
  643. action = screenSpaceEventHandler.getInputAction(
  644. ScreenSpaceEventType.MOUSE_MOVE,
  645. modifier
  646. );
  647. if (defined(action)) {
  648. Cartesian2.clone(previousPosition, touchMoveEvent.startPosition);
  649. Cartesian2.clone(position, touchMoveEvent.endPosition);
  650. action(touchMoveEvent);
  651. }
  652. Cartesian2.clone(position, previousPosition);
  653. event.preventDefault();
  654. } else if (numberOfTouches === 2 && screenSpaceEventHandler._isPinching) {
  655. // moving pinch
  656. action = screenSpaceEventHandler.getInputAction(
  657. ScreenSpaceEventType.PINCH_MOVE,
  658. modifier
  659. );
  660. if (defined(action)) {
  661. var position1 = positions.values[0];
  662. var position2 = positions.values[1];
  663. var previousPosition1 = previousPositions.values[0];
  664. var previousPosition2 = previousPositions.values[1];
  665. var dX = position2.x - position1.x;
  666. var dY = position2.y - position1.y;
  667. var dist = Math.sqrt(dX * dX + dY * dY) * 0.25;
  668. var prevDX = previousPosition2.x - previousPosition1.x;
  669. var prevDY = previousPosition2.y - previousPosition1.y;
  670. var prevDist = Math.sqrt(prevDX * prevDX + prevDY * prevDY) * 0.25;
  671. var cY = (position2.y + position1.y) * 0.125;
  672. var prevCY = (previousPosition2.y + previousPosition1.y) * 0.125;
  673. var angle = Math.atan2(dY, dX);
  674. var prevAngle = Math.atan2(prevDY, prevDX);
  675. Cartesian2.fromElements(
  676. 0.0,
  677. prevDist,
  678. touchPinchMovementEvent.distance.startPosition
  679. );
  680. Cartesian2.fromElements(
  681. 0.0,
  682. dist,
  683. touchPinchMovementEvent.distance.endPosition
  684. );
  685. Cartesian2.fromElements(
  686. prevAngle,
  687. prevCY,
  688. touchPinchMovementEvent.angleAndHeight.startPosition
  689. );
  690. Cartesian2.fromElements(
  691. angle,
  692. cY,
  693. touchPinchMovementEvent.angleAndHeight.endPosition
  694. );
  695. action(touchPinchMovementEvent);
  696. }
  697. }
  698. }
  699. function handlePointerDown(screenSpaceEventHandler, event) {
  700. event.target.setPointerCapture(event.pointerId);
  701. if (event.pointerType === "touch") {
  702. var positions = screenSpaceEventHandler._positions;
  703. var identifier = event.pointerId;
  704. positions.set(
  705. identifier,
  706. getPosition(screenSpaceEventHandler, event, new Cartesian2())
  707. );
  708. fireTouchEvents(screenSpaceEventHandler, event);
  709. var previousPositions = screenSpaceEventHandler._previousPositions;
  710. previousPositions.set(
  711. identifier,
  712. Cartesian2.clone(positions.get(identifier))
  713. );
  714. } else {
  715. handleMouseDown(screenSpaceEventHandler, event);
  716. }
  717. }
  718. function handlePointerUp(screenSpaceEventHandler, event) {
  719. if (event.pointerType === "touch") {
  720. var positions = screenSpaceEventHandler._positions;
  721. var identifier = event.pointerId;
  722. positions.remove(identifier);
  723. fireTouchEvents(screenSpaceEventHandler, event);
  724. var previousPositions = screenSpaceEventHandler._previousPositions;
  725. previousPositions.remove(identifier);
  726. } else {
  727. handleMouseUp(screenSpaceEventHandler, event);
  728. }
  729. }
  730. function handlePointerMove(screenSpaceEventHandler, event) {
  731. if (event.pointerType === "touch") {
  732. var positions = screenSpaceEventHandler._positions;
  733. var identifier = event.pointerId;
  734. var position = positions.get(identifier);
  735. if (!defined(position)) {
  736. return;
  737. }
  738. getPosition(screenSpaceEventHandler, event, position);
  739. fireTouchMoveEvents(screenSpaceEventHandler, event);
  740. var previousPositions = screenSpaceEventHandler._previousPositions;
  741. Cartesian2.clone(
  742. positions.get(identifier),
  743. previousPositions.get(identifier)
  744. );
  745. } else {
  746. handleMouseMove(screenSpaceEventHandler, event);
  747. }
  748. }
  749. /**
  750. * Handles user input events. Custom functions can be added to be executed on
  751. * when the user enters input.
  752. *
  753. * @alias ScreenSpaceEventHandler
  754. *
  755. * @param {HTMLCanvasElement} [element=document] The element to add events to.
  756. *
  757. * @constructor
  758. */
  759. function ScreenSpaceEventHandler(element) {
  760. this._inputEvents = {};
  761. this._buttonDown = {
  762. LEFT: false,
  763. MIDDLE: false,
  764. RIGHT: false,
  765. };
  766. this._isPinching = false;
  767. this._isTouchHolding = false;
  768. this._lastSeenTouchEvent = -ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds;
  769. this._primaryStartPosition = new Cartesian2();
  770. this._primaryPosition = new Cartesian2();
  771. this._primaryPreviousPosition = new Cartesian2();
  772. this._positions = new AssociativeArray();
  773. this._previousPositions = new AssociativeArray();
  774. this._removalFunctions = [];
  775. this._touchHoldTimer = undefined;
  776. // TODO: Revisit when doing mobile development. May need to be configurable
  777. // or determined based on the platform?
  778. this._clickPixelTolerance = 5;
  779. this._holdPixelTolerance = 25;
  780. this._element = defaultValue(element, document);
  781. registerListeners(this);
  782. }
  783. /**
  784. * Set a function to be executed on an input event.
  785. *
  786. * @param {Function} action Function to be executed when the input event occurs.
  787. * @param {Number} type The ScreenSpaceEventType of input event.
  788. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a <code>type</code>
  789. * event occurs.
  790. *
  791. * @see ScreenSpaceEventHandler#getInputAction
  792. * @see ScreenSpaceEventHandler#removeInputAction
  793. */
  794. ScreenSpaceEventHandler.prototype.setInputAction = function (
  795. action,
  796. type,
  797. modifier
  798. ) {
  799. //>>includeStart('debug', pragmas.debug);
  800. if (!defined(action)) {
  801. throw new DeveloperError("action is required.");
  802. }
  803. if (!defined(type)) {
  804. throw new DeveloperError("type is required.");
  805. }
  806. //>>includeEnd('debug');
  807. var key = getInputEventKey(type, modifier);
  808. this._inputEvents[key] = action;
  809. };
  810. /**
  811. * Returns the function to be executed on an input event.
  812. *
  813. * @param {Number} type The ScreenSpaceEventType of input event.
  814. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a <code>type</code>
  815. * event occurs.
  816. *
  817. * @returns {Function} The function to be executed on an input event.
  818. *
  819. * @see ScreenSpaceEventHandler#setInputAction
  820. * @see ScreenSpaceEventHandler#removeInputAction
  821. */
  822. ScreenSpaceEventHandler.prototype.getInputAction = function (type, modifier) {
  823. //>>includeStart('debug', pragmas.debug);
  824. if (!defined(type)) {
  825. throw new DeveloperError("type is required.");
  826. }
  827. //>>includeEnd('debug');
  828. var key = getInputEventKey(type, modifier);
  829. return this._inputEvents[key];
  830. };
  831. /**
  832. * Removes the function to be executed on an input event.
  833. *
  834. * @param {Number} type The ScreenSpaceEventType of input event.
  835. * @param {Number} [modifier] A KeyboardEventModifier key that is held when a <code>type</code>
  836. * event occurs.
  837. *
  838. * @see ScreenSpaceEventHandler#getInputAction
  839. * @see ScreenSpaceEventHandler#setInputAction
  840. */
  841. ScreenSpaceEventHandler.prototype.removeInputAction = function (
  842. type,
  843. modifier
  844. ) {
  845. //>>includeStart('debug', pragmas.debug);
  846. if (!defined(type)) {
  847. throw new DeveloperError("type is required.");
  848. }
  849. //>>includeEnd('debug');
  850. var key = getInputEventKey(type, modifier);
  851. delete this._inputEvents[key];
  852. };
  853. /**
  854. * Returns true if this object was destroyed; otherwise, false.
  855. * <br /><br />
  856. * If this object was destroyed, it should not be used; calling any function other than
  857. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
  858. *
  859. * @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
  860. *
  861. * @see ScreenSpaceEventHandler#destroy
  862. */
  863. ScreenSpaceEventHandler.prototype.isDestroyed = function () {
  864. return false;
  865. };
  866. /**
  867. * Removes listeners held by this object.
  868. * <br /><br />
  869. * Once an object is destroyed, it should not be used; calling any function other than
  870. * <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
  871. * assign the return value (<code>undefined</code>) to the object as done in the example.
  872. *
  873. * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
  874. *
  875. *
  876. * @example
  877. * handler = handler && handler.destroy();
  878. *
  879. * @see ScreenSpaceEventHandler#isDestroyed
  880. */
  881. ScreenSpaceEventHandler.prototype.destroy = function () {
  882. unregisterListeners(this);
  883. return destroyObject(this);
  884. };
  885. /**
  886. * The amount of time, in milliseconds, that mouse events will be disabled after
  887. * receiving any touch events, such that any emulated mouse events will be ignored.
  888. * @type {Number}
  889. * @default 800
  890. */
  891. ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds = 800;
  892. /**
  893. * The amount of time, in milliseconds, before a touch on the screen becomes a
  894. * touch and hold.
  895. * @type {Number}
  896. * @default 1500
  897. */
  898. ScreenSpaceEventHandler.touchHoldDelayMilliseconds = 1500;
  899. export default ScreenSpaceEventHandler;