拖动后如何检测'轻弹'?

时间:2012-04-24 13:36:49

标签: javascript mobile-website

我正在尝试在我的移动网络应用中实现一种简单的自定义滚动方法。我在垂直滚动方面遇到了麻烦,如果页面被“轻弹”,我希望有一点动量效果。

问题是在拖动手势,改变方向和不改变方向之后检测“轻弹”手势(手势的速度和可能的长度)。希望你理解我的意思,你可以向上或向下拖动页面,在拖动结束时,我想检测是否还有一个轻弹..

你如何将两者分开?这样的逻辑怎么样?

非常感谢您的帮助。

代码:(对不起,如果这段摘录有点混乱)

 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;


        }

2 个答案:

答案 0 :(得分:2)

我认为可以使用与位置和时间相关的多个点来检测轻弹。如果你考虑正常阻力的运动,它将以相当恒定的速度完成。然而,最后的轻弹应该具有显着高于阻力的其余部分的速度。

因此,如果你有一个5点阵列,包含间隔约0.1秒的拖动点样本(以及较旧值在数组中较早的位置),你可以像这样计算法线拖动的速度(对于垂直拖动) )

var dragVelY = (samples[2].y - samples[0].y)/3;

然后潜在的轻弹速度如下:

var flickVelY = (samples[4].y-samples[2].y)/3;

如果flickVelY明显大于dragVelY,那么您已检测到轻弹。我没有对此进行测试,因此您可能需要调整阵列中的样本数量以及您要比较的样本数量以获得良好的拖动或轻弹速度。如果它拖了很长时间,我只是使用Array.shift和Array.push方法将旧位置移出samples数组并在最后推送新位置。

答案 1 :(得分:0)

仅供参考,我最终做的只是使用onTouchMove - 事件和onTouchEnd - 事件之间经过的时间。

基本上,我有一个日期变量,在onTouchMove回调中不断覆盖自己:

onTouchMove: function (evt) {
   ...
 intermediateTime = new Date();
   ...
}

然后onTouchEnd我得到一个endTime值并评估差异。如果这个时间非常短,那么我假设最后有一个“轻弹”。

onTouchEnd: function (evt) {
   ...
 var endTime = new Date();

 var vel  = (endTime - intermediateTime);

 if (vel <= 100) {

    doMomentumOrSomething();

 }
   ...
}

这很简单但实际上工作得相当好。