webapp之滑动组件(基于zepto)

一直都没有封装过一套移动web下的滑动组件,正巧今天公司没事,就闲下来写了一个... 

;
(function($) {
    $.fn.slider = function(opts) {
        this.each(function() {
            init.call(this, opts);
        });
        return this;
    };

    function init(opts) {
        var defaults = {
            contWidth: false, //容器宽度,默认屏幕宽
            contHeight: false, //容器高度,默认屏幕高
            dots: false,
            loop: false,
            autoplay: false,
            lazyload: false, //是否开启图片懒加载,默认关闭
            spacing: 5000, //自动播放间隔时长
            aniTime: 200, //每帧动画时间
            ifAjax: false, //ajax传图生成html,默认false
            ajaxUrl: '', //当开启ajax后,需传入用以构建的json
            ifMiddle: false //是否开启图片垂直居中
        };

        var options = $.extend({}, defaults, opts);

        var index = 0, //图片索引
            loopIndex = 0, //循环是的索引
            prefix = '', //css前缀
            testEl = document.createElement('div'),
            vendors = {
                Webkit: 'webkit',
                Moz: '',
                O: 'o'
            },
            cWidth = options.contWidth ? options.contWidth : $(window).width(), //容器宽度
            cHeight = options.contHeight ? options.contHeight : $(window).height(), //容器高度
            cScale = cWidth / cHeight,
            self = this,
            currentX = 0,
            slopeSwitch = true,
            defaultSwitch = true,
            eleNum = 1,
            loop = options.loop,
            startX, startY, offsetX, offsetY, startTime, endTime, autoTime;

        $.each(vendors, function(vendor) { //生成css前缀
            if (testEl.style[vendor + 'TransitionProperty'] !== undefined) {
                prefix = '-' + vendor.toLowerCase() + '-'
                return false
            }
        })

        var utils = {
            render: function() { //如果给定json,则渲染
                var hm = '',
                    ele = options.ajaxUrl,
                    eleLen = ele.length,
                    h_li = $('li'),
                    picUrl = 'src',
                    i = 0;
                for (; i < eleLen; i++) {
                    if (options.lazyload) {
                        picUrl = 'data-src'
                    }
                    hm += '<li>' + '<img ' + picUrl + '="' + ele[i].img + '" data-width="' + ele[i].width + '" data-height="' + ele[i].height + '" /></li>'
                }
                $(self).html('<ul>' + hm + '</ul>')
            },
            imgResize: function() { //重置图片,把超出限制宽(高)度的图片都缩小
                var imgOriginalWidth,
                    imgOriginalHeight,
                    imgScale,
                    imgNewWidth,
                    imgNewHeight,
                    _this = this;
                $('li', self).find('img').each(function(i) {
                    imgOriginalWidth = _this.getImgWidth($(this))
                    imgOriginalHeight = _this.getImgHeight($(this))
                    imgScale = imgOriginalWidth / imgOriginalHeight
                    if (imgScale >= cScale) { //利用长宽比,计算该用宽度限制还是长度限制
                        imgNewWidth = imgOriginalWidth >= cWidth ? cWidth : imgOriginalWidth
                        $(this).attr('width', imgNewWidth)
                        $(this).attr('height', imgNewWidth / imgScale) //这里的高度一定要赋值,否则高度居中的时候获取不到值
                    } else {
                        imgNewHeight = imgOriginalHeight >= cHeight ? cHeight : imgOriginalHeight
                        $(this).attr('height', imgNewHeight)
                    }
                    $(this).parent().width(cWidth)
                });
                if (options.ifMiddle) {
                    this.setImgMiddle()
                }
                this.setUlWidth()
                this.imgLazyload(0)
            },
            imgLazyload: function(index) {
                var ele = $('li', self).find('img').eq(index),
                    url = ele.attr('data-src')
                if (url != undefined) {
                    ele.attr('src', url)
                    ele.removeAttr('data-src')
                } else {
                    return false
                }
            },
            cloneImg: function() {
                var c1 = $('li', self).last().clone(),
                    c2 = $('li', self).first().clone();
                $('ul', self).prepend(c1)
                $('ul', self).append(c2)
            },
            setImgMiddle: function() {
                var _this = this,
                    imgHeight, offsetHeight;
                $('li', self).find('img').each(function(i) {
                    imgHeight = $(this).height()
                    offsetHeight = cHeight - imgHeight
                    if (offsetHeight != 0) {
                        $(this).css('margin-top', offsetHeight / 2)
                    }
                })
            },
            getImgWidth: function(pic) {
                return pic.attr('data-width')
            },
            getImgHeight: function(pic) {
                return pic.attr('data-height')
            },
            setUlWidth: function() {
                var slideEle = $(self).find('ul');
                eleNum = this.getElementNum(); //设置ul宽度时,确认元素个数,并保存在闭包变量中
                slideEle.width(cWidth * eleNum)
                if (loop) {
                    loopIndex = 1;
                    slideEle.width(cWidth * (eleNum + 2))
                    slideEle.height(cHeight)
                    currentX = -cWidth * loopIndex
                    this.gotoAnimate('0ms', currentX)
                }
            },
            getElementNum: function() {
                return $(self).find('li').size()
            },
            setAutoplay: function() {
                var _this = this
                if (options.autoplay) {
                    autoTime = setInterval(function() {
                        _this.goToIndex(1)
                    }, options.spacing)
                }
            },
            clearAutoplay: function() {
                var _this = this
                if (options.autoplay) {
                    clearInterval(autoTime)
                }
            },
            dotsRender: function() {
                var lists = '',
                    i = 0;
                for (; i < eleNum; i++) {
                    lists += '<span>' + (i + 1) + '</span>'
                }
                $(self).append('<div class="dots">' + lists + '</div>')
                this.dotsOn(0)
            },
            dotsOn: function(i) {
                var $dots = $('.dots', self).find('span')
                $dots.removeClass('cur')
                $dots.eq(i).addClass('cur')
            },
            bindEvents: function() {
                var _this = this,
                    slideEle = $(self).find('ul');
                slideEle.on('touchstart', _this.touchStart)
                slideEle.on('touchmove', _this.touchMove)
                slideEle.on('touchend', _this.touchEnd)
                this.setAutoplay()
            },
            touchStart: function(e) {
                var slideEle = $(self).find('ul'),
                    nowTime = new Date();
                slopeSwitch = true;
                defaultSwitch = true;
                startTime = nowTime.getTime()
                startX = e.touches[0].pageX
                startY = e.touches[0].pageY
                slideEle.css(prefix + 'transition', prefix + 'transform 0ms ease-out')
                utils.clearAutoplay()
            },
            touchMove: function(e) {
                var slope = (startX - e.touches[0].pageX) / (startY - e.touches[0].pageY),
                    u = 2; //减速因素,越大减速越明显
                if (defaultSwitch) { //如果判定已经为浏览器默认滚动,则跳过流程
                    if (Math.abs(slope) < 0.8 && slopeSwitch) { //slopeSwitch控制当判定为图片轮播时,slope判定失效
                        defaultSwitch = false
                    } else {
                        slopeSwitch = false
                        var slideEle = $(self).find('ul')
                        offsetX = e.touches[0].pageX - startX
                        if (!loop) { //如果不是循环滚动,边界设置减速
                            if (index <= 0 && offsetX > 0) {
                                offsetX = offsetX / u
                            } else if (index >= eleNum - 1 && offsetX < 0) {
                                offsetX = offsetX / u
                            }
                        }
                        slideEle.css(prefix + 'transform', 'translate3d(' + (currentX + offsetX) + 'px, 0,0)')
                        e.preventDefault()
                    }
                }
            },
            touchEnd: function(e) {
                // endX = e.changedTouches[0].pageX
                // endY = e.changedTouches[0].pageY
                if (defaultSwitch) {
                    var nowTime = new Date(),
                        duration = 0;
                    endTime = nowTime.getTime()
                    duration = endTime - startTime
                    if (duration > 300) { //手指拨的慢
                        if (Math.abs(offsetX) >= cWidth / 2) {
                            if (offsetX < 0) {
                                utils.goToIndex(1)
                            }
                            if (offsetX > 0) {
                                utils.goToIndex(-1)
                            }
                        } else {
                            utils.goToIndex(0)
                        }
                    } else { //手指划的快
                        if (offsetX < -50) {
                            utils.goToIndex(1)
                        } else if (offsetX > 50) {
                            utils.goToIndex(-1)
                        } else {
                            utils.goToIndex(0)
                        }
                    }
                    offsetX = 0 //当结束后,重置offsetX,避免影响后续流程
                }
                utils.setAutoplay()
            },
            goToIndex: function(i) {
                var _this = this,
                    tweentime = options.aniTime;
                if (!loop) {
                    currentX = 0;
                    if (i > 0) {
                        index++
                        index = index >= eleNum ? eleNum - 1 : index
                    } else if (i < 0) {
                        index--
                        index = index <= 0 ? 0 : index
                    }
                    currentX = -(cWidth * index)
                    this.imgLazyload(index)
                    _this.gotoAnimate(tweentime + 'ms', currentX)
                } else { //循环时
                    if (i > 0) {
                        index++
                        loopIndex++
                        index = index >= eleNum ? 0 : index
                        if (loopIndex >= eleNum + 1) {
                            loopIndex = 1
                            currentX = -(cWidth * loopIndex)
                            _this.gotoAnimate(tweentime + 'ms', -(cWidth * (eleNum + 1))) //先跳转至clone的图片
                            setTimeout(function() {
                                _this.gotoAnimate('0ms', currentX) //时间到之后再变成正常的切换图片
                            }, tweentime)
                        } else {
                            currentX = -(cWidth * loopIndex)
                            _this.gotoAnimate(tweentime + 'ms', currentX)
                        }
                    } else if (i < 0) {
                        index--
                        loopIndex--
                        index = index < 0 ? eleNum - 1 : index
                        if (loopIndex < 1) {
                            loopIndex = eleNum
                            this.imgLazyload(0) //特殊情况处理,第0张图片的lazyload
                            currentX = -(cWidth * loopIndex)
                            _this.gotoAnimate(tweentime + 'ms', 0) //先跳转至clone的图片
                            setTimeout(function() {
                                _this.gotoAnimate('0ms', currentX) //时间到之后再变成正常的切换图片
                            }, tweentime)
                        } else {
                            currentX = -(cWidth * loopIndex)
                            _this.gotoAnimate(tweentime + 'ms', currentX)
                        }
                    } else {
                        _this.gotoAnimate(tweentime + 'ms', currentX)
                    }
                    this.imgLazyload(loopIndex)
                }
                this.dotsOn(index)
            },
            gotoAnimate: function(sp, cx) {
                var slideEle = $(self).find('ul');
                slideEle.css(prefix + 'transition', prefix + 'transform ' + sp + ' ease-out')
                slideEle.css(prefix + 'transform', 'translate3d(' + cx + 'px, 0,0)')
            }
        };

        var initialize = (function() { //初始化
            if (options.ifAjax) {
                utils.render()
            }
            utils.imgResize()
            utils.bindEvents()
            if (options.dots) {
                utils.dotsRender()
            }
            if (loop) {
                utils.cloneImg()
            }
        })()
    }
})(Zepto || jQuery)

其实移动端的滑动组件编写起来与电脑上的还是有很大差异的,对于手指滑动的角度问题,动画需要使用css3,还有手指快速滑动图片切换的效果等等,这些都是电脑上不会遇到的问题。等以后有空了详细分析下怎么写滑动组件以及开发过程中会遇到的问题吧。

demo下载

原文地址:https://www.cnblogs.com/junhua/p/4464412.html