Skip to content

Instantly share code, notes, and snippets.

@krizpoon
Last active August 29, 2015 14:21
Show Gist options
  • Save krizpoon/a51827423ee88a6bbf1c to your computer and use it in GitHub Desktop.
Save krizpoon/a51827423ee88a6bbf1c to your computer and use it in GitHub Desktop.
Fast Button using jQuery
/*
* Usage: $(elem).fastButton(opts);
*
* opts.propagates: true|false that touch events should propagate
* opts.moveThreshold: maximum touch move distance after which the action should be considered cancelled, false if there is no threshold
* opts.pressedClass: class name to add when touch started
* opts.insideClass: class name to add when touch is inside
* opts.outsideClass: class name to add when touch is outside
*
* To turn off fast button: $(elem).trigger('fastButtonOff');
*/
(function($)
{
function fastButton(elem, opts)
{
elem = $(elem);
if (!elem.length) return elem;
var isTouchDevice = 'ontouchstart' in window || navigator.msMaxTouchPoints;
if (!isTouchDevice) return elem;
opts = $.extend({propagates:false, moveThreshold:[10,10], pressedClass:'pressed', insideClass:'inside', outsideClass:'outside'}, opts);
function firstTouch(evt)
{
if (evt.originalEvent) evt = evt.originalEvent;
return evt.changedTouches[0];
}
function getTouch(evt, touchId)
{
if (evt.originalEvent) evt = evt.originalEvent;
for (var i = 0; i < evt.changedTouches.length; i++)
{
var touch = evt.changedTouches[i];
if (touch.identifier == touchId) return touch;
}
return null;
}
function getXY(evt, touchId)
{
var touch = getTouch(evt, touchId);
if (!touch) return null;
var x = touch.pageX;
var y = touch.pageY;
return [x,y];
}
function isInside(evt, touchId)
{
var touch = getTouch(evt, touchId);
if (!touch) return false;
var x = touch.pageX - window.pageXOffset;
var y = touch.pageY - window.pageYOffset;
var target = document.elementFromPoint(x, y);
var ret = elem[0] == target || $.contains(elem[0], target);
return ret;
}
var cancelled = false, inside = false, startLocation = null, touchId = null;
function touchStart(evt)
{
if (touchId) return;
if (!opts.propagates) { evt.stopPropagation(); evt.preventDefault(); }
elem.removeClass(opts.outsideClass).addClass(opts.pressedClass).addClass(opts.insideClass);
cancelled = false;
inside = true;
touchId = firstTouch(evt).identifier;
startLocation = getXY(evt, touchId);
}
function touchMove(evt)
{
var touch = getTouch(evt, touchId);
if (!touch) return;
if (!opts.propagates) { evt.stopPropagation(); evt.preventDefault(); }
if (cancelled) return;
inside = isInside(evt, touchId);
var mt = opts.moveThreshold;
if (mt !== false && mt.length)
{
var loc = getXY(evt, touchId);
var dx = loc[0] - startLocation[0];
var dy = loc[1] - startLocation[1];
cancelled = Math.abs(dx) > mt[0] || Math.abs(dy) > mt[1];
}
if (cancelled)
{
elem.removeClass(opts.pressedClass).removeClass(opts.insideClass).removeClass(opts.outsideClass);
}
else if (inside)
{
elem.addClass(opts.insideClass).removeClass(opts.outsideClass);
}
else
{
elem.addClass(opts.outsideClass).removeClass(opts.insideClass);
}
}
function touchEnd(evt)
{
var touch = getTouch(evt, touchId);
if (!touch) return;
touchId = null;
if (!opts.propagates) { evt.stopPropagation(); evt.preventDefault(); }
elem.removeClass(opts.pressedClass).removeClass(opts.insideClass).removeClass(opts.outsideClass);
if (!cancelled && inside)
{
elem.trigger('click');
}
}
function touchCancel(evt)
{
var touch = getTouch(evt, touchId);
if (!touch) return;
cancelled = true;
touchEnd(evt);
}
function destroy()
{
elem.off({touchstart:touchStart, touchmove:touchMove, touchend:touchEnd, touchcancel:touchCancel, fastButtonOff:destroy});
}
return elem.on({touchstart:touchStart, touchmove:touchMove, touchend:touchEnd, touchcancel:touchCancel, fastButtonOff:destroy});
}
$.fn.fastButton = function() { return this.each(function(){ fastButton(this); }); };
}
($));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment