How can I receive combinations of two/one finger and single/double taps in hammer.js - hammer.js

I'm attempting to receive three types of tap events using hammer.js
single tap with one finger
double tap with one finger
double tap with two fingers
How can I set this up correctly in hammer.js
Here is what I currently have:
if (mc == null) {
mc = new Hammer.Manager(el);
}
mc.set({ touchAction: "none" });
//mc.get("swipe").set({direction: Hammer.DIRECTION_ALL, pointers: 2});'
//mc.get('tap').set({enable: false});
var singletap = new Hammer.Tap({
event: "singletap",
taps: 1,
pointers: 1
});
var doubletap = new Hammer.Tap({
event: "doubletap",
taps: 2,
pointers: 1
});
var twodoubletap = new Hammer.Tap({
event: "twodoubletap",
taps: 2,
pointers: 2
});
mc.add([twodoubletap, doubletap,singletap]);//, twotap, threetap]);
twodoubletap.recognizeWith([doubletap,singletap])
doubletap.recognizeWith([singletap,twodoubletap])
//doubletap.requireFailure(twodoubletap);
singletap.requireFailure([doubletap,twodoubletap]);
mc.on("twodoubletap doubletap singletap", binding.value);
I can get a single tap to fire consistently and a one finger double tap sometimes.
The two-finger double tap either does not fire, or fires along with a one other taps following it immediately.
Ideally, I would like each type of tap to fire consistently.

I was able to get this to work by doing three things.
1. I added dropRequireFailure to the two-fingerdouble tap for the other taps
2. I made the thresholds larger in the events.
3. I added a time-based filter in the event handler that rejects taps too close to each other. I know this isn't ideal but it works for now.
if (mc == null) {
mc = new Hammer.Manager(el);
}
mc.set({ touchAction: "none" });
//mc.get("swipe").set({direction: Hammer.DIRECTION_ALL, pointers: 2});'
//mc.get('tap').set({enable: false});
var singletap = new Hammer.Tap({
event: "singletap",
taps: 1,
pointers: 1,
interval: 500,
threshold: 200,
posThreshold: 200
});
var doubletap = new Hammer.Tap({
event: "doubletap",
taps: 2,
pointers: 1,
interval: 300,
threshold: 200,
posThreshold: 200
});
var twodoubletap = new Hammer.Tap({
event: "twodoubletap",
taps: 2,
pointers: 2,
interval: 400,
threshold: 500,
posThreshold: 200
});
mc.add([twodoubletap, doubletap,singletap]);//, twotap, threetap]);
twodoubletap.recognizeWith([doubletap,singletap])
doubletap.recognizeWith([twodoubletap])
doubletap.requireFailure(twodoubletap);
singletap.requireFailure([doubletap,twodoubletap]);
twodoubletap.dropRequireFailure(doubletap);
twodoubletap.dropRequireFailure(singletap);
mc.on("twodoubletap doubletap singletap", handler);
Then at the top of the event handler:
let now = Date.now();
if((now - lastTap)<500){
return;
}
lastTap = now;

Related

hammer.js, prevent "tap" from firing on "doubletap"

