练习 - javascript 触摸滑动程序

Posted myrocknroll

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了练习 - javascript 触摸滑动程序相关的知识,希望对你有一定的参考价值。

addTouchListWrapper

支持垂直、水平滑动、tab、轮播图等

function addTouchListWrapper( option ) {
        //事件注册给哪个对象?默认document
        var d = option.target == undefined ? document : option.target;

        //指示线与ul滑动距离比
        var scale = option.scale ? option.scale : 1;

        //是单点还是多点触控,支持single、more,默认more
        var isMorePointTouch = option.isMorePointTouch == undefined ? true : option.isMorePointTouch;

        //滑动类型,取值:垂直tab、水平tab、垂直轮播、水平轮播
        var slideType = option.slideType;

        //导航小圆点的父元素
        var circle = option.circle == undefined ? undefined : option.circle;

        //是否自动轮播
        var autoPlay = option.autoPlay == undefined ? false : option.autoPlay;

        //tab指示线
        var tabLine = option.tabLine;

        //滑动前tab指示线上面的a元素
        var slideNowElm = null;

        //内容
        var ul = option.ul;

        //滑动前显示在屏幕以内的li
        var slideNowElmLi = null;

        //滑动定时器
        var ms = 0;

        //轮播图定时器
        var timmer = null;

        //手指触碰屏幕的X的起始位置
        var startX = 0;

        //tab指示线上一次滑动的translateX
        var lastTransLateX_hr = null;

        //内容上一次滑动的translateX
        var lastTransLateX_ul = null;

        //记录屏幕上所有滑动信息的数组
        var touchInformation = [];

        //自定义索引
        var index = -1;

        //上一个滑动的touc对象
        var movingTouch = null;

        //上一次滑动后的坐标
        var beforeDistansX = null;

        //touch对象唯一标识
        var touch_identity = null;

        //屏幕上的最新触摸点
        var lastTouch = null;

        //chrome passive
        var passive = { passive: true };


        function play() {
            //定时滑动
            timmer = setInterval( function () {

                shuffling();
            }, 1000 );
        }

        //轮播图无缝结合
        function set_shuffling() {

            var bool = slideType.includes( "水平" );
            var t;
            //第一个元素前面的最后一个元素
            if ( slideNowElmLi.getAttribute( "index" ) == "last" ) {

                t = bool ? ul.querySelector( "li:last-child" ).previousElementSibling.offsetLeft
                    : ul.querySelector( "li:last-child" ).previousElementSibling.offsetTop;
                //替换为最后一个元素
                ul.style.transitionDuration = "0ms";

                ul.style.transform = ( bool ? "translateX" : "translateY" ) + "(-" + t + "px)";
                slidebeforeTranslate_ul = t;
                //更改slideNowElmLi为替换后的当前元素
                slideNowElmLi = getNowElement( ul, ul.querySelectorAll( "li" ), bool ? 0 : 1 );

            }
            //最后一个元素后面的第一个元素
            else if ( slideNowElmLi.getAttribute( "index" ) == "first" ) {

                t = bool ? ul.querySelector( "li:first-child" ).nextElementSibling.offsetLeft
                    : ul.querySelector( "li:first-child" ).nextElementSibling.offsetTop;
                //替换为第一个元素
                ul.style.transitionDuration = "0ms";

                ul.style.transform = bool ? "translateX(-" + t + "px)" : "translateY(-" + t + "px)";
                slidebeforeTranslate_ul = t;
                //更改slideNowElmLi为替换后的当前元素
                slideNowElmLi = getNowElement( ul, ul.querySelectorAll( "li" ), bool ? 0 : 1 );

            }
        }

        //自动轮播
        function shuffling() {

            var bool = slideType.includes( "水平" );
            var distans_circle;
            //当前屏幕上的li元素
            slideNowElmLi = getNowElement( ul, ul.querySelectorAll( "li" ), bool ? 0 : 1 );

            set_shuffling();
            ul.style.transitionDuration = "300ms";
            ul.style.transform = bool ? "translateX(-" + slideNowElmLi.nextElementSibling.offsetLeft + "px)" : "translateY(-" + slideNowElmLi.nextElementSibling.offsetTop + "px)";
            if ( !circle ) return;
            var circleIndex = slideNowElmLi.nextElementSibling.getAttribute( "circleIndex" );
            bool ? distans_circle = circle.parentNode.querySelectorAll( "li" )[circleIndex].offsetLeft - parseFloat( getComputedStyle( circle ).marginLeft )
                : distans_circle = circle.parentNode.querySelectorAll( "li" )[circleIndex].offsetTop - parseFloat( getComputedStyle( circle ).marginTop );
            circle.style.transform = ( bool ? "translateX" : "translateY" ) + "(" + distans_circle + "px)";
        }

        //为第一张图和最后一张图的无缝结合动态添加节点
        function addChild() {

            var bool = slideType.includes( "水平" );
            var first = ul.querySelector( "li:first-child" );
            var last = ul.querySelector( "li:last-child" );
            var newfirst = first.cloneNode( true );
            newfirst.setAttribute( "index", "first" );
            ul.appendChild( newfirst );
            var newlast = last.cloneNode( true );
            newlast.setAttribute( "index", "last" );
            ul.prepend( newlast );
            //将ul定位到第一张图片
            ul.style.transitionDuration = "0s";

            ul.style.transform = bool ? "translateX( -" + ul.parentNode.offsetWidth + "px)" : "translateY( -" + ul.parentNode.offsetHeight + "px)";
        }

        if ( slideType.includes( "轮播" ) ) {
            addChild();
        }

        //启用自动轮播
        if ( autoPlay ) play();


        //如果当前滑动元素的translateX绝对等于参数2指定的元素与父元素的左边距,则返回真
        function testRange( moving, elm, transType ) {

            var movingTranslate;
            if ( transType == 1 ) {
                movingTranslate = moving.style.transform.replace( "translateY(", ‘‘ ).replace( "px)", ‘‘ ) == false ? 0 : parseFloat( moving.style.transform.replace( "translateY(", ‘‘ ).replace( "px)", ‘‘ ) );
                if ( Math.abs( movingTranslate ) == elm.offsetTop ) return true;
            }
            else {
                movingTranslate = moving.style.transform.replace( "translateX(", ‘‘ ).replace( "px)", ‘‘ ) == false ? 0 : parseFloat( moving.style.transform.replace( "translateX(", ‘‘ ).replace( "px)", ‘‘ ) );
                if ( Math.abs( movingTranslate ) == elm.offsetLeft ) return true;
            }
        }

        //获取当前指示线上面的a元素或屏幕以内的li元素
        function getNowElement( moving, chiList, transType ) {

            return Array.from( chiList ).filter( elm => {
                if ( testRange( moving, elm, transType ) ) return elm;
            } )[0];
        }

        //根据方向获取元素的translate
        function getTranslaste( el, direction ) {

            if ( direction == 1 ) {
                return el.style.transform.replace( "translateY(", ‘‘ ).replace( "px)", ‘‘ ) == false ? 0 : parseFloat( el.style.transform.replace( "translateY(", ‘‘ ).replace( "px)", ‘‘ ) );
            }
            return el.style.transform.replace( "translateX(", ‘‘ ).replace( "px)", ‘‘ ) == false ? 0 : parseFloat( el.style.transform.replace( "translateX(", ‘‘ ).replace( "px)", ‘‘ ) );
        }

        //滑动中
        function slideX( touch ) {

            if ( touch.moveX - beforeDistansX == 0 ) { return; }
            var ulDistans = 0;
            var distans = 0;
            var ulTranslate;
            var bool = slideType.includes( "水平" );
            if ( slideType.includes( "tab" ) ) {
                var hrTranslate;
                if ( slideType.includes( "水平" ) ) hrTranslate = getTranslaste( tabLine, 0 );
                else hrTranslate = getTranslaste( tabLine, 1 );
            }
            if ( slideType.includes( "水平" ) ) ulTranslate = getTranslaste( ul, 0 );
            else ulTranslate = getTranslaste( ul, 1 );
            movingTouch = touch.touch_identity;
            //手指左滑
            if ( touch.moveX - beforeDistansX < 0 ) {

                console.log( "手指左滑或上滑" );
                //即时记录手指左滑的坐标,右滑(上滑)时用最后一个左滑坐标作为起始坐标计算左滑距离
                touch.slideLeftEndPoint = touch.moveX;

                touch.leftSlideTranslateX = hrTranslate == undefined ? null : hrTranslate;
                touch.leftSlideTranslateX_ul = ulTranslate;
                //最后一个元素不能左滑(上滑)
                var translate = hrTranslate != undefined ? hrTranslate : ulTranslate;

                var nowElm = hrTranslate != undefined ? slideNowElm : slideNowElmLi;
                if ( translate != parseFloat( slideType.includes( "水平" ) ? nowElm.parentNode.offsetWidth : nowElm.parentNode.offsetHeight ) - parseFloat( slideType.includes( "水平" ) ? nowElm.offsetWidth : nowElm.offsetHeight ) ) {
                    var startPoint = touch.slideRightEndPoint == null ? touch.startX : touch.slideRightEndPoint;
                    if ( slideType.includes( "tab" ) ) {
                        distans = ( touch.rightSlideTranslateX == null ? touch.translateX_tabline : touch.rightSlideTranslateX ) + Math.abs( touch.moveX - startPoint ) * scale;
                        var slideAfterSibling = slideNowElm.nextElementSibling.nodeName.toLowerCase() == "a" ? slideNowElm.nextElementSibling : slideNowElm;
                        var leftOrTop = slideType.includes( "水平" ) ? slideAfterSibling.offsetLeft : slideAfterSibling.offsetTop;
                        if ( distans >= leftOrTop ) distans = leftOrTop;
                        console.log( distans );
                        slideType.includes( "水平" ) ? tabLine.style.transform = "translateX(" + distans + "px)" : tabLine.style.transform = "translateY(" + distans + "px)";
                    }
                    ulDistans = -( touch.rightSlideTranslateX_ul == null ? Math.abs( touch.translateX_ul ) : Math.abs( touch.rightSlideTranslateX_ul ) ) + ( touch.moveX - startPoint );
                    console.log( touch.translateX_ul );
                    var slideAfterSiblingLi = slideNowElmLi.nextElementSibling == null ? slideNowElmLi : slideNowElmLi.nextElementSibling;
                    var leftOrTop = slideType.includes( "水平" ) ? slideAfterSiblingLi.offsetLeft : slideAfterSiblingLi.offsetTop;
                    if ( Math.abs( ulDistans ) >= leftOrTop ) ulDistans = -leftOrTop;
                    ul.style.transform = ( bool ? "translateX" : "translateY" ) + "(" + ulDistans + "px)";
                }
            }
            //手指右滑
            else if ( touch.moveX - beforeDistansX > 0 ) {

                console.log( "手指右滑或下滑" );
                //即时记录手指右滑的坐标,左滑时用最后一个右滑坐标作为起始坐标计算右滑距离
                touch.slideRightEndPoint = touch.moveX;

                touch.rightSlideTranslateX = hrTranslate == undefined ? null : hrTranslate;
                touch.rightSlideTranslateX_ul = ulTranslate;
                //第一个元素不能右滑
                var translate = hrTranslate != undefined ? hrTranslate : ulTranslate;

                if ( translate != 0 ) {
                    var startPoint = touch.slideLeftEndPoint == null ? touch.startX : touch.slideLeftEndPoint;
                    if ( slideType.includes( "tab" ) ) {
                        distans = ( touch.leftSlideTranslateX == null ? touch.translateX_tabline : touch.leftSlideTranslateX ) - ( touch.moveX - startPoint ) * scale;
                        var slideBeforeSibling = slideNowElm.previousElementSibling == null ? slideNowElm : slideNowElm.previousElementSibling;
                        var leftOrTop = slideType.includes( "水平" ) ? slideBeforeSibling.offsetLeft : slideBeforeSibling.offsetTop;
                        if ( distans <= leftOrTop ) distans = leftOrTop;
                        slideType.includes( "水平" ) ? tabLine.style.transform = "translateX(" + distans + "px)" : tabLine.style.transform = "translateY(" + distans + "px)";
                    }
                    ulDistans = ( touch.leftSlideTranslateX_ul == null ? Math.abs( touch.translateX_ul ) : Math.abs( touch.leftSlideTranslateX_ul ) ) - ( touch.moveX - startPoint );
                    ulDistans = -ulDistans;
                    var slideBeforeSiblingLi = slideNowElmLi.previousElementSibling == null ? slideNowElmLi : slideNowElmLi.previousElementSibling;
                    var leftOrTop = slideType.includes( "水平" ) ? slideBeforeSiblingLi.offsetLeft : slideBeforeSiblingLi.offsetTop;
                    if ( ulDistans >= -( leftOrTop ) ) ulDistans = -( leftOrTop );
                    ul.style.transform = ( bool ? "translateX" : "translateY" ) + "(" + ulDistans + "px)";
                }
            }
            beforeDistansX = touch.moveX;
            lastTransLateX_hr = hrTranslate == undefined ? null : hrTranslate;
            lastTransLateX_ul = ulTranslate;
        }

        //滑动未至,则原路退回,滑动过半,则自动吸附
        function setRange( msflag ) {

            var distans = 0;
            var ulDistans = 0;
            var circleIndex = null;
            var bool = slideType.includes( "水平" );
            if ( slideType.includes( "tab" ) ) {
                var hrTranslate = bool ? getTranslaste( tabLine, 0 ) : getTranslaste( tabLine, 1 );
                var slideBeforeSibling = slideNowElm.previousElementSibling;
                var slideAfterSibling = slideNowElm.nextElementSibling;
                var afterLeft = slideAfterSibling.nodeName.toLowerCase() != "a" ? null : bool ? parseFloat( slideAfterSibling.offsetLeft ) : parseFloat( slideAfterSibling.offsetTop );
                var nowLeft = bool ? parseFloat( slideNowElm.offsetLeft ) : parseFloat( slideNowElm.offsetTop );
                var beforeLeft = slideBeforeSibling == null ? null : bool ? parseFloat( slideBeforeSibling.offsetLeft ) : parseFloat( slideBeforeSibling.offsetTop );
            }
            var ulTranslate = bool ? Math.abs( getTranslaste( ul, 0 ) ) : Math.abs( getTranslaste( ul, 1 ) );
            var slideBeforeSiblingLi = slideNowElmLi.previousElementSibling == null ? slideNowElmLi : slideNowElmLi.previousElementSibling;
            var slideAfterSiblingLi = slideNowElmLi.nextElementSibling == null ? slideNowElmLi : slideNowElmLi.nextElementSibling;
            var fatherWidth = bool ? parseFloat( ul.parentNode.offsetWidth ) : parseFloat( ul.parentNode.offsetHeight );
            var nowLeft_ul;
            var afterLeft_ul;
            var beforeLeft_ul;
            if ( bool ) {
                nowLeft_ul = slideNowElmLi.offsetLeft;
                afterLeft_ul = slideAfterSiblingLi.offsetLeft;
                beforeLeft_ul = slideBeforeSiblingLi.offsetLeft;
            }
            else {
                nowLeft_ul = slideNowElmLi.offsetTop;
                afterLeft_ul = slideAfterSiblingLi.offsetTop;
                beforeLeft_ul = slideBeforeSiblingLi.offsetTop;
            }
            if ( msflag && ulTranslate > nowLeft_ul ) { distans = afterLeft; ulDistans = afterLeft_ul; circleIndex = slideAfterSiblingLi.getAttribute( "circleIndex" ); }
            else if ( msflag && ulTranslate < nowLeft_ul ) { distans = beforeLeft; ulDistans = beforeLeft_ul; circleIndex = slideBeforeSiblingLi.getAttribute( "circleIndex" ); }
            else if ( ulTranslate - nowLeft_ul > fatherWidth / 2 ) { hrTranslate != undefined ? distans = afterLeft : ""; ulDistans = afterLeft_ul; circleIndex = slideAfterSiblingLi.getAttribute( "circleIndex" ); }
            else if ( ulTranslate - nowLeft_ul < -fatherWidth / 2 ) { distans = beforeLeft; ulDistans = beforeLeft_ul; circleIndex = slideBeforeSiblingLi.getAttribute( "circleIndex" ); }
            else { distans = nowLeft; ulDistans = nowLeft_ul; circleIndex = slideNowElmLi.getAttribute( "circleIndex" ); }
            ul.style.transitionDuration = "300ms";
            if ( bool ) {
                if ( slideType.includes( "tab" ) ) {
                    tabLine.style.transitionDuration = "300ms";
                    tabLine.style.transform = "translateX(" + distans + "px)";
                }
                ul.style.transform = "translateX(" + -ulDistans + "px)";
            }
            else {
                if ( slideType.includes( "tab" ) ) {
                    tabLine.style.transitionDuration = "300ms";
                    tabLine.style.transform = "translateY(" + distans + "px)";
                }
                ul.style.transform = "translateY(" + -ulDistans + "px)";
            }
            if ( circle ) {
                var distans_circle;
                distans_circle = bool ? circle.parentNode.querySelectorAll( "li" )[circleIndex].offsetLeft - parseFloat( getComputedStyle( circle ).marginLeft )
                    : circle.parentNode.querySelectorAll( "li" )[circleIndex].offsetTop - parseFloat( getComputedStyle( circle ).marginTop );
                circle.style.transform = ( bool ? "translateX" : "translateY" ) + "(" + distans_circle + "px)";
            }
            //清空屏幕触控点信息
            touchInformation = [];

            index = -1;
            beforeDistansX = null;
            lastTransLateX_hr = null;
            lastTransLateX_ul = null;
        }

        d.addEventListener( "touchstart", function ( e ) {
            var slidebeforeTranslate_ul = 0;
            var bool = slideType.includes( "水平" );
            if ( getNowElement( ul, ul.querySelectorAll( "li" ), bool ? 0 : 1 ) != null ) {
                //当前屏幕上的li元素
                slideNowElmLi = getNowElement( ul, ul.querySelectorAll( "li" ), bool ? 0 : 1 );

            }
            if ( bool ) {
                beforeDistansX = startX = e.touches[e.touches.length - 1].clientX;
                if ( slideType.includes( "tab" ) ) {
                    if ( getNowElement( tabLine, tabLine.parentNode.querySelectorAll( "a" ), 0 ) != null ) {
                        //当前tab指示线上面的a元素
                        slideNowElm = getNowElement( tabLine, tabLine.parentNode.querySelectorAll( "a" ), 0 );

                    }
                    var slidebeforeTranslate_hr = Math.abs( getTranslaste( tabLine, 0 ) );
                }
                if ( slideType.includes( "轮播" ) ) {
                    if ( autoPlay ) clearInterval( timmer );//触摸时关掉定时滑动
                    set_shuffling();

                }
            }
            else {
                beforeDistansX = startX = e.touches[e.touches.length - 1].clientY;
                if ( slideType.includes( "tab" ) ) {
                    if ( getNowElement( tabLine, tabLine.parentNode.querySelectorAll( "a" ), 1 ) != null ) {
                        //当前tab指示线上面的a元素
                        slideNowElm = getNowElement( tabLine, tabLine.parentNode.querySelectorAll( "a" ), 1 );

                    }
                    var slidebeforeTranslate_hr = Math.abs( getTranslaste( tabLine, 1 ) );
                }
                if ( slideType.includes( "轮播" ) ) {
                    if ( autoPlay ) clearInterval( timmer );//触摸时关掉定时滑动
                    set_shuffling();

                }
            }
            slidebeforeTranslate_ul = Math.abs( getTranslaste( ul, bool ? 0 : 1 ) );
            touch_identity = e.touches[e.touches.length - 1].identifier;
            touchInformation.push( {
                //触摸唯一标识
                touch_identity: touch_identity,

                //自定义索引
                index: ++index,

                //起始坐标
                startX: startX,

                //移动坐标
                moveX: startX,

                //tab指示线的偏移距离
                translateX_tabline: slidebeforeTranslate_hr,

                //ul的偏移距离
                translateX_ul: slidebeforeTranslate_ul,

                //tab指示线左滑实时translateX
                leftSlideTranslateX: null,

                //tab指示线右滑实时translateX
                rightSlideTranslateX: null,

                //ul左滑实时translateX
                leftSlideTranslateX_ul: null,

                //ul右滑时实时偏移距离
                rightSlideTranslateX_ul: null,

                //右滑实时移动点
                slideRightEndPoint: null,

                //左滑实时移动点
                slideLeftEndPoint: null,

                //上一个移动点
                beforeDistansX: startX

            } );
            if ( slideType.includes( "tab" ) ) tabLine.style.transitionDuration = "0ms";
            ul.style.transitionDuration = "0ms";
            ms = new Date().getTime();
        }, passive );

        d.addEventListener( "touchmove", function ( e ) {
            if ( touchInformation == null ) return;
            lastTouch = e.touches[e.touches.length - 1];
            length = touchInformation.length;
            var touch;
            for ( var i = length - 1; i >= 0; i-- ) {
                //只有接触屏幕的最后一根手指才可以滑动
                if ( touchInformation[i].touch_identity == lastTouch.identifier ) {

                    touch = touchInformation[i];
                    break;
                }
            }
            //多点触控
            if ( isMorePointTouch ) {

                if ( lastTouch.identifier != movingTouch ) {
                    if ( slideType.includes( "tab" ) ) touch.translateX_tabline = lastTransLateX_hr == null ? touch.translateX_tabline : lastTransLateX_hr;
                    touch.translateX_ul = lastTransLateX_ul == null ? touch.translateX_ul : lastTransLateX_ul;
                    touch.rightSlideTranslateX = touch.rightSlideTranslateX_ul = touch.leftSlideTranslateX = touch.leftSlideTranslateX_ul = touch.slideLeftEndPoint = touch.slideRightEndPoint = null;
                    if ( slideType.includes( "水平" ) ) touch.startX = lastTouch.clientX;
                    else touch.startX = lastTouch.clientY;
                }
            }
            //单点触控
            else {

                if ( lastTouch.identifier != movingTouch && movingTouch != null ) {
                    setRange();
                    return;
                }
            }
            if ( slideType.includes( "水平" ) ) touch.moveX = lastTouch.clientX;
            else touch.moveX = lastTouch.clientY;
            lastTouch = touch;
            slideX( lastTouch );
        }, passive );

        d.addEventListener( "touchend", function ( e ) {
            //删除touchInformation中抬起的touch对象
            index -= 1;

            //不设置吸附
            if ( slideType.includes( "tab" ) ) {

                if ( getNowElement( tabLine, tabLine.parentNode.querySelectorAll( "a" ), slideType.includes( "水平" ), slideType.includes( "水平" ) ? 0 : 1 ) != null ) {
                    return;
                }
            }
            if ( e.touches.length == 0 ) {
                var flag = new Date().getTime() - ms <= 200;
                setRange( flag );
                if ( slideType.includes( "tab" ) ) tabLine.style.transition = "none 0 ease";
                ul.style.transition = "none 0 ease";
                if ( autoPlay ) {
                    play();
                }
            }
        }, passive );

        d.addEventListener( "touchcancel", function ( e ) {
            setRange();
            if ( slideType.includes( "tab" ) ) tabLine.style.transition = "none 0 ease";
            ul.style.transition = "none 0 ease";
            if ( autoPlay ) {
                play();
            }
        }, passive );
    }
