js原生轮播图封装思想

1,171 阅读3分钟

插件版轮播图:渐隐渐现

插件封装的思想

基于面向对象的方式来处理

  1. 调取一次插件相当于创建插件的一个实例

  2. 这样私有的可以设置,公有的方法也可以设置

  3. 我们封装公共方法的时候,如果需要传递的参数过多(超过两个就可以理解为多了),则不要定义形参,让用户依次传递,这样会受到顺序、传或者不传等因素的影响,管理起来很复杂。我们可以把需要传递的值统一放到一个对象中(一般都是options),这样我们传递的信息可传可不传,顺序也随便,最后把传递的信息覆盖默认的信息即可,管理方便。也方便进行二次扩展

  4. 我们把后期需要用到的信息都挂载(给谁谁谁设置了啥啥啥)到当前的实例上,这样后面不管在那个方法中用这些信息,只要能获取到实例,直接通过实例获取既可

  5. 本插件中需要使用的工具类方法,我们尽量都放到类的私有属性上(普通对象)

枚举属性

可枚举属性、不可枚举属性

Object.prototype.AAA = 'zhufeng'
let obj = {
    name:'zhufeng',
    year:10
    // __protp__:Object.prototype
};
for (let key in obj) {
    if(!obj.hasOwnProperty(key)) break;
    // 防止别人在原型上扩展的方法,是我们不需要的
    console.log(key);
}

在 for in 遍历循环的时候,可以被迭代到的属性是可枚举属性,反之是不可枚举的属性。 可枚举的:一般是自己设置的私有属性和方法或者自己设置的共有属性和方法 不可枚举的:js中基本包装类型的原型属性是不可枚举的,如Object, Array, Number等

向jQuery扩展插件

JQUERY:extend  向JQ内部扩展方法

// 向JQ的原型上扩展方法[写插件]
 $.fn.extend({ xxx:function () {} }) 
 $('.box').xxx();
 // 向JQ对象中增加私有的属性方法[完善类库,提供更多工具类方法]
 $.extend({ xxx:function () {} })
 $.xxx();

代码部分

~ function ($) {
    if (typeof $ === 'undefined') {
        // throw new Error() 抛出浏览器异常信息,此时下面代码不在执行
        throw new Error('当前插件必须依托JQUERY才可以实现~~');
    }
    // THROTTLE:函数节流
    function throttle(func, wait) {
        let timer = null,
            result = null,
            previous = 0;
        return function anonymous(...args) {
            let context = this,
                now = new Date,
                spanTime = wait - (now - previous);
            if (spanTime <= 0) {
                result = func.call(context, ...args);
                clearTimeout(timer);
                timer = null;
                previous = now;
            } else if (!timer) {
                timer = setTimeout(() => {
                    result = func.call(context, ...args);
                    timer = null;
                    previous = new Date;
                }, spanTime);
            }
            return result;
        }
    }
    // BANNER-PLUGIN:只封装和轮播图相关的功能(自动轮播、焦点触发、左右按钮)
    function bannerPlugin() {
        
        // this:要实现轮播图的容器(原生JS对象)
        let $this = $(this),
            $wrapper = $this.find('.wrapper'),
            $pagination = $this.find('.pagination'),
            $buttonPrev = $this.find('.button-prev'),
            $buttonNext = $this.find('.button-next'),
            $slideList = $wrapper.find('.slide'),
            $paginationList = $pagination.find('span');
        let autoTimer = null,
            interval = 1000,
            speed = 300,
            activeIndex = 0,
            count = $slideList.length;
        let change = function () {
            let $active = $slideList.eq(activeIndex),
                $siblings = $active.siblings();
            $active.css('transition', `opacity ${speed}ms`);
            $siblings.css('transition', `opacity 0ms`);
            $active.css('z-index', 1);
            $siblings.css('z-index', 0);
            $active.css('opacity', 1).on('transitionend', function () {
                $siblings.css('opacity', 0);
            });
            // 焦点对齐
            $paginationList.each((index, item) => {
                let $item = $(item);
                if (index === activeIndex) {
                    $item.addClass('active');
                    return;
                }
                $item.removeClass('active');
            });
        };
        let autoMove = function () {
            activeIndex++;
            activeIndex >= count ? activeIndex = 0 : null;
            change();
        };
        let handlePagination = function () {
            $paginationList.mouseover(throttle(function () {
                activeIndex = $(this).index();
                change();
            }, 500));
        };
        let handleButton = function () {
            $buttonNext.click(throttle(autoMove, 300));
            $buttonPrev.click(throttle(function () {
                activeIndex--;
                activeIndex < 0 ? activeIndex = count - 1 : null;
                change();
            }, 500));
        };
        autoTimer = setInterval(autoMove, interval);
        handlePagination();
        handleButton();
        $this.mouseenter(() => clearInterval(autoTimer))
            .mouseleave(() => autoTimer = setInterval(autoMove, interval));
    }
    $.fn.extend({
        // bannerPlugin:bannerPlugin
        bannerPlugin
    });
}(jQuery);

