Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect a 'flick' after drag?

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;


        }
like image 538
jenswirf Avatar asked Nov 05 '22 01:11

jenswirf


1 Answers

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.

like image 166
netfire Avatar answered Nov 12 '22 15:11

netfire