多个触摸点

已实现多点触控,你可以建立第1、第2、第3、第n个触控点,只有最新加入的触控点才可以滑动,除非它离开了屏幕,一旦它离开屏幕,那么最后一个触控点就是紧靠它前面的那个触控点。滑动顺序为n……、3、2、1。另外,按照e.touches对触控点的存储规则,比如前两根手指触控屏幕,接着第一根手指离开屏幕,此时再重新触控屏幕,那么新的手指的触控点会替换掉第一根手指原来的触控点,原来的触控点的索引为0,那么新的触控点的索引就是0(索引覆盖),而不是最后一个索引。这导致新加入的触控点不能滑动,因为它的索引是0,而最后一个触控点的索引是1(第二根手指)。参考:触摸事件 - e.touches。

非全屏滑动

支持非全屏的滑动,但本程序依赖于offsetLeft,该属性需要正确找到元素离父元素的offsetLeft,参照以下各种例子中的position定位设置才行。

六种模式
水平简约

不实现无缝结合。第一张不能右滑,最后一张不能左滑。

* {
    padding0;
    margin0;
    font-size3.3vw;
    color#fff;
    text-decorationnone;
    list-stylenone;
    font-family微软雅黑;
}

.content-wrapper {
    positionrelative;
    width100%;
    overflowhidden;
}