插件版轮播图

html部分

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>渐隐渐现版轮播图</title>
    <!-- IMPORT CSS -->
    <link rel="stylesheet" href="css/reset.min.css">
    <link rel="stylesheet" href="css/banner.css">
</head>
<body>
    <section class="container" id="container1">
        <!-- 轮播图容器 -->
        <div class="wrapper"></div>
        <!-- 分页器容器 -->
        <div class="pagination"></div>
    </section>
    <section class="container" id="container2">
        <!-- 轮播图容器 -->
        <div class="wrapper"></div>
        <!-- 前进后退按钮 -->
        <a href="javascript:;" class="button-prev"></a>
        <a href="javascript:;" class="button-next"></a>
    </section>
    <!-- IMPORT JS -->
    <!-- <script src="js/jquery.min.js"></script>
    <script src="js/banner-plugin-jquery.js"></script>
    <script src="js/banner.js"></script> -->
    <script src="js/jquery.min.js"></script>
    <script src="js/banner-plugin.min.js"></script>
    <script src="js/banner.js"></script>
</body>
</html>

css部分

.container {
    box-sizing: border-box;
    position: relative;
    margin: 20px auto;
    width: 1226px;
    height: 460px;
    overflow: hidden;
}
.container .wrapper {
    position: relative;
    height: 100%;
}
.container .wrapper .slide {
    position: absolute;
    top: 0;
    left: 0;
    box-sizing: border-box;
    width: 100%;
    height: 100%;
    z-index: 0;
    opacity: 0;
}
.container .wrapper .slide:nth-child(1) {
    z-index: 1;
    opacity: 1;
}
.container .wrapper .slide img {
    width: 100%;
    height: 100%;
}
.container .wrapper .slide span{
    display: block;
    transform: translateY(-100px);
    font-size: 30px;
    color: red;
}
.container .pagination {
    position: absolute;
    right: 20px;
    bottom: 20px;
    z-index: 999;
    font-size: 0;
}
.container .pagination span {
    display: inline-block;
    margin: 0 4px;
    width: 6px;
    height: 6px;
    background: rgba(0, 0, 0, .4);
    border: 2px solid rgba(255, 255, 255, .4);
    border-radius: 50%;
    cursor: pointer;
}
.container .pagination span.active {
    background: rgba(255, 255, 255, .4);
    border-color: rgba(0, 0, 0, .4);
}
.container .button-prev,
.container .button-next {
    position: absolute;
    top: 50%;
    margin-top: -35px;
    z-index: 999;
    width: 40px;
    height: 70px;
    background: url("../images/icon-slides.png") no-repeat;
}
.container .button-prev {
    left: 0;
    background-position: -83px 0;
}
.container .button-prev:hover {
    background-position: 0 0;
}
.container .button-next {
    right: 0;
    background-position: -124px 0;
}
.container .button-next:hover {
    background-position: -41px 0;
}

清除默认样式

