Skip to content

Commit

Permalink
Merge pull request #7286 from adridavid/touch-events
Browse files Browse the repository at this point in the history
Add support for touch and hold gesture
  • Loading branch information
emackey authored Mar 20, 2019
2 parents 464d2cd + 5ad60e2 commit ead9f36
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 13 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Change Log
##### Additions :tada:
* `Resource.fetchImage` now has a `flipY` option to vertically flip an image during fetch & decode. It is only valid when `ImageBitmapOptions` is supported by the browser. [#7579](https://github.com/AnalyticalGraphicsInc/cesium/pull/7579)
* Added `backFaceCulling` and `normalShading` options to `PointCloudShading`. Both options are only applicable for point clouds containing normals. [#7399](https://github.com/AnalyticalGraphicsInc/cesium/pull/7399)
* 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 the value for `BlendFunction.ONE_MINUS_CONSTANT_COLOR`. [#7624](https://github.com/AnalyticalGraphicsInc/cesium/pull/7624)
Expand Down
67 changes: 54 additions & 13 deletions Source/Core/ScreenSpaceEventHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,14 @@ define([
return (getTimestamp() - screenSpaceEventHandler._lastSeenTouchEvent) > ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds;
}

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 < pixelTolerance;
}

function handleMouseDown(screenSpaceEventHandler, event) {
if (!canProcessMouseEvent(screenSpaceEventHandler)) {
return;
Expand Down Expand Up @@ -193,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);
Expand Down Expand Up @@ -398,11 +402,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;
Expand All @@ -411,6 +417,12 @@ define([
if (numberOfTouches !== 1 && screenSpaceEventHandler._buttonDown[MouseButton.LEFT]) {
// transitioning from single touch, trigger UP and might trigger CLICK
screenSpaceEventHandler._buttonDown[MouseButton.LEFT] = false;

if(defined(screenSpaceEventHandler._touchHoldTimer)) {
clearTimeout(screenSpaceEventHandler._touchHoldTimer);
screenSpaceEventHandler._touchHoldTimer = undefined;
}

action = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.LEFT_UP, modifier);

if (defined(action)) {
Expand All @@ -419,25 +431,23 @@ 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) {
var endPosition = screenSpaceEventHandler._previousPositions.values[0];
if(checkPixelTolerance(startPosition, endPosition, screenSpaceEventHandler._clickPixelTolerance)) {
Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchClickEvent.position);

clickAction(touchClickEvent);
}
}
}

screenSpaceEventHandler._isTouchHolding = false;

// Otherwise don't trigger CLICK, because we are adding more touches.
}

Expand Down Expand Up @@ -469,6 +479,25 @@ define([
action(touchStartEvent);
}

screenSpaceEventHandler._touchHoldTimer = setTimeout(function() {
if(!screenSpaceEventHandler.isDestroyed()) {
screenSpaceEventHandler._touchHoldTimer = undefined;
screenSpaceEventHandler._isTouchHolding = true;

clickAction = screenSpaceEventHandler.getInputAction(ScreenSpaceEventType.RIGHT_CLICK, modifier);

if (defined(clickAction)) {
var startPosition = screenSpaceEventHandler._primaryStartPosition;
var endPosition = screenSpaceEventHandler._previousPositions.values[0];
if(checkPixelTolerance(startPosition, endPosition, screenSpaceEventHandler._holdPixelTolerance)) {
Cartesian2.clone(screenSpaceEventHandler._primaryPosition, touchHoldEvent.position);

clickAction(touchHoldEvent);
}
}
}
}, ScreenSpaceEventHandler.touchHoldDelayMilliseconds);

event.preventDefault();
}

Expand Down Expand Up @@ -669,6 +698,7 @@ define([
RIGHT: false
};
this._isPinching = false;
this._isTouchHolding = false;
this._lastSeenTouchEvent = -ScreenSpaceEventHandler.mouseEmulationIgnoreMilliseconds;

this._primaryStartPosition = new Cartesian2();
Expand All @@ -680,9 +710,12 @@ 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;
this._holdPixelTolerance = 25;

this._element = defaultValue(element, document);

Expand Down Expand Up @@ -799,5 +832,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;
});
83 changes: 83 additions & 0 deletions Specs/Core/ScreenSpaceEventHandlerSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down

0 comments on commit ead9f36

Please sign in to comment.