.content-wrapper .content-ul {
    overflowhidden;
    width600%;
}

.content-wrapper .content-ul li {
    floatleft;
    width16.66%;
    height135.74vw;
}

.content-wrapper .content-ul li img {
    width100%;
    height100%;
}

<div class="content-wrapper">
    <ul class="content-ul">
        <li circleIndex="0"><img src="./Img/x1.jpg" /> </li>
        <li circleIndex="1"><img src="./Img/x2.jpg" /></li>
        <li circleIndex="2"><img src="./Img/x3.jpg" /></li>
        <li circleIndex="3"><img src="./Img/x4.jpg" /></li>
    </ul>
</div>

window.onload = function () {
    var $ = document.querySelector.bind( document );
    var ul = $( ".content-ul" );

    addTouchListWrapper( {
        //滑动类型:水平简约、水平tab、水平轮播、垂直简约、垂直tab、垂直轮播
        slideType: "水平简约",
        ul: ul
    } );
}

技术图片 

水平轮播图

实现第一张和最后一张图切换时的无缝结合,不需要在第一张和最后一张前加图片,但你需要在css中把多出的两张图的宽度计算进每个li的宽度百分比。

* {
    padding0;
    margin0;
    font-size3.3vw;
    color#fff;
    text-decorationnone;
    list-stylenone;
    font-family微软雅黑;
}

