zoukankan      html  css  js  c++  java
  • zepto源码学习-06 touch

    先上菜,看这个模块的最后一段代码,一看就明白。

    ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown',
        'doubleTap', 'tap', 'singleTap', 'longTap'
    ].forEach(function(eventName) {
        $.fn[eventName] = function(callback) {
            return this.on(eventName, callback)
        }
    })
    • tap —元素tap的时候触发。
    • singleTap and doubleTap — 这一对事件可以用来检测元素上的单击和双击。(如果你不需要检测单击、双击,使用 tap 代替)。
    • longTap — 当一个元素被按住超过750ms触发。
    • swipeswipeLeftswipeRightswipeUpswipeDown — 当元素被划过时触发。(可选择给定的方向)

    已经广为人知了,因为 click 事件有 200~300 ms 的延迟,为了更快的响应,最好用 Zepto 提供的 tap 事件,可是zepto的tap会点透,我想说它的设计必然会点透。

    看看zepto中tap的实现,其中有个setTimeout(function(){},0)。如果我们引入了fastclick,其实元素的click会比tap要先触发,原因吗就是那个setTimeout,singleTap延迟250毫秒,这个就更慢了。fastclick写得更好,稍后再来分学习学习,然后分析分析。

    tapTimeout = setTimeout(function() {
    
        // trigger universal 'tap' with the option to cancelTouch()
        // (cancelTouch cancels processing of single vs double taps for faster 'tap' response)
        var event = $.Event('tap')
            //可以cancelAll
        event.cancelTouch = cancelAll
            //触发tap
        touch.el.trigger(event)
    
        // trigger double tap immediately
        // 之前touchstart 里面有判断和上次点击时间的时间差
        if (touch.isDoubleTap) {
            if (touch.el) touch.el.trigger('doubleTap')
            touch = {}
        }
        // trigger single tap after 250ms of inactivity
        else {
            //延迟250毫秒
            touchTimeout = setTimeout(function() {
                touchTimeout = null
                if (touch.el) touch.el.trigger('singleTap')
                touch = {}
            }, 250)
        }
    }, 0)

    android 4.4 swipe 事件会有问题,需要再修复,不过修复也不好使,慎重!! 所以一般还是自己实现,不用zepto的。

    关于点透,zepto把事件绑在document上,里面的tap没有组织冒泡,上机元素必然会触发相关事件,所以点透真是预料之中。那么解决也就简单,阻止冒泡就是了,具体怎么做,两种方式吧!!修改源码加上阻止冒泡,或者给里面元素绑定事件,手动阻止冒泡。

    没啥可说的直接上代码。

    ;
    (function($) {
        //保存滑动过程中的一些数据
        var touch = {},
            //记录各种timeout
            touchTimeout, tapTimeout, swipeTimeout, longTapTimeout,
            //默认超过750毫秒就触发longTap
            longTapDelay = 750,
            gesture
    
        //判断滑动的方向 上下左右。首先判断x和y哪个滑动距离大,然后在根据x确定是左右,根据一确定上下
        function swipeDirection(x1, x2, y1, y2) {
            return Math.abs(x1 - x2) >=
                Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
        }
    
        function longTap() {
            //置为null
            longTapTimeout = null
            if (touch.last) {
                //触发touch.el的longTap
                touch.el.trigger('longTap')
                    //重置touch对象
                touch = {}
            }
        }
    
        //取消longTapTimeout定时器,
        function cancelLongTap() {
                if (longTapTimeout) clearTimeout(longTapTimeout)
                longTapTimeout = null
            }
    
            //全部取消 重置
        function cancelAll() {
                if (touchTimeout) clearTimeout(touchTimeout)
                if (tapTimeout) clearTimeout(tapTimeout)
                if (swipeTimeout) clearTimeout(swipeTimeout)
                if (longTapTimeout) clearTimeout(longTapTimeout)
                touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null
                touch = {}
            }
            /*
             * 是否为移动webkit内核的事件
             */
        function isPrimaryTouch(event) {
                return (event.pointerType == 'touch' ||
                    event.pointerType == event.MSPOINTER_TYPE_TOUCH) && event.isPrimary
            }
            /*
             * IE 10 和11的事件
             */
        function isPointerEventType(e, type) {
            return (e.type == 'pointer' + type ||
                e.type.toLowerCase() == 'mspointer' + type)
        }
        //两次$(document) 难道开始就不能 var $d=$(document) 吗!!
        $(document).ready(function() {
            var now, delta, deltaX = 0,
                deltaY = 0,
                firstTouch, _isPointerType
    
            if ('MSGesture' in window) {
                gesture = new MSGesture()
                gesture.target = document.body
            }
    
            $(document)
                .bind('MSGestureEnd', function(e) {
                    var swipeDirectionFromVelocity =
                        e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null;
                    if (swipeDirectionFromVelocity) {
                        //触发元素相关事件 MSGestureEnd,不清楚是神马东西,估计就是一些奇葩特有的,IE!!!!
                        touch.el.trigger('swipe')
                        touch.el.trigger('swipe' + swipeDirectionFromVelocity)
                    }
                })
                /**
                 * MSPointerDown pointerdown IE10 11
                 * touchstart 移动Webkit
                 */
                .on('touchstart MSPointerDown pointerdown', function(e) {
                    //踢出奇葩的,不是标准webkit或者IE10 11 直接返回
                    if ((_isPointerType = isPointerEventType(e, 'down')) &&
                        !isPrimaryTouch(e)) return
                        //我只知道正常情况都是 e.touches[0]
                    firstTouch = _isPointerType ? e : e.touches[0]
                        //一个手指在操作
                    if (e.touches && e.touches.length === 1 && touch.x2) {
                        //重置x2 和y2
                        // Clear out touch movement data if we have it sticking around
                        // This can occur if touchcancel doesn't fire due to preventDefault, etc.
                        touch.x2 = undefined
                        touch.y2 = undefined
                    }
                    //初始触发时间
                    now = Date.now()
                        //当前的now减去上次触发时间,取得差值
                        //如果上次时间没有就是这次的时间, 那么delta就是0
                    delta = now - (touch.last || now)
                        //取得touch el
                    touch.el = $('tagName' in firstTouch.target ?
                            firstTouch.target : firstTouch.target.parentNode)
                        //touchTimeout存在即清除 清除 延迟250毫秒执行的singleTap
                    touchTimeout && clearTimeout(touchTimeout)
                        //取到开始的 pageX 、pageY 赋值给x1、y1
                    touch.x1 = firstTouch.pageX
                    touch.y1 = firstTouch.pageY
    
                    //上次touchstart 和这次touchstart时间相隔在250毫秒以内,就是isDoubleTap
                    if (delta > 0 && delta <= 250) touch.isDoubleTap = true
                        //赋值touch.last,下次触发减去这个时间,来计算是否触发doubleTap
                    touch.last = now
    
                    //设置longtap事件
                    longTapTimeout = setTimeout(longTap, longTapDelay)
                        // adds the current touch contact for IE gesture recognition
                        //这个表示不懂,我们从不兼容IE
                    if (gesture && _isPointerType) gesture.addPointer(e.pointerId);
                })
                .on('touchmove MSPointerMove pointermove', function(e) {
                    //touchstart、touchmove、touchend 都用到这个判断,拷贝三次,我觉得改简单封装下,虽然代码不多,但是也有必要。^_^
                    if ((_isPointerType = isPointerEventType(e, 'move')) &&
                        !isPrimaryTouch(e)) return
    
                    firstTouch = _isPointerType ? e : e.touches[0]
                        //一旦发生touchmove 就取消cancelLongTap。 我觉得应该还是有个居来判断吧!!!!比如不超过5像素也没问题
                    cancelLongTap()
                        //取得此时的pageX、pageY
                    touch.x2 = firstTouch.pageX
                    touch.y2 = firstTouch.pageY
    
                    //计算出和touchstart的差值
                    deltaX += Math.abs(touch.x1 - touch.x2)
                    deltaY += Math.abs(touch.y1 - touch.y2)
    
                    /**
                     * 修复 android 4.4 swipe 事件
                     * https://github.com/madrobby/zepto/issues/315#issuecomment-8386027
                     */
                    //当页面有滚动条的时候,这个修复就啃爹了。没有滚动条倒是可以随便修复。
                    /*if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 10) &&  (touch.y2 && Math.abs(touch.y1 - touch.y2) < 10))
                        e.preventDefault()*/
                    /*if (touch.x2 && Math.abs(touch.x1 - touch.x2) > 10)
                        e.preventDefault()*/
                })
                .on('touchend MSPointerUp pointerup', function(e) {
                    if ((_isPointerType = isPointerEventType(e, 'up')) &&
                        !isPrimaryTouch(e)) return
                        //这个就不说了,
                    cancelLongTap()
    
                    // swipe 默认超过就触发swipe相关事件
                    if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) || (touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))
                        swipeTimeout = setTimeout(function() {
                            //触发swpie 和'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown'
                            touch.el.trigger('swipe')
                            touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
                                //重置touch
                            touch = {}
                        }, 0)
                        // normal tap
                    else if ('last' in touch)
                    // don't fire tap when delta position changed by more than 30 pixels,
                    // for instance when moving to a point and back to origin
                    // 滑动距离小于30像素
                        if (deltaX < 30 && deltaY < 30) {
                            // delay by one tick so we can cancel the 'tap' event if 'scroll' fires
                            // ('tap' fires before 'scroll')
                            // 为什么要搞个setTimeout呢,因为发生滚动的时候要取消这些事件的执行,直接执行了就取消不了了
                            tapTimeout = setTimeout(function() {
    
                                // trigger universal 'tap' with the option to cancelTouch()
                                // (cancelTouch cancels processing of single vs double taps for faster 'tap' response)
                                var event = $.Event('tap')
                                    //可以cancelAll
                                event.cancelTouch = cancelAll
                                    //触发tap
                                touch.el.trigger(event)
    
                                // trigger double tap immediately
                                // 之前touchstart 里面有判断和上次点击时间的时间差
                                if (touch.isDoubleTap) {
                                    if (touch.el) touch.el.trigger('doubleTap')
                                    touch = {}
                                }
                                // trigger single tap after 250ms of inactivity
                                else {
                                    //延迟250毫秒
                                    touchTimeout = setTimeout(function() {
                                        touchTimeout = null
                                        if (touch.el) touch.el.trigger('singleTap')
                                        touch = {}
                                    }, 250)
                                }
                            }, 0)
                        } else {
                            touch = {}
                        }
                        //重置这两个数据
                    deltaX = deltaY = 0
    
                })
                // when the browser window loses focus,
                // for example when a modal dialog is shown,
                // cancel all ongoing events
                .on('touchcancel MSPointerCancel pointercancel', cancelAll)
    
            // scrolling the window indicates intention of the user
            // to scroll, not tap or swipe, so cancel all ongoing events
            $(window).on('scroll', cancelAll)
        })
    
        ;
        ['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown',
            'doubleTap', 'tap', 'singleTap', 'longTap'
        ].forEach(function(eventName) {
            $.fn[eventName] = function(callback) {
                return this.on(eventName, callback)
            }
        })
    })(Zepto)
  • 相关阅读:
    字符串的输入输出 附带一道练习题
    NOIP2009 1.多项式输出
    算法--欧几里得
    小程序:2048
    虚函数和多态
    c++学习记录(十五)
    面向对象程序设计寒假作业3
    c++学习记录(十四)
    c++学习记录(十三)
    c++学习记录(十二)
  • 原文地址:https://www.cnblogs.com/Bond/p/4212918.html
Copyright © 2011-2022 走看看