如何检测拖动后的“轻弹”

How to detect a 'flick' after drag?

本文关键字:轻弹 拖动 何检测 检测      更新时间:2023-09-26

我正在尝试在我的移动 Web 应用程序中实现一个简单的自定义滚动方法。我在垂直滚动时遇到问题,如果页面被"轻拂",我希望有一点动量效果。

问题是在拖动手势之后检测"轻拂"手势(该手势的速度和长度),改变方向等等。 希望你明白我的意思,你可以向上或向下拖动页面,在拖动结束时,我想检测是否也有轻拂。

你如何区分两者?这样的逻辑是什么样子的?

非常感谢任何帮助。

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

 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;

        }

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

因此,如果您有一个 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 方法将旧位置移出样本数组,并将新位置推到末尾。

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

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

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

然后onTouchEnd我得到一个结束时间值并评估差异。如果这个时间很短,那么我认为最后有一个"轻弹"。

onTouchEnd: function (evt) {
   ...
 var endTime = new Date();
 var vel  = (endTime - intermediateTime);
 if (vel <= 100) {
    doMomentumOrSomething();
 }
   ...
}

这非常简单,但实际上效果很好。