.content-wrapper {
    positionrelative;
    width100%;
    overflowhidden;
}

.content-wrapper .content-ul {
    overflowhidden;
    width600%; /*一共四张图,但js动态添加了两张,所以是600%*/
}

.content-wrapper .content-ul li {
    floatleft;
    width16.66%/*一共四张图,但js动态添加了两张,每张占比16.66%*/
    height150vw;
}

.content-wrapper .content-ul li img {
    width100%;
}

.content-wrapper .little-circle-ul {
    overflowhidden;
    positionabsolute;
    top30vw;
    left50%;
    transformtranslateX(-50%);
}

.content-wrapper .little-circle-ul li {
    floatleft;
    width3.33vw;
    height3.33vw;
    margin0 2.06vw;
    border0.13vw solid #fff;
    border-radius50%;
}

.little-circle-ul .active {
    positionabsolute;
    width3.33vw;
    height3.33vw;
    background#b6ff00;
}

<div class="content-wrapper">
    <ul class="content-ul">
        <li circleIndex="0"><img src="./Img/x1.jpg" /> </li>
        <li circleIndex="1"><img src="./Img/x2.jpg" /></li>
        <li circleIndex="2"><img src="./Img/x3.jpg" /></li>
        <li circleIndex="3"><img src="./Img/x4.jpg" /></li>
    </ul>
    <ul class="little-circle-ul">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li class="active"></li>
    </ul>
