Reference Source

src/viewer/scene/CameraControl/lib/handlers/TouchPickHandler.js

  1. import {math} from "../../../math/math.js";
  2.  
  3. const TAP_INTERVAL = 150;
  4. const DBL_TAP_INTERVAL = 325;
  5. const TAP_DISTANCE_THRESHOLD = 4;
  6.  
  7. const getCanvasPosFromEvent = function (event, canvasPos) {
  8. if (!event) {
  9. event = window.event;
  10. canvasPos[0] = event.x;
  11. canvasPos[1] = event.y;
  12. } else {
  13. let element = event.target;
  14. let totalOffsetLeft = 0;
  15. let totalOffsetTop = 0;
  16. while (element.offsetParent) {
  17. totalOffsetLeft += element.offsetLeft;
  18. totalOffsetTop += element.offsetTop;
  19. element = element.offsetParent;
  20. }
  21. canvasPos[0] = event.pageX - totalOffsetLeft;
  22. canvasPos[1] = event.pageY - totalOffsetTop;
  23. }
  24. return canvasPos;
  25. };
  26.  
  27. /**
  28. * @private
  29. */
  30. class TouchPickHandler {
  31.  
  32. constructor(scene, controllers, configs, states, updates) {
  33.  
  34. this._scene = scene;
  35.  
  36. const pickController = controllers.pickController;
  37. const cameraControl = controllers.cameraControl;
  38.  
  39. let touchStartTime;
  40. const activeTouches = [];
  41. const tapStartPos = new Float32Array(2);
  42. let tapStartTime = -1;
  43. let lastTapTime = -1;
  44.  
  45. const canvas = this._scene.canvas.canvas;
  46.  
  47. const flyCameraTo = (pickResult) => {
  48. let pos;
  49. if (pickResult && pickResult.worldPos) {
  50. pos = pickResult.worldPos
  51. }
  52. const aabb = pickResult ? pickResult.entity.aabb : scene.aabb;
  53. if (pos) { // Fly to look at point, don't change eye->look dist
  54. const camera = scene.camera;
  55. const diff = math.subVec3(camera.eye, camera.look, []);
  56. controllers.cameraFlight.flyTo({
  57. aabb: aabb
  58. });
  59. // TODO: Option to back off to fit AABB in view
  60. } else {// Fly to fit target boundary in view
  61. controllers.cameraFlight.flyTo({
  62. aabb: aabb
  63. });
  64. }
  65. };
  66.  
  67. canvas.addEventListener("touchstart", this._canvasTouchStartHandler = (e) => {
  68.  
  69. if (!(configs.active && configs.pointerEnabled)) {
  70. return;
  71. }
  72.  
  73. if (states.longTouchTimeout !== null) {
  74. clearTimeout(states.longTouchTimeout);
  75. states.longTouchTimeout = null;
  76. }
  77.  
  78. const touches = e.touches;
  79. const changedTouches = e.changedTouches;
  80.  
  81. touchStartTime = Date.now();
  82.  
  83. if (touches.length === 1 && changedTouches.length === 1) {
  84. tapStartTime = touchStartTime;
  85.  
  86. getCanvasPosFromEvent(touches[0], tapStartPos);
  87.  
  88. const rightClickClientX = tapStartPos[0];
  89. const rightClickClientY = tapStartPos[1];
  90.  
  91. const rightClickPageX = touches[0].pageX;
  92. const rightClickPageY = touches[0].pageY;
  93.  
  94. states.longTouchTimeout = setTimeout(() => {
  95. controllers.cameraControl.fire("rightClick", { // For context menus
  96. pagePos: [Math.round(rightClickPageX), Math.round(rightClickPageY)],
  97. canvasPos: [Math.round(rightClickClientX), Math.round(rightClickClientY)],
  98. event: e
  99. }, true);
  100.  
  101. states.longTouchTimeout = null;
  102. }, configs.longTapTimeout);
  103.  
  104. } else {
  105. tapStartTime = -1;
  106. }
  107.  
  108. while (activeTouches.length < touches.length) {
  109. activeTouches.push(new Float32Array(2))
  110. }
  111.  
  112. for (let i = 0, len = touches.length; i < len; ++i) {
  113. getCanvasPosFromEvent(touches[i], activeTouches[i]);
  114. }
  115.  
  116. activeTouches.length = touches.length;
  117.  
  118. }, {passive: true});
  119.  
  120.  
  121. canvas.addEventListener("touchend", this._canvasTouchEndHandler = (e) => {
  122.  
  123. if (!(configs.active && configs.pointerEnabled)) {
  124. return;
  125. }
  126.  
  127. const currentTime = Date.now();
  128. const touches = e.touches;
  129. const changedTouches = e.changedTouches;
  130.  
  131. const pickedSurfaceSubs = cameraControl.hasSubs("pickedSurface");
  132.  
  133. if (states.longTouchTimeout !== null) {
  134. clearTimeout(states.longTouchTimeout);
  135. states.longTouchTimeout = null;
  136. }
  137.  
  138. // process tap
  139.  
  140. if (touches.length === 0 && changedTouches.length === 1) {
  141.  
  142. if (tapStartTime > -1 && currentTime - tapStartTime < TAP_INTERVAL) {
  143.  
  144. if (lastTapTime > -1 && tapStartTime - lastTapTime < DBL_TAP_INTERVAL) {
  145.  
  146. // Double-tap
  147.  
  148. getCanvasPosFromEvent(changedTouches[0], pickController.pickCursorPos);
  149. pickController.schedulePickEntity = true;
  150. pickController.schedulePickSurface = pickedSurfaceSubs;
  151.  
  152. pickController.update();
  153.  
  154. if (pickController.pickResult) {
  155.  
  156. pickController.pickResult.touchInput = true;
  157.  
  158. cameraControl.fire("doublePicked", pickController.pickResult);
  159.  
  160. if (pickController.pickedSurface) {
  161. cameraControl.fire("doublePickedSurface", pickController.pickResult);
  162. }
  163.  
  164. if (configs.doublePickFlyTo) {
  165. flyCameraTo(pickController.pickResult);
  166. }
  167. } else {
  168. cameraControl.fire("doublePickedNothing");
  169. if (configs.doublePickFlyTo) {
  170. flyCameraTo();
  171. }
  172. }
  173.  
  174. lastTapTime = -1;
  175.  
  176. } else if (math.distVec2(activeTouches[0], tapStartPos) < TAP_DISTANCE_THRESHOLD) {
  177.  
  178. // Single-tap
  179.  
  180. getCanvasPosFromEvent(changedTouches[0], pickController.pickCursorPos);
  181. pickController.schedulePickEntity = true;
  182. pickController.schedulePickSurface = pickedSurfaceSubs;
  183.  
  184. pickController.update();
  185.  
  186. if (pickController.pickResult) {
  187.  
  188. pickController.pickResult.touchInput = true;
  189.  
  190. cameraControl.fire("picked", pickController.pickResult);
  191.  
  192. if (pickController.pickedSurface) {
  193. cameraControl.fire("pickedSurface", pickController.pickResult);
  194. }
  195.  
  196. } else {
  197. cameraControl.fire("pickedNothing");
  198. }
  199.  
  200. lastTapTime = currentTime;
  201. }
  202.  
  203. tapStartTime = -1
  204. }
  205. }
  206.  
  207. activeTouches.length = touches.length;
  208.  
  209. for (let i = 0, len = touches.length; i < len; ++i) {
  210. activeTouches[i][0] = touches[i].pageX;
  211. activeTouches[i][1] = touches[i].pageY;
  212. }
  213.  
  214. // e.stopPropagation();
  215.  
  216. }, {passive: true});
  217.  
  218. }
  219.  
  220. reset() {
  221. // TODO
  222. // tapStartTime = -1;
  223. // lastTapTime = -1;
  224.  
  225. }
  226.  
  227. destroy() {
  228. const canvas = this._scene.canvas.canvas;
  229. canvas.removeEventListener("touchstart", this._canvasTouchStartHandler);
  230. canvas.removeEventListener("touchend", this._canvasTouchEndHandler);
  231. }
  232. }
  233.  
  234.  
  235. export {TouchPickHandler};