PDA

View Full Version : [OPEN] Finger move tolerance when tapping



Steffen Hiller
12 Nov 2012, 8:24 AM
I've heard and seen users whose taps on any tappable items in Sencha Touch such as buttons frequently fail.

The reason is that Sencha Touch is very sensible to any finger movement while pressing/tapping.
This is not how native iOS apps behave.

In a native iOS app you can tap/press a button, move your finger on the button and then release it and it will fire the button's action.
This doesn't work in Sencha Touch applications which is nicely reproducible through the kitchensink's "Touch Events" page. Moving the finger slightly triggers the dragstart, touchmove, drag, touchend, dragend events, but not the tap event.

What's y'alls opinion on this? Is that by design due to any constraints? Can this be improved/fixed?

mitchellsimoens
13 Nov 2012, 5:54 AM
I would agree that the tolerance is a little sensitive. I personally haven't had an issue but I'm also a developer that understands what is going on. I have opened a bug in our bug tracker.

Steffen Hiller
13 Nov 2012, 5:59 AM
Thanks, Mitch.

Exactly, I haven't that big of an issue with that myself either since I naturally seem to tap carefully. :)
But imagine everybody else as well as situations like meetings where you look together with somebody else on a tablet and maybe tap from the side (that's where it also happens to me).

ingo.hefti
13 Nov 2012, 7:12 AM
But imagine everybody else as well as situations like meetings where you look together with somebody else on a tablet and maybe tap from the side (that's where it also happens to me).
+1

hedgehog1
13 Nov 2012, 10:28 PM
When testing on phones, our QA found it could take 2 or 3 presses to get a 'good' press. We developers didn't notice it as we were working in Chrome with a Mouse.

The mouse tends to stay on the same pixel, while fingers on a phone tend to move a bit.

hedgehog1
14 Nov 2012, 12:23 PM
We found a workaround using: 'Fixed Button'.

First, the URL to the code: github.com/roycyang/Sencha-Touch-Extensions

(http://github.com/roycyang/Sencha-Touch-Extensions)This extends the Ext.Button:


Ext.define('GT.Button', {
extend: 'Ext.Button',
xtype: 'fixedbutton',
config: {

You can then replace the xtype: 'button' with xtype: 'fixedbutton' to get the preferred tablet/phone friendly behavior:


items: [{
//xtype: 'button',
xtype: 'fixedbutton',
iconCls: 'refresh',
iconMask: true,
align: 'left',
id: 'MainViewButtonRefresh'
},

The Hedge

ssweriduk
3 Dec 2012, 11:44 AM
Hey guys,
We have just deployed our app and found that a lot of our customers aren't able to tap on buttons as well. Was wondering if there has been any further progress on this issue.

ssweriduk
4 Dec 2012, 7:49 AM
The above example is brilliant

jweber
4 Dec 2012, 6:41 PM
I've noticed this too, but I always blamed myself.

jbondc
6 Dec 2012, 7:37 AM
The problem is a 'tap' event fails in Sencha Touch as soon as you 'move'.

A simple fix is allow 1-2 touchmove events before you invalidate the tap:

http://pastebin.com/fQNY1XzD

mankz
9 Dec 2012, 3:45 AM
Better to use a tolerance value, this is how we solve this issue in our Touch Scheduler:


/**
* A event recogniser which knows when you tap and hold for more than {@link #minDuration} ms (defaults to 400ms).
*
* @private
*/
Ext.define('Sch.recognizer.SemiLongPress', {
extend: 'Ext.event.recognizer.SingleTouch',

/**
* @cfg {Int} moveTolerance
* While pressing a finger on the screen, it can be hard to keep it perfectly still - which aborts a normal 'longpress' gesture.
* Setting this tolerance value allows for some slight movement during the press.
*/
moveTolerance : 5,

inheritableStatics: {
DURATION_NOT_ENOUGH: 0x20
},

config: {
minDuration: 400
},

handledEvents: ['semilongpress'],

/**
* @event semilongpress
* Fires when you touch and hold finger (almost) still for more than 400 ms.
* @param {Ext.event.Event} event The {@link Ext.event.Event} event encapsulating the DOM event.
* @param {HTMLElement} node The target of the event.
* @param {Object} options The options object passed to Ext.mixin.Observable.addListener.
*/
fireLongPress: function(e) {
var touch = e.changedTouches[0];

this.fire('semilongpress', e, [touch], {
touch: touch,
duration: this.getMinDuration()
});

this.isLongPress = true;
},

onTouchStart: function(e) {
var me = this;

if (this.callParent(arguments) === false) {
return false;
}

this.isLongPress = false;
this._touchX = e.touch.pageX;
this._touchY = e.touch.pageY;

this.timer = setTimeout(function() {
me.fireLongPress(e);
}, this.getMinDuration());
},

onTouchMove: function(e) {
if (Math.abs(this._touchX - e.touch.pageX) > this.moveTolerance ||
Math.abs(this._touchY - e.touch.pageY) > this.moveTolerance) {
return this.fail(this.self.TOUCH_MOVED);
}
},

onTouchEnd: function() {
if (!this.isLongPress) {
return this.fail(this.self.DURATION_NOT_ENOUGH);
}
},

fail: function() {
clearTimeout(this.timer);

return this.callParent(arguments);
}

});

Steffen Hiller
6 Jan 2013, 2:10 PM
Thanks for all the work around suggestions!

Sencha seems to solve this problem very elegantly in ST 2.2.0.alpha.

Here's the code as override that also works for ST 2.1. (I only changed the moveDistance var to be hard coded instead of a config var since configs don't work in overrides.)



Ext.define('App.override.event.recognizer.Tap', {
override: 'Ext.event.recognizer.Tap',

handledEvents: ['tap', 'tapcancel'],

onTouchStart: function(e) {
if (this.callSuper(arguments) === false) {
return false;
}

this.startPoint = e.changedTouches[0].point;
},

onTouchMove: function(e) {
var touch = e.changedTouches[0],
point = touch.point,
moveDistance = 20;

if (Math.abs(point.getDistanceTo(this.startPoint)) >= moveDistance) {
this.fire('tapcancel', e, [touch], {
touch: touch
});
return this.fail(this.self.TOUCH_MOVED);
}
},

onTouchEnd: function(e) {
var touch = e.changedTouches[0];

this.fire('tap', e, [touch], {
touch: touch
});
}
});

drb
12 Aug 2013, 3:29 PM
Here is what we are using as of 2.2.1. It seems to work.


// Adds a little damping so that small touch moves do not prevent taphold and itemtaphold
Ext.define('Ext.overrides.event.recognizer.LongPress', {
override: 'Ext.event.recognizer.LongPress',
onTouchStart: function (e) {
this.startPoint = e.changedTouches[0].point;
if (this.callParent(arguments) === false) {
return false;
}
},
onTouchMove: function (e) {
var touch = e.changedTouches[0],
point = touch.point
moveDistance = 7;
if (Math.abs(point.getDistanceTo(this.startPoint)) >= moveDistance) {
return this.fail(this.self.TOUCH_MOVED);
}
}
});