body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,button,input,textarea,th,td{margin:0;padding:0}body{font-size:12px;font-style:normal;font-family:"\5FAE\8F6F\96C5\9ED1",Helvetica,sans-serif}small{font-size:12px}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4,h5,h6{font-size:100%}ul,ol{list-style:none}a{text-decoration:none;background-color:transparent}a:hover,a:active{outline-width:0;text-decoration:none}table{border-collapse:collapse;border-spacing:0}hr{border:0;height:1px}img{border-style:none}img:not([src]){display:none}svg:not(:root){overflow:hidden}html{-webkit-touch-callout:none;-webkit-text-size-adjust:100%}input,textarea,button,a{-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]),video:not([controls]){display:none;height:0}progress{vertical-align:baseline}mark{background-color:#ff0;color:#000}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}button,input,select,textarea{font-size:100%;outline:0}button,input{overflow:visible}button,select{text-transform:none}textarea{overflow:auto}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.clearfix:after{display:block;height:0;content:"";clear:both}

js部分

~ function () {
    /* Banner:渐隐渐现轮播图插件 */
    class Banner {
        constructor(selector, options = {}) {
            // 参数初始化
            this.initialParams(options);
            // 获取需要操作的容器
            if (!selector) throw new ReferenceError('The first selector parameter must be passed~~');
            if (typeof selector === "string") {
                this.container = document.querySelector(selector);
            } else if (selector.nodeType) {
                this.container = selector;
            }
            this.wrapper = this.container.querySelector('.wrapper');
            this.slideList = this.wrapper.querySelectorAll('.slide');
            this.autoTimer = null;
            this.activeIndex = this.initialSlide;
            this.count = this.slideList.length;
            // 初始展示SLIDE
            [].forEach.call(this.slideList, (item, index) => {
                if (index === this.initialSlide) {
                    item.style.zIndex = 1;
                    item.style.opacity = 1;
                    return;
                }
                item.style.zIndex = 0;
                item.style.opacity = 0;
            });
            // 自动轮播处理
            if (this.autoplay) {
                let anonymous = this.autoMove.bind(this);
                this.autoTimer = setInterval(anonymous, this.autoplay);
                this.container.addEventListener('mouseenter', () => {
                    clearInterval(this.autoTimer);
                });
                this.container.addEventListener('mouseleave', () => {
                    this.autoTimer = setInterval(anonymous, this.autoplay);
                });
            }
            // 分页器的处理
            if (this.pagination && this.pagination.el) {
                this.handlePagination();
            }
            // 前进和后退按钮处理
            if (this.navigation) {
                this.handleButton();
            }
            // 钩子函数的处理
            // 初始化成功
            this.on && this.on.init && this.on.init.call(this, this);
        }
        /*===Banner.prototype===*/
        /*
         * initialParams:初始化插件的参数配置信息
         */
        initialParams(options) {
            // 1.首先设置默认的参数信息
            let _default = {
                initialSlide: 0,
                speed: 300,
                autoplay: 3000,
                pagination: {
                    el: '.pagination',
                    triggerEvent: 'click'
                },
                navigation: {
                    nextEl: '.button-next',
                    prevEl: '.button-prev',
                    hide: true
                },
                on: {
                    init: function (examp) {},
                    transitionStart: function (examp) {},
                    transitionEnd: function (examp) {}
                }
            };
            // 2.把传递进来的OPTIONS中的信息替换_DEFAULT中的信息
            for (let key in options) {
                if (!options.hasOwnProperty(key)) break;
                if (/^(pagination|navigation|on)$/i.test(key)) continue;
                _default[key] = options[key];
            }
            // pagination
            let pagination = options.pagination;
            if (pagination !== null) {
                pagination = pagination || {};
                for (let key in pagination) {
                    if (!pagination.hasOwnProperty(key)) break;
                    _default['pagination'][key] = pagination[key];
                }
            } else {
                _default['pagination'] = null;
            }
            // navigation
            let navigation = options.navigation;
            if (navigation !== null) {
                navigation = navigation || {};
                for (let key in navigation) {
                    if (!navigation.hasOwnProperty(key)) break;
                    _default['navigation'][key] = navigation[key];
                }
            } else {
                _default['navigation'] = null;
            }
            // on
            let _on = options.on;
            if (_on !== null) {
                _on = _on || {};
                for (let key in _on) {
                    if (!_on.hasOwnProperty(key)) break;
                    _default['on'][key] = _on[key];
                }
            } else {
                _default['on'] = null;
            }
            // 3.把处理好的信息挂载到实例上
            for (let key in _default) {
                if (!_default.hasOwnProperty(key)) break;
                this[key] = _default[key];
            }
        }
        /* 实现轮播图切换 */
        change() {
            [].forEach.call(this.slideList, (item, index) => {
                if (index === this.activeIndex) {
                    // 当前要操作的SLIDE
                    item.style.transition = `opacity ${this.speed}ms`;
                    item.style.zIndex = 1;
                    return;
                }
                // 其余的SLIDE
                item.style.transition = `opacity 0ms`;
                item.style.zIndex = 0;
            });
            // 开始动画
            // 动画开始前的钩子函数
            this.on && this.on.transitionStart && this.on.transitionStart.call(this, this);
            let active = this.slideList[this.activeIndex];
            active.style.opacity = 1;
            active.addEventListener('transitionend', () => {
                // addEventListener:DOM2级事件绑定
                [].forEach.call(this.slideList, (item, index) => {
                    if (index !== this.activeIndex) {
                        item.style.opacity = 0;
                    }
                });
                // 动画结束后的钩子函数
                this.on && this.on.transitionEnd && this.on.transitionEnd.call(this, this);
            });
            // 焦点对齐
            if (this.paginationList) {
                [].forEach.call(this.paginationList, (item, index) => {
                    if (index === this.activeIndex) {
                        item.className = "active";
                        return;
                    }
                    item.className = "";
                });
            }
        }
        /* 自动轮播 */
        autoMove() {
            this.activeIndex++;
            this.activeIndex >= this.count ? this.activeIndex = 0 : null;
            this.change();
        }
        /* 分页器处理 */
        handlePagination() {
            // 获取分页器盒子,动态创建内容
            this.paginationBox = this.container.querySelector(this.pagination.el);
            let str = ``;
            for (let i = 0; i < this.count; i++) {
                str += `<span class='${i===this.activeIndex?'active':''}'></span>`;
            }
            this.paginationBox.innerHTML = str;
            this.paginationList = this.paginationBox.querySelectorAll('span');
            // 是否焦点触发切换
            if (this.pagination.triggerEvent) {
                [].forEach.call(this.paginationList, (item, index) => {
                    item.addEventListener(this.pagination.triggerEvent, Banner.throttle(() => {
                        this.activeIndex = index;
                        this.change();
                    }, 500));
                });
            }
        }
        /* 前进后退按钮 */
        handleButton() {
            this.prevEl = this.container.querySelector(this.navigation.prevEl);
            this.prevEl.addEventListener('click', Banner.throttle(() => {
                this.activeIndex--;
                this.activeIndex < 0 ? this.activeIndex = this.count - 1 : null;
                this.change();
            }, 500));
            this.nextEl = this.container.querySelector(this.navigation.nextEl);
            this.nextEl.addEventListener('click', Banner.throttle(this.autoMove.bind(this), 500));
            // 显示隐藏的处理
            if (this.navigation.hide) {
                this.prevEl.style.display = 'none';
                this.nextEl.style.display = 'none';
                this.container.addEventListener('mouseenter', () => {
                    this.prevEl.style.display = 'block';
                    this.nextEl.style.display = 'block';
                });
                this.container.addEventListener('mouseleave', () => {
                    this.prevEl.style.display = 'none';
                    this.nextEl.style.display = 'none';
                });
            }
        }
        /* 设置私有的方法 */
        static throttle(func, wait) {
            let timer = null,
                result = null,
                previous = 0;
            return function anonymous(...args) {
                let context = this,
                    now = new Date,
                    spanTime = wait - (now - previous);
                if (spanTime <= 0) {
                    result = func.call(context, ...args);
                    clearTimeout(timer);
                    timer = null;
                    previous = now;
                } else if (!timer) {
                    timer = setTimeout(() => {
                        result = func.call(context, ...args);
                        timer = null;
                        previous = new Date;
                    }, spanTime);
                }
                return result;
            }
        }
    }
    window.Banner = Banner;
}();