</div>

window.onload = function () {
    var $ = document.querySelector.bind( document );
    var ul = $( ".content-ul" );

    addTouchListWrapper( {
        //滑动类型:水平简约、水平tab、水平轮播、垂直简约、垂直tab、垂直轮播
        slideType: "水平轮播",
        //是否自动轮播,默认false
        autoPlay: true,
        //圆点,如果不提供circle,则轮播时不调用圆点。
        //在此例中,圆点列表是4个,多出的这个circle是额外的,需要把这个circle绝对定位到第一个圆点处,以便滑动程序控制它移动,其它四个圆点起始是保持不动的
        circle: $( ".active" ),
        ul: ul
    } );
}

技术图片

水平tab

不实现无缝结合。第一张不能右滑,最后一张不能左滑。

* {
    padding0;
    margin0;
    font-size3.3vw;
    color#fff;
    text-decorationnone;
    list-stylenone;
    font-family微软雅黑;
}

.tab-box {
    positionrelative;
    width100%;
    height12vw;
    marginauto;
    overflowhidden;
    line-height12vw;
    text-aligncenter;
    background#000;
}

.tab-box a {
    floatleft;
    width25%;
    heightinherit;
}

.tabLine {
    positionabsolute;
    left9.067vw;
    bottom0;
    width7vw;
    height0.5vw;
    background#fff;
}

