I'm trying to implement a simple custom scrolling method in my mobile web app. I'm having trouble with the vertical scrolling where I'd like to have a little momentum effect if the page is 'flicked'.
The problem is detecting a "flick" gesture, (the velocity and perhaps length of that gesture) after a dragging gesture, changing direction and what not. Hope you understand what I mean, you can drag the page up or down and at the end of that drag, I'd like to detect if there is also a flick..
How do you separate the two? How does such a logic look?
Many thanks for any help.
The code: (sorry if this excerpt is a bit of a mess)
var Device = function() {
//define some private variablees
var startY,
startX,
startTime,
deltaY = 0,
deltaX = 0,
lastY,
currentPage,
nextPage,
prevPage,
directionY,
lastTime,
pages,
pane,
offsetX = 0,
offsetY = 0,
isPanning,
isScrolling,
isTouch = "ontouchstart" in window;
return {
init: function() {
document.getElementById('frame').addEventListener(isTouch ? 'touchstart' : 'mousedown', Device.onTouchStart, false);
//get all panes in an array
panes = document.querySelectorAll('.pane');
},
onTouchStart: function (evt) {
//get X and Y of the touch event
var touch = isTouch ? event.touches[0] : event;
startY = touch.clientY;
//add listener for touch move and end
document.addEventListener(isTouch ? 'touchmove' : 'mousemove', Device.onTouchMove, false);
document.addEventListener(isTouch ? 'touchend' : 'mouseup', Device.onTouchEnd, false);
startTime = new Date();
},
onTouchMove: function (evt) {
//get X and Y of the touch event
var touch = isTouch ? event.touches[0] : event;
currentY = touch.clientY;
//calc touch length
deltaY = currentY - startY;
//Detect if scroll is bigger than threshold 5px
if (Math.abs(deltaY) > 5 && !isPanning) {
isScrolling = true;
//get the element
pane = panes[0];
//set new position
offsetY = pane.lastOffset + deltaY;
//call animation
Device.scrollTo(pane,0,offsetY);
}
//detect last direction
directionY = (lastY >= currentY) ? 1 : 0;
//roll over last variables
lastY = currentY;
lastTime = new Date();
},
onTouchEnd: function () {
//timing
var endTime = new Date();
var velocity = (endTime - lastTime).toFixed(0);
console.log('velocity: ' + velocity);
//TEMPORARY
pane.lastOffset = offsetY;
isScrolling = false;
//housekeeping
document.removeEventListener(isTouch ? 'touchmove' : 'mousemove', Device.onTouchMove, false);
document.removeEventListener(isTouch ? 'touchend' : 'mouseup', Device.onTouchEnd, false);
//call for momentum
Device.doMomentum(velocity);
},
scrollTo: function(el,x,y) {
if (el) {
el.style['-webkit-transition-timing-function'] = '';
el.style['-webkit-transition-duration'] = '0ms';
el.style[ 'WebkitTransform' ] = 'translate3d('+x+'px,'+y+'px, 0px)';
}
},
animateTo: function(el,x,y) {
if (el) {
el.style['-webkit-transition-timing-function'] = 'cubic-bezier(0,0,0.25,1)';
el.style['-webkit-transition-duration'] = '300ms';
el.style[ 'WebkitTransform' ] = 'translate3d('+x+'px,'+y+'px, 0px)';
}
},
doMomentum: function(velocity) {
console.log((directionY == 1) ? 'up': 'down');
console.log('pane.lastOffset: ' + pane.lastOffset);
var endPosition;
if (directionY == 1) {
endPosition = pane.lastOffset - velocity;
} else {
endPosition = parseFloat(pane.lastOffset) + parseFloat(velocity);
}
console.log(endPosition);
Device.animateTo(pane,0,endPosition);
pane.lastOffset = endPosition;
}
I would think a flick would be detected using multiple points associated with position and time. If you think about the motion of a normal drag, it would be done at a fairly constant velocity. A flick at the end however, should have a velocity significantly higher than the rest of the drag.
So, if you have a 5 point array, containing drag point samples spaced about 0.1 seconds apart (and where the older values are earlier in the array), you could calculate the velocity of the normal drag like so (for vertical dragging)
var dragVelY = (samples[2].y - samples[0].y)/3;
Then the velocity of the potential flick like so:
var flickVelY = (samples[4].y-samples[2].y)/3;
If flickVelY is significantly greater than dragVelY, then you have detected a flick. I haven't tested this, so you might need to adjust the number of samples in the array and how many samples you are comparing to get good drag or flick velocities. If its a long drag, I'd just use the Array.shift and Array.push methods to move old positions out of the samples array and push new ones on the end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With