I have the following setup
function hammerIt(elm) {
hammertime = new Hammer(elm, {});
hammertime.on('tap doubletap pan pinch panstart panend pinchend swipe swipeleft swiperight swipedown swipeup', function(ev) {
// Doesn't work...
var doubletap = hammertime.get('doubletap');
var tap = hammertime.get('tap');
doubletap.recognizeWith(tap);
tap.requireFailure(doubletap);
if (ev.type == 'tap') {
doSomething();
}
if (ev.type == 'doubletap') { // This is never fired....
doSomethingElse();
}
// etc.
}
When I doubletap/-click on the element, both the tap and the doubletap event are fired. I only want the doubletap to fire but somehow my code doesn't work? Is it because I'm using on(events) and .get("event name") instead of dedicated events like in the example on https://hammerjs.github.io/require-failure/ or am I missing something else?
Thank you john Smith, based on your fiddle I've come up with a working solution:
function hammerIt(elm) {
hammertime = new Hammer.Manager(elm);
var doubletap = new Hammer.Tap({event: 'doubletap', taps: 2});
var tap = new Hammer.Tap({event: 'tap'});
var pan = new Hammer.Pan();
var pinch = new Hammer.Pinch({enable:true});
var swipe = new Hammer.Swipe();
hammertime.add([doubletap, tap, pan, pinch, swipe]);
hammertime.get('tap').requireFailure('doubletap');
hammertime.on('tap doubletap pan pinch panstart panend pinchend swipe swipeleft swiperight swipedown swipeup', function(ev) {
if (ev.type == 'tap') {
doSomething();
}
if (ev.type == 'doubletap') {
doSomethingElse();
}
// etc.
}
}
I think the problem was using "new Hammer(elem..." instead of a "new Hammer.Manager(elem)". This way I have to add the recognizers manually instead of using the default set but at least it works as expected :-)

Hammer.js Vertical Swipes Are Erratic

It seems that 'swipeup' and 'swipedown' events are not fired consistently. Is this a known issue?
I'm testing this pen on OSX/Chrome with a mouse: http://codepen.io/felixturner/pen/RWbPoN
var mc = new Hammer(myElement);
//enable all directions
mc.get('swipe').set({
direction: Hammer.DIRECTION_ALL
});
// listen to events...
mc.on("swipeup swipedown swipeleft swiperight tap press", function(ev) {
myElement.textContent = ev.type + " gesture detected.";
});
Thanks!
Lowering the threshold and velocity fixed this issue:
mc.get('swipe').set({
direction: Hammer.DIRECTION_ALL,
threshold: 1,
velocity:0.1
});

hammer.js detect variables in panmove and unbind when it hits certain criteria

my goal is detect when an element has reached a certain margin-left, and than unbind or stop the panmove from continuing if it hits that threshold.
I have a "panmove" bound to an element using hammer.js, and jquery hammer plugin.
I noticed that in the panmove, console.log(e) will fire hundreds of times as you move the elements, which is expected. If you however put an if statement in the panmove function, it only goes off of the initial state of the first panmove and not the current one.
.bind("panmove", function (e) {
var count = 0;
console.log(e);
console.log(count++);
var _this = $(e.target);
var _thisDataLeft = _this.attr("data-left");
var _thisDataMaxLeft = _this.attr("data-maxleft"); // this is derived from the width of the Delete box, which can be any width.
if (Math.abs(_thisDataLeft) < Number(_thisDataMaxLeft)) {
_this.css({ left: Number(_thisDataLeft) + e.gesture.deltaX }); // controls movement of top layer
console.log(count++);
}
I noticed that the console.log(count++) always fires 1, instead of iterating up, as if it is only reading it once in the beginning.
How can I run an if statement inside of this Pan, so that it is always the current information, and not just the first iteration?
Ended up moving away from Hammer.js, was not able to get the results I needed. It looks like the more basic jquery.event.move.js was easier to use than hammer.
here is my example in js fiddle
https://jsfiddle.net/williamhowley/o9uvo50y/
$(document).ready(function () {
// http://stephband.info/jquery.event.move/
// http://stephband.info/jquery.event.swipe/
// add swipe functionality to the rows.
// I think you will need to add the swipe left, after it is activated by a HOLD down press.
// idk, how do you always make something swipable.
var wrap = $('ul#main');
$('ul#main > li')
.on('movestart', function (e) {
console.log("move start");
// var $li = $(e.target).closest('.swipable'); // this would be normal live integration
var $li = $(e.target);
if ($li.attr("data-hasplaceholder") !== "true") { // if it does not have a placeholder, add one.
createBackgroundSpacer($li);
$li.attr("data-hasplaceholder", true); // signify that a placeholder has been created for this element already.
}
// If the movestart heads off in a upwards or downwards
// direction, prevent it so that the browser scrolls normally.
if ((e.distX > e.distY && e.distX < -e.distY) ||
(e.distX < e.distY && e.distX > -e.distY)) {
e.preventDefault();
return;
}
// To allow the slide to keep step with the finger,
// temporarily disable transitions.
wrap.addClass('notransition'); // add this to the container wrapper.
})
.on('move', function (e) {
// event definitions
// startX : 184, where from left the mouse curser started.
// deltaX: ?
// distX: how far the mouse has moved, if negative moving left. Still need to account for double movement, currently can only handle one movement.
console.log("move");
console.log(e);
var maxLeft = $('.rightContent').width();
var marginLeftNum = Number($(this).css('margin-left').replace(/[^-\d\.]/g, ''));
if (marginLeftNum <= -maxLeft && e.deltaX < 0) { // Case when user is at outermost left threshold, and trying to move farther left.
$(this).css({ 'margin-left': -maxLeft });
}
else if (marginLeftNum == -maxLeft && e.deltaX > 0) { // When user is at threshold, and trying to move back right.
$(this).css({ 'margin-left': marginLeftNum + e.deltaX });
}
else if (e.target.offsetLeft>=0 && e.deltaX>0) { // If the offset is 0 or more, and the user is scrolling right (which is a positive delta, than limit the element. )
$(this).css({ 'margin-left': 0 });
}
// Must have a Negative offset, and e.deltaX is Negative so it is moving left.
else if (e.deltaX < 0) { // Case when element is at 0, and mouse movement is going left.
$(this).css({ 'margin-left': marginLeftNum + e.deltaX });
}
else { // Moving Right when not on 0
$(this).css({ 'margin-left': marginLeftNum + e.deltaX });
}
})
.on('swipeleft', function (e) {
console.log("swipeleft");
})
.on('activate', function (e) {
// not seeing this activate go off, i think this is custom function we can add on if swipe left hits a threshold or something.
console.log("activate");
})
.on('moveend', function (e) {
console.log("move end");
wrap.removeClass('notransition');
});
var createBackgroundSpacer = function ($shoppingListRow) {
var border = 2;
$shoppingListRow.css({ 'width': $shoppingListRow.width() + border, 'height': $shoppingListRow.height() + border }); // gives itself set width and height
$shoppingListRow.addClass('swipable');
// placeholder HTML
var leftPlaceholder = $('<div class="leftPlaceholder"></div>').css({ 'height': $shoppingListRow.height()});
var rightPlaceholder = $('<div class="rightPlaceholder"></div>')
var rightContent = $('<div class="rightContent">Delete</div>').css({ 'height': $shoppingListRow.height()});
rightPlaceholder.append(rightContent);
var placeHolder = $('<div class="swipePlaceholder clearfix"></div>'); // goes around the two floats.
placeHolder.css({ 'width': $shoppingListRow.width(), 'height': $shoppingListRow.height() });
placeHolder.append(leftPlaceholder, rightPlaceholder);
$shoppingListRow.before(placeHolder); // adds placeholder before the row.
$shoppingListRow.css({ 'marginTop': -($shoppingListRow.height() + border) });
};
});

Infinite Scrolling with wookmark plugins scrolling

With ref. to above subject, I am using wookmark plugin to scroll our home page data dynamically….I have studied the tutorial provided on wookmark and I m using the exact script provided by wookmark and working fine shorts of not 100% working.
Things it stucks when it reaches at bottom of the window then we slightly press the up arrow key, that loads the products again and this is happens randomly some time it scrolls perfectly and some time it stucks and if presses up arrow key it starts working again.
Kindly help me out where I m going wrong. Kindly provide me the easy working script for the same.
I m using following code :
(function ($) {
$('#main').imagesLoaded(function () {
var handler = null;
// Prepare layout options.
var options = {
itemWidth: 200, // Optional min width of a grid item
autoResize: true, // This will auto-update the layout when the browser window is resized.
container: $('#main'), // Optional, used for some extra CSS styling
offset: 20, // Optional, the distance between grid items
outerOffset: 20, // Optional the distance from grid to parent
flexibleWidth: 300 // Optional, the maximum width of a grid item
};
function applyLayout() {
$('#main').imagesLoaded(function () {
// Destroy the old handler
if (handler.wookmarkInstance) {
handler.wookmarkInstance.clear();
}
// Create a new layout handler.
handler = $('#display li');
handler.wookmark(options);
});
handler.wookmark(options);
}
/**
* When scrolled all the way to the bottom, add more tiles.
*/
function onScroll(event) {
// Check if we're within 100 pixels of the bottom edge of the broser window.
var winHeight = window.innerHeight ? window.innerHeight : $(window).height(); // iphone fix
//var closeToBottom = ($(window).scrollTop() >= $((document)).height() - $((window)).height() - $("#footer").height() - 500); //(($(window).scrollTop() - 100)); //+ "%"
var closeToBottom = ($(window).scrollTop() + winHeight > $(document).height() - 100);
if (closeToBottom) {
// Get the first then items from the grid, clone them, and add them to the bottom of the grid.
var items = $('#display li'),
firstTen = items.slice(0, 10);
//$('#display').append(firstTen.clone());
applyLayout();
}
};
// Capture scroll event.
$(window).bind('scroll', onScroll);
// Call the layout function.
handler = $('#display li');
handler.wookmark(options);
});
$(window).load(function () {
handler.wookmark(options);
});
})(jQuery);
If you commented out
//$('#display').append(firstTen.clone());
then the new items will not be loaded on the end of list. You need to uncomment that line to get new items.
In real life instead of
var items = $('#display li'),
firstTen = items.slice(0, 10);
$('#display').append(firstTen.clone());
you would need a code that will load new items.
Also I think it might make sense to change > to >=
var closeToBottom = ($(window).scrollTop() + winHeight >= $(document).height() - 100);
to load new items if scroll position is more or equal to the height of window - 100, where 100 is just some value - you could try 200 or even more to see if it will work better for you.

Bing Maps API v7 Custom HTML pushpins in Firefox not positioned correctly

I've created an app where I use several custom HTML pushpins. Each one of these pushpins has a click event that will call setView on the map to center the map on the selected pin. This works perfectly in all browsers except for Firefox (testing version 22.0).
In Firefox, after the setView animation completes and the map is centered on the pushpin, the pushpin is then offset horizontally, vertically or both by a certain amount of pixels. The amount seems to correspond with amount of pixels the map has moved. If you then drag the map manually with the mouse, upon releasing the mouse button, the pushpin snaps back to its proper place. I've checked the top and left position values of the MapPushpinBase anchor tag in compared it with other browsers and the values differ.
Unfortunately, I cannot post a live example because the product has not yet been publicly released. But see below for the code I'm using.
this.clickHandlers.push(Microsoft.Maps.Events.addHandler(node, 'click', function (e) {
var element = $(e.target._htmlContent);
self.nodeHitboxHandler(element);
}));
Within the nodeHitboxHandler function, the only piece of Bing Map code is this:
this.map.setView({
center: new Microsoft.Maps.Location(panStart.latitude, panStart.longitude),
zoom: this.zoom,
mapTypeId: Microsoft.Maps.MapTypeId[self.mapTypeId]
});
Thanks in advance for any help.
UPDATE:
In order to make things clearer, I've created a simple example that demonstrates the problem. You can see it here: http://ginof.com/tests/bing/
In Firefox, try clicking on the different pushpins and watch the behaviour of the pushpin you've just clicked on after the map finishes panning to its new location. The map works fine in all browsers except Firefox.
Here's the complete JavaScript code for this example:
$(document).ready(function () {
var map = null,
initialCoordinates = {latitude: 40.71435, longitude: -74.00597},
initialPoint = new Microsoft.Maps.Location(initialCoordinates.latitude, initialCoordinates.longitude),
range = {
top: 3,
right: 5,
bottom: 0.01,
left: 5
},
mapOptions = {
credentials:"BING-MAPS-API-KEY",
disableKeyboardInput: true,
disableZooming: true,
enableClickableLogo: false,
enableSearchLogo: false,
showBreadcrumb: false,
showDashboard: false,
showMapTypeSelector: false,
showScalebar: false,
inertiaIntensity: 0.5,
mapTypeId: Microsoft.Maps.MapTypeId.birdseye,
labelOverlay: Microsoft.Maps.LabelOverlay.hidden,
center: initialPoint,
zoom: 14
};
function GetMap() {
// Initialize the map
map = new Microsoft.Maps.Map(document.getElementById("mapDiv"), mapOptions);
// Create nodes
map.entities.clear();
var pushpinOptions,
pushpin,
nodeCoordinates;
for (var i = 0; i < 5; i++) {
pushpinOptions = {width: null, height: null, htmlContent: '<div id="node' + i + '" class="node">' + i + '. Custom HTML</div>'};
nodeCoordinates = {latitude: initialCoordinates.latitude + i * 0.005, longitude: initialCoordinates.longitude + i * 0.005};
pushpin = new Microsoft.Maps.Pushpin(new Microsoft.Maps.Location(nodeCoordinates.latitude, nodeCoordinates.longitude), pushpinOptions);
pushpinClick = Microsoft.Maps.Events.addHandler(pushpin, 'click',
(function () {
var nodeId = i,
homeCoordinates = nodeCoordinates;
return function () {
console.log("node " + nodeId + " clicked.");
map.setView({center: new Microsoft.Maps.Location(homeCoordinates.latitude, homeCoordinates.longitude)});
}
})()
);
map.entities.push(pushpin);
}
}
GetMap();
});
Thanks again for any help.
This is a known issue which I was able to reproduce an has been escalated to the development team. Note that the Bing Maps forums are the best place to report these types of issues. You can find a similar thread on this topic here: http://social.msdn.microsoft.com/Forums/en-US/f77e6a80-2e0f-44c3-81cc-edb4c2327016/custom-html-pushpins-in-firefox-not-positioned-correctly

Resources