/*.tabLine {
    position: absolute;
    left:0;
    bottom: 0;
    width: 25vw;
    height: 0.5vw;
    background: #cfff56;
}*/

/*.tabLine {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 25vw;
    height: 12.5vw;
    background: #fff;
    opacity: 0.3;
    z-index: 1;
}*/

.content-wrapper {
    positionrelative;
    width100%;
    overflowhidden;
}

.content-wrapper .content-ul {
    overflowhidden;
    width600%;
}

.content-wrapper .content-ul li {
    floatleft;
    width16.66%;
    height150vw;
}

.content-wrapper .content-ul li img {
    width100%;
}

<div class="tab-box">
    <a href="#" style="background:#2a2b2f;">山魈</a>
    <a href="#" style="background:#9f0105;">寒食</a>
    <a href="#" style="background:#118eb8;">训诂</a>
    <a href="#" style="background:#c4282b;">觞逝</a>
    <div class="tabLine"></div>
</div>

<div class="content-wrapper">
    <ul class="content-ul">
        <li circleIndex="0"><img src="./Img/x1.jpg" /> </li>
        <li circleIndex="1"><img src="./Img/x2.jpg" /></li>
        <li circleIndex="2"><img src="./Img/x3.jpg" /></li>
        <li circleIndex="3"><img src="./Img/x4.jpg" /></li>
    </ul>
