【Javascript】手机滑动应用

2012-05-18

完善版本参见Github

https://github.com/xesam/TouchSlide

简介

浏览器的动画效果一般都是用js来控制元素的 top,left,right,bottom 等属性来实现,不过在移动浏览器上,鉴于对css3的支持,完全可以抢先使用css3 translate。 不过需要注意的是,使用css translate在android上比较那个啥XX,在safari上,transalte2d的效果远远不如translate3d。 所以,移动浏览器上,最好是使用translate3d来实现。

手机滑动事件处理主要使用的是一个touch事件,在iOS上还有gusture事件,不过android现在还很悲剧。具体可以参考apple开发论坛,里面有详细说明。

当我们点击一个元素时,touchstart会最先触发,出发顺序:

touchstart —— mousedown —— click

测试代码

<div id="test" style="width: 100%; height: 200px; background: red;"></div>
<div id="result"></div>
<script type="text/javascript">
    var Time = {};
    document.getElementById("test").addEventListener('touchstart',function(){
        Time.t1 = (new Date()).getTime();
    },false);
    document.getElementById("test").addEventListener('mousedown',function(){
        Time.t2 = (new Date()).getTime();
    },false);
    document.getElementById("test").addEventListener('click',function(){
        Time.t3 = (new Date()).getTime();
        document.getElementById("result").innerHTML = 'touchstart - mousedown = ' + (Time.t2 - Time.t1) + '<br />'
                'mousedown - click = ' + (Time.t3 - Time.t2) ;
    },false);
</script>

测试的各个浏览器版本越低,click相对 touchstart 的延迟越高

具体的滑动实现(一个swipe.js的简化版本):

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title></title>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <style type="text/css">
        *{margin: 0; padding: 0;}
        body{ width: 100%;}
        ul,li{ list-style: none;}
        input[type="button"]{ margin: 10px; width: 40px; height: 24px;}
        #page{ text-align: center;}
        .demo{ width: 100%; height: 200px; overflow: hidden;}
        .list{}
        .list li{ display: table-cell; height: 200px; }
        .list li:first-child{ background: #87ceeb;}
        .list li:last-child{ background: #8b4513;}
    </style>
</head>
<body>
<div id="page">
    <div class="demo">
        <ul class="list">
            <li>1</li>
            <li>2</li>
        </ul>
    </div>
    <div class="demo">
        <ul class="list">
            <li>3</li>
            <li>4</li>
        </ul>
    </div>
</div>
<script type="text/javascript">

    window.TouchSlide = function(container){
        if(!container){ //没有外包装,直接返回
            return 1;
        }
        this.container = this._$(container);
        this.element   = this.container.children[0];
        this.slides    = this.element.children;
        this.index     = 0;
        this.init();

        var _this = this;

        this.element.addEventListener('touchstart',function(e){
            _this.touchstart(e);
        },false);
        this.element.addEventListener('touchmove',function(e){
            _this.touchmove(e);
        },false)
        this.element.addEventListener('touchend',function(e){
            _this.touchend(e);
        },false)
        window.addEventListener('resize', function(e){ //缩放屏幕的时候需要动态调整
            _this.init();
        }, false);
    }
    TouchSlide.prototype = {
        constructor : TouchSlide,
        _$ : function(el){
            return 'string' == el ? document.getElementById(id) : el;
        },
        init : function(){
            this.container.style.visibility = 'none';
            this.width = this.container.getBoundingClientRect().width;
            this.element.style.width = this.slides.length * this.width + 'px';
            var index = this.slides.length;
            while(index--){
                this.slides[index].style.width = this.width + 'px';
            }
            this.container.style.visibility = 'visible';
        },
        slideTo : function(index, duration) {
            this.move(0,index,duration);
            this.index = index;
        },
        move : function(deltaX,index,duration){
            var style = this.element.style;
            style.webkitTransitionDuration = duration + 'ms';
            style.webkitTransform = 'translate3d(' + ( deltaX - index * this.width) + 'px,0,0)';
        },
        isValidSlide : function(){
            return Number(new Date()) - this.start.time < 250 && Math.abs(this.deltaX) > 20 //在250ms内滑动的距离超过20px
                    || Math.abs(this.deltaX) > this.width/2 //或者滑动超过容器的一半宽度
        },
        isPastBounds : function(){
            return !this.index && this.deltaX > 0 //第一个,但是依旧向右滑动
                    || this.index == this.slides.length - 1 && this.deltaX < 0//最后一个,但是依旧向左滑动,这两种情况越界了,是无效的
        },
        touchstart : function(e){
            var touchEvent = e.touches[0];
            this.deltaX = 0;
            this.start = {
                x    : touchEvent.pageX,
                y    : touchEvent.pageY,
                time : Number(new Date())
            } ;
            this.isScrolling = undefined;
            this.element.style.webkitTransitionDuration = 0;
        },
        touchmove : function(e){
            this.deltaX = e.touches[0].pageX - this.start.x;
            //判断是左右滑动还是上下滑动,上下滑动的话就无视
            if(typeof this.isScrolling == 'undefined'){
                this.isScrolling = !!( this.isScrolling || Math.abs(this.deltaX) < Math.abs(e.touches[0].pageY - this.start.pageY) );//判断是否是是竖直滚动
            }
            if(!this.isScrolling){
                e.preventDefault();
                this.deltaX = this.deltaX / (this.isPastBounds() ? 2 : 1);
            }
            this.move(this.deltaX,this.index,0);
        },
        touchend : function(e){
            if (!this.isScrolling) {
                this.slideTo( this.index + ( this.isValidSlide() && !this.isPastBounds() ? (this.deltaX < 0 ? 1 : -1) : 0 ), 200 );
            }
        }
    }

    Array.prototype.slice.call(document.getElementsByClassName('demo'),0).forEach(function(item){
        new TouchSlide(item)
    })

</script>
</body>
</html>

如有问题,请咨询QQ群 723245925


文档信息 by XiaoPingYuan

版权声明:自由转载-非商用-非衍生-保持署名。发表日期:2012-05-18 by XiaoPingYuan(https://xesam.github.io/)