From 362242de47fffb43a5e75dce82dd34ae3954a7d0 Mon Sep 17 00:00:00 2001 From: Adrien David <16332780+adridavid@users.noreply.github.com> Date: Mon, 26 Nov 2018 17:09:42 +0100 Subject: [PATCH 1/7] Handle touch and hold gesture --- Source/Core/ScreenSpaceEventHandler.js | 58 ++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/Source/Core/ScreenSpaceEventHandler.js b/Source/Core/ScreenSpaceEventHandler.js index 5e92cf1bc3b9..7bb7e66864de 100644 --- a/Source/Core/ScreenSpaceEventHandler.js +++ b/Source/Core/ScreenSpaceEventHandler.js @@ -133,6 +133,16 @@ define([ return (getTimestamp() - screenSpaceEventHandler._lastSeenTouchEvent) > ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds; } + function checkClickPixelTolerance(screenSpaceEventHandler) { + var startPosition = screenSpaceEventHandler._primaryStartPosition; + var endPosition = screenSpaceEventHandler._previousPositions.values[0]; + var xDiff = startPosition.x - endPosition.x; + var yDiff = startPosition.y - endPosition.y; + var totalPixels = Math.sqrt(xDiff * xDiff + yDiff * yDiff); + + return totalPixels < screenSpaceEventHandler._clickPixelTolerance; + } + function handleMouseDown(screenSpaceEventHandler, event) { if (!canProcessMouseEvent(screenSpaceEventHandler)) { return; @@ -393,11 +403,13 @@ define([ var touchClickEvent = { position : new Cartesian2() }; + var touchHoldEvent = { + position : new Cartesian2() + }; function fireTouchEvents(screenSpaceEventHandler, event) { var modifier = getModifier(event); var positions = screenSpaceEventHandler._positions; - var previousPositions = screenSpaceEventHandler._previousPositions; var numberOfTouches = positions.length; var action; var clickAction; @@ -406,6 +418,12 @@ define([ if (numberOfTouches !== 1 && screenSpaceEventHandler._buttonDown === MouseButton.LEFT) { // transitioning from single touch, trigger UP and might trigger CLICK screenSpaceEventHandler._buttonDown = undefined; + + if(defined(screenSpaceEventHandler._touchHoldTimer)) { + clearTimeout(screenSpaceEventHandler._touchHoldTimer); + screenSpaceEventHandler._touchHoldTimer = undefined; + } + action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.LEFT_UP, modifier); if (defined(action)) { @@ -414,18 +432,12 @@ define([ action(touchEndEvent); } - if (numberOfTouches === 0) { + if (numberOfTouches === 0 && !screenSpaceEventHandler._isTouchHolding) { // releasing single touch, check for CLICK clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.LEFT_CLICK, modifier); if (defined(clickAction)) { - var startPosition = screenSpaceEventHandler._primaryStartPosition; - var endPosition = previousPositions.values[0]; - var xDiff = startPosition.x - endPosition.x; - var yDiff = startPosition.y - endPosition.y; - var totalPixels = Math.sqrt(xDiff * xDiff + yDiff * yDiff); - - if (totalPixels < screenSpaceEventHandler._clickPixelTolerance) { + if(checkClickPixelTolerance(screenSpaceEventHandler)) { Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchClickEvent.position); clickAction(touchClickEvent); @@ -433,6 +445,8 @@ define([ } } + screenSpaceEventHandler._isTouchHolding = false; + // Otherwise don't trigger CLICK, because we are adding more touches. } @@ -464,6 +478,21 @@ define([ action(touchStartEvent); } + screenSpaceEventHandler._touchHoldTimer = setTimeout(function() { + screenSpaceEventHandler._touchHoldTimer = undefined; + screenSpaceEventHandler._isTouchHolding = true; + + clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.RIGHT_CLICK, modifier); + + if (defined(clickAction)) { + if(checkClickPixelTolerance(screenSpaceEventHandler)) { + Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchHoldEvent.position); + + clickAction(touchHoldEvent); + } + } + }, ScreenSpaceEventHandler.touchHoldDelayMilliseconds); + event.preventDefault(); } @@ -660,6 +689,7 @@ define([ this._inputEvents = {}; this._buttonDown = undefined; this._isPinching = false; + this._isTouchHolding = false; this._lastSeenTouchEvent = -ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds; this._primaryStartPosition = new Cartesian2(); @@ -671,6 +701,8 @@ define([ this._removalFunctions = []; + this._touchHoldTimer = undefined; + // TODO: Revisit when doing mobile development. May need to be configurable // or determined based on the platform? this._clickPixelTolerance = 5; @@ -790,5 +822,13 @@ define([ */ ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds = 800; + /** + * The amount of time, in milliseconds, before a touch on the screen becomes a + * touch and hold. + * @type {Number} + * @default 1500 + */ + ScreenSpaceEventHandler.touchHoldDelayMilliseconds = 1500; + return ScreenSpaceEventHandler; }); From 37475cd1a8a50b8b4367f231a3e33cff012a54b9 Mon Sep 17 00:00:00 2001 From: Adrien David <16332780+adridavid@users.noreply.github.com> Date: Mon, 26 Nov 2018 17:42:05 +0100 Subject: [PATCH 2/7] Add spec for touch and hold event --- Specs/Core/ScreenSpaceEventHandlerSpec.js | 83 +++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/Specs/Core/ScreenSpaceEventHandlerSpec.js b/Specs/Core/ScreenSpaceEventHandlerSpec.js index 3b5766bf58c9..ebaeb29c6799 100644 --- a/Specs/Core/ScreenSpaceEventHandlerSpec.js +++ b/Specs/Core/ScreenSpaceEventHandlerSpec.js @@ -1207,6 +1207,89 @@ defineSuite([ expect(action).not.toHaveBeenCalled(); }); + it('handles touch and hold gesture', function() { + jasmine.clock().install(); + + var delay = ScreenSpaceEventHandler.touchHoldDelayMilliseconds; + + var eventType = ScreenSpaceEventType.RIGHT_CLICK; + + var action = createCloningSpy('action'); + handler.setInputAction(action, eventType); + + expect(handler.getInputAction(eventType)).toEqual(action); + + // start, then end + function simulateInput(timeout) { + var touchStartPosition = { + clientX : 1, + clientY : 2 + }; + var touchEndPosition = { + clientX : 1, + clientY : 2 + }; + + if (usePointerEvents) { + DomEventSimulator.firePointerDown(element, combine({ + pointerType : 'touch', + pointerId : 1 + }, touchStartPosition)); + jasmine.clock().tick(timeout); + DomEventSimulator.firePointerUp(element, combine({ + pointerType : 'touch', + pointerId : 1 + }, touchEndPosition)); + } else { + DomEventSimulator.fireTouchStart(element, { + changedTouches : [combine({ + identifier : 0 + }, touchStartPosition)] + }); + jasmine.clock().tick(timeout); + DomEventSimulator.fireTouchEnd(element, { + changedTouches : [combine({ + identifier : 0 + }, touchEndPosition)] + }); + } + } + + simulateInput(delay + 1); + + expect(action.calls.count()).toEqual(1); + expect(action).toHaveBeenCalledWith({ + position : new Cartesian2(1, 2) + }); + + // Should not be fired if hold delay is less than touchHoldDelayMilliseconds. + action.calls.reset(); + + simulateInput(delay - 1); + + expect(action).not.toHaveBeenCalled(); + + // Should not be fired after removal. + action.calls.reset(); + + handler.removeInputAction(eventType); + + simulateInput(delay + 1); + + expect(action).not.toHaveBeenCalled(); + + // Should not fire click action if touch and hold is triggered. + eventType = ScreenSpaceEventType.LEFT_CLICK; + + handler.setInputAction(action, eventType); + + simulateInput(delay + 1); + + expect(action).not.toHaveBeenCalled(); + + jasmine.clock().uninstall(); + }); + it('treats touch cancel as touch end for touch clicks', function() { var eventType = ScreenSpaceEventType.LEFT_CLICK; From 57cb630c77bb614490f4a63b00c148eb7c914c3e Mon Sep 17 00:00:00 2001 From: Adrien David <16332780+adridavid@users.noreply.github.com> Date: Mon, 26 Nov 2018 17:57:22 +0100 Subject: [PATCH 3/7] Check event handler has not been destroyed --- Source/Core/ScreenSpaceEventHandler.js | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Core/ScreenSpaceEventHandler.js b/Source/Core/ScreenSpaceEventHandler.js index 7bb7e66864de..93593749ed8b 100644 --- a/Source/Core/ScreenSpaceEventHandler.js +++ b/Source/Core/ScreenSpaceEventHandler.js @@ -479,16 +479,18 @@ define([ } screenSpaceEventHandler._touchHoldTimer = setTimeout(function() { - screenSpaceEventHandler._touchHoldTimer = undefined; - screenSpaceEventHandler._isTouchHolding = true; + if(!screenSpaceEventHandler.isDestroyed()) { + screenSpaceEventHandler._touchHoldTimer = undefined; + screenSpaceEventHandler._isTouchHolding = true; - clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.RIGHT_CLICK, modifier); + clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.RIGHT_CLICK, modifier); - if (defined(clickAction)) { - if(checkClickPixelTolerance(screenSpaceEventHandler)) { - Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchHoldEvent.position); + if (defined(clickAction)) { + if(checkClickPixelTolerance(screenSpaceEventHandler)) { + Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchHoldEvent.position); - clickAction(touchHoldEvent); + clickAction(touchHoldEvent); + } } } }, ScreenSpaceEventHandler.touchHoldDelayMilliseconds); From b02a5f9dd4b365d83c7709673b764c2480699280 Mon Sep 17 00:00:00 2001 From: Adrien David <16332780+adridavid@users.noreply.github.com> Date: Mon, 26 Nov 2018 20:39:51 +0100 Subject: [PATCH 4/7] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index 33a24e094fee..961381ea165d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Change Log * Added `computeLineSegmentLineSegmentIntersection` to `Intersections2D`. [#7228](https://github.com/AnalyticalGraphicsInc/Cesium/pull/7228) * Added ability to load availability progressively from a quantized mesh extension instead of upfront. This will speed up load time and reduce memory usage. [#7196](https://github.com/AnalyticalGraphicsInc/cesium/pull/7196) * Added the ability to apply styles to 3D Tilesets that don't contain features. [#7255](https://github.com/AnalyticalGraphicsInc/Cesium/pull/7255) +* Added support for touch and hold gesture. The touch and hold delay can be customized by updating `ScreenSpaceEventHandler.touchHoldDelayMilliseconds`. [#7286](https://github.com/AnalyticalGraphicsInc/cesium/pull/7286) ##### Fixes :wrench: * Fixed issue causing polyline to look wavy depending on the position of the camera [#7209](https://github.com/AnalyticalGraphicsInc/cesium/pull/7209) From fce28db1c77c93bc8ddd6742899bf5a3725f8a4f Mon Sep 17 00:00:00 2001 From: Adrien David <16332780+adridavid@users.noreply.github.com> Date: Fri, 4 Jan 2019 22:30:38 +0100 Subject: [PATCH 5/7] Update CHANGES.md --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 965abfb82029..e57a548dd665 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -15,6 +15,7 @@ Change Log * Added support for polylines on 3D Tiles. [#7437](https://github.com/AnalyticalGraphicsInc/cesium/pull/7437) * Added `classificationType` property to `PolylineGraphics` and `GroundPolylinePrimitive` which specifies whether a polyline clamped to ground should be clamped to terrain, 3D Tiles, or both. [#7437](https://github.com/AnalyticalGraphicsInc/cesium/pull/7437) * Added the ability to specify the width of the intersection volume for `Scene.sampleHeight`, `Scene.clampToHeight`, `Scene.sampleHeightMostDetailed`, and `Scene.clampToHeightMostDetailed`. [#7287](https://github.com/AnalyticalGraphicsInc/cesium/pull/7287) +* Added support for touch and hold gesture. The touch and hold delay can be customized by updating `ScreenSpaceEventHandler.touchHoldDelayMilliseconds`. [#7286](https://github.com/AnalyticalGraphicsInc/cesium/pull/7286) ##### Fixes :wrench: * Fixed an issue where classification primitives with the `CESIUM_3D_TILE` classification type would render on terrain. [#7422](https://github.com/AnalyticalGraphicsInc/cesium/pull/7422) @@ -55,7 +56,6 @@ Change Log * Added `computeLineSegmentLineSegmentIntersection` to `Intersections2D`. [#7228](https://github.com/AnalyticalGraphicsInc/Cesium/pull/7228) * Added ability to load availability progressively from a quantized mesh extension instead of upfront. This will speed up load time and reduce memory usage. [#7196](https://github.com/AnalyticalGraphicsInc/cesium/pull/7196) * Added the ability to apply styles to 3D Tilesets that don't contain features. [#7255](https://github.com/AnalyticalGraphicsInc/Cesium/pull/7255) -* Added support for touch and hold gesture. The touch and hold delay can be customized by updating `ScreenSpaceEventHandler.touchHoldDelayMilliseconds`. [#7286](https://github.com/AnalyticalGraphicsInc/cesium/pull/7286) ##### Fixes :wrench: * Fixed issue causing polyline to look wavy depending on the position of the camera [#7209](https://github.com/AnalyticalGraphicsInc/cesium/pull/7209) From 72cd1e799bc09cf24bbda2919b6a087fa2fbfad5 Mon Sep 17 00:00:00 2001 From: Adrien David <16332780+adridavid@users.noreply.github.com> Date: Sat, 9 Feb 2019 13:11:08 +0100 Subject: [PATCH 6/7] Update CHANGES.md --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index d36067da186d..3b6b9523c309 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ Change Log ##### Additions :tada: * `czm_materialInput.aspect` was added as an angle in radians between 0 and 2pi (east, north, west to south). +* Added support for touch and hold gesture. The touch and hold delay can be customized by updating `ScreenSpaceEventHandler.touchHoldDelayMilliseconds`. [#7286](https://github.com/AnalyticalGraphicsInc/cesium/pull/7286) ##### Fixes :wrench: * Fixed an issue where models would cause a crash on load if some primitives were Draco encoded and others were not. [#7383](https://github.com/AnalyticalGraphicsInc/cesium/issues/7383) From 2e93362fa747c00900abd0a505affdd5d3a83f83 Mon Sep 17 00:00:00 2001 From: Adrien David <16332780+adridavid@users.noreply.github.com> Date: Fri, 15 Feb 2019 19:15:30 +0100 Subject: [PATCH 7/7] Add higher pixel tolerance for touch and hold --- Source/Core/ScreenSpaceEventHandler.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Source/Core/ScreenSpaceEventHandler.js b/Source/Core/ScreenSpaceEventHandler.js index 05a5f8c9b1aa..2b539365173a 100644 --- a/Source/Core/ScreenSpaceEventHandler.js +++ b/Source/Core/ScreenSpaceEventHandler.js @@ -133,14 +133,12 @@ define([ return (getTimestamp() - screenSpaceEventHandler._lastSeenTouchEvent) > ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds; } - function checkClickPixelTolerance(screenSpaceEventHandler) { - var startPosition = screenSpaceEventHandler._primaryStartPosition; - var endPosition = screenSpaceEventHandler._previousPositions.values[0]; + function checkPixelTolerance(startPosition, endPosition, pixelTolerance) { var xDiff = startPosition.x - endPosition.x; var yDiff = startPosition.y - endPosition.y; var totalPixels = Math.sqrt(xDiff * xDiff + yDiff * yDiff); - return totalPixels < screenSpaceEventHandler._clickPixelTolerance; + return totalPixels < pixelTolerance; } function handleMouseDown(screenSpaceEventHandler, event) { @@ -203,11 +201,7 @@ define([ if (defined(clickAction)) { var startPosition = screenSpaceEventHandler._primaryStartPosition; - var xDiff = startPosition.x - position.x; - var yDiff = startPosition.y - position.y; - var totalPixels = Math.sqrt(xDiff * xDiff + yDiff * yDiff); - - if (totalPixels < screenSpaceEventHandler._clickPixelTolerance) { + if (checkPixelTolerance(startPosition, position, screenSpaceEventHandler._clickPixelTolerance)) { Cartesian2.clone(position, mouseClickEvent.position); clickAction(mouseClickEvent); @@ -442,7 +436,9 @@ define([ clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.LEFT_CLICK, modifier); if (defined(clickAction)) { - if(checkClickPixelTolerance(screenSpaceEventHandler)) { + var startPosition = screenSpaceEventHandler._primaryStartPosition; + var endPosition = screenSpaceEventHandler._previousPositions.values[0]; + if(checkPixelTolerance(startPosition, endPosition, screenSpaceEventHandler._clickPixelTolerance)) { Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchClickEvent.position); clickAction(touchClickEvent); @@ -491,7 +487,9 @@ define([ clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.RIGHT_CLICK, modifier); if (defined(clickAction)) { - if(checkClickPixelTolerance(screenSpaceEventHandler)) { + var startPosition = screenSpaceEventHandler._primaryStartPosition; + var endPosition = screenSpaceEventHandler._previousPositions.values[0]; + if(checkPixelTolerance(startPosition, endPosition, screenSpaceEventHandler._holdPixelTolerance)) { Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchHoldEvent.position); clickAction(touchHoldEvent); @@ -717,6 +715,7 @@ define([ // TODO: Revisit when doing mobile development. May need to be configurable // or determined based on the platform? this._clickPixelTolerance = 5; + this._holdPixelTolerance = 25; this._element = defaultValue(element, document);