</div>

window.onload = function () {
    var $ = document.querySelector.bind( document );
    var tabLine = $( ".tabLine" );
    var ul = $( ".content-ul" );
    var scale = tabLine.parentNode.querySelector( "a" ).offsetWidth / ( window.screen.width ); //tab指示线与ul的滑动距离比

    addTouchListWrapper( {
       //滑动类型:水平简约、水平tab、水平轮播、垂直简约、垂直tab、垂直轮播
        slideType: "水平tab",
       //tab指示线
        tabLine: tabLine,
        ul: ul,
        scale: scale
    } );
}

tab有三种css风格

三种风格可以参照以上注释掉的tabLine的css

========短线========

技术图片

========长线========

技术图片

========框========

 技术图片  

tab注意

1.tab指示线通过position定位到起始位置,css中不要用translateX定位,也不要设置其transition(js里已经做了处理)

2.盒子内所有元素都不要设置padding和margin,只需要设置a元素的宽度,a元素内可以加其他内容

3.指示线定位时可以在chrome浏览器里逐步设置其偏移,直到其位置正确为止。短线风格:tab指示线必须定位到a元素的下面,与a元素离父元素的距离相等才行,例子中的tab指示线定位在第一个a元素的下面,此时它的translateX是0,而a元素离父元素的距离恰好也是0。如果一开始就需要将tab指示线的起始位置定位在非第一个a元素下,不要在css中这样做,你可以在js里在调用滑动函数之前通过编程将tab指示线定位,这样可以保证指示线的起始translateX是在第一个a元素下,否则会出问题。你可以载js中如下处理将tab指示线定位在其它a元素下:

tabLine.style.transform = "translateX(" + $(".tab-box").querySelector( "a" ).offsetWidth + "px)";
ul.style.transform = "translateX(-" + ul.querySelector( "li" ).offsetWidth + "px)";
垂直简约
* {
    padding0;
    margin0;
    font-size3.3vw;
    color#fff;
    text-decorationnone;
    list-stylenone;
    font-family微软雅黑;
}

.content-wrapper {
    positionrelative;
    width100%;
    height135.74vw;
    overflowhidden;
}

.content-wrapper .content-ul {
    overflowhidden;
    width100%;
    height400%;
}

.content-wrapper .content-ul li {
    width100%;
    height135.74vw;
}

.content-wrapper .content-ul li img {
    width100%;
    height100%;
}

<div class="content-wrapper">
    <ul class="content-ul">
        <li circleIndex="0"><img src="./Img/x1.jpg" /> </li>
        <li circleIndex="1"><img src="./Img/x2.jpg" /></li>
        <li circleIndex="2"><img src="./Img/x3.jpg" /></li>
        <li circleIndex="3"><img src="./Img/x4.jpg" /></li>
    </ul>
</div>

window.onload = function () {
    var $ = document.querySelector.bind( document );
    var ul = $( ".content-ul" );

    addTouchListWrapper( {
        //滑动类型:水平简约、水平tab、水平轮播、垂直简约、垂直tab、垂直轮播
        slideType: "垂直简约",
        ul: ul
    } );
}

技术图片   

垂直轮播图
* {
    padding0;
    margin0;
    font-size3.3vw;
    color#fff;
    text-decorationnone;
    list-stylenone;
    font-family微软雅黑;
}

.content-wrapper {
    positionrelative;
    width100%;
    height135.74vw;
    overflowhidden;
}

.content-wrapper .content-ul {
    overflowhidden;
    width100%;
    height600%;
}

.content-wrapper .content-ul li {
    width100%;
    height16.66%;
}

.content-wrapper .content-ul li img {
    width100%;
    height100%;
}

.content-wrapper .little-circle-ul {
    overflowhidden;
    positionabsolute;
    top50%;
    left50%;
    transformtranslate(-50%,-50%);
}

.content-wrapper .little-circle-ul li {
    positionrelative;
    width2.49vw;
    height2.49vw;
    margin3vw 0;
    border-radius50%;
    border0.5vw solid #fff;
}

.content-wrapper .little-circle-ul .active {
    positionabsolute;
    top0;
    background#8f00ff;
}

<div class="content-wrapper">
    <ul class="content-ul">
        <li circleIndex="0"><img src="./Img/x1.jpg" /> </li>
        <li circleIndex="1"><img src="./Img/x2.jpg" /></li>
        <li circleIndex="2"><img src="./Img/x3.jpg" /></li>
        <li circleIndex="3"><img src="./Img/x4.jpg" /></li>
    </ul>

    <ul class="little-circle-ul">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li class="active"></li>
    </ul>
</div>

window.onload = function () {
    var $ = document.querySelector.bind( document );
    var ul = $( ".content-ul" );

    addTouchListWrapper( {
        //滑动类型:水平简约、水平tab、水平轮播、垂直简约、垂直tab、垂直轮播
        slideType: "垂直轮播",
        //是否自动轮播,默认false
        autoPlay: true,
        //圆点,如果不设置circle,则轮播时不调用圆点。
        //在此例中,圆点列表是4个,多出的这个circle是额外的,需要把circle绝对定位到第一个圆点处,以便滑动程序控制它移动,其它四个圆点保持不动
        circle: $( ".active" ),
        ul: ul
    } );
}

技术图片

垂直tab
* {
    padding0;
    margin0;
    font-size3.3vw;
    color#fff;
    text-decorationnone;
    list-stylenone;
    font-family微软雅黑;
}

.tab-box {
    positionfixed;
    right0;
    width12.07vw;
    z-index1;
}

.tab-box a {
    displayblock;
    border-radius:12.62vw;
    width12.07vw;
    height12.07vw;
}

.tab-box a div {
    width3.3vw;
    heightinherit;
    marginauto;
    padding-top7px;
    box-sizingborder-box;
}

/*.tabLine {
    position: absolute;
    top: 11px;
    bottom: 0;
    width: 0.5vw;
    height: 7vw;
    background: #fff;
}*/


/*.tabLine {
    position: absolute;
    top:0;
    width: 0.5vw;
    height: 12.07vw;
    background: #fff;
}*/


.tabLine {
    positionabsolute;
    top:0;
    width12.07vw;
    height12.07vw;
    background#ff3700;
    opacity0.25;
    z-index1;
    border-radius:12.62vw;
}

.content-wrapper {
    positionrelative;
    width100%;
    height135.74vw;
    overflowhidden;
}

.content-wrapper .content-ul {
    overflowhidden;
    width100%;
    height400%;
}

.content-wrapper .content-ul li {
    width100%;
    height135.74vw;
}

.content-wrapper .content-ul li img {
    width100%;
    height100%;
}
    </style>
</head>
<body>
<div class="tab-box">
    <a href="#"><div>山魈</div></a>
    <a href="#"><div>寒食</div></a>
    <a href="#"><div>训诂</div></a>
    <a href="#"><div>觞逝</div></a>
    <div class="tabLine"></div>
</div>

<div class="content-wrapper">
    <ul class="content-ul">
        <li circleIndex="0"><img src="./Img/x1.jpg" /> </li>
        <li circleIndex="1"><img src="./Img/x2.jpg" /></li>
        <li circleIndex="2"><img src="./Img/x3.jpg" /></li>
        <li circleIndex="3"><img src="./Img/x4.jpg" /></li>
    </ul>
</div>
window.onload = function () {
    var $ = document.querySelector.bind( document );
    var tabLine = $( ".tabLine" );
    var ul = $( ".content-ul" );
    var scale = tabLine.parentNode.querySelector( "a" ).offsetHeight / ( ul.parentNode.offsetHeight ); //tab指示线与ul的滑动距离比

    addTouchListWrapper( {
       //滑动类型:水平简约、水平tab、水平轮播、垂直简约、垂直tab、垂直轮播
        slideType:"垂直tab",
        tabLine: tabLine,
        ul: ul,
        scale: scale           
    } );
}

技术图片 

源代码下载:列表滑动

练习 - 学习总目录

以上是关于练习 - javascript 触摸滑动程序的主要内容,如果未能解决你的问题,请参考以下文章

Javascript 触摸对象

如何在离子应用程序中检测多指触摸

需要延迟触摸 3 指滑动而不是 1 指滑动

微信小程序——左右滑动切换页面

使用 Javascript 检测触摸屏设备

JavaScript中的Touch事件简介