渐隐渐现轮播图以及插件的封装(36)

本文介绍了如何使用HTML、CSS和JavaScript实现一个基本的图片轮播效果,包括轮播图的结构、样式和交互功能。同时,讨论了将此功能封装成一个可配置的插件,支持自定义参数如切换速度、自动播放等,以提高代码复用性和用户体验。
摘要由CSDN通过智能技术生成

 

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>珠峰培训</title>
    <link rel="stylesheet" href="css/reset.min.css">
    <link rel="stylesheet" href="css/banner-fade.css">
</head>
<body>
<section class="container" id="container">
    <div class="wrapper">
        <!--<div class="slide"><img src="img/banner1.jpg" alt=""></div>
        <div class="slide"><img src="img/banner2.jpg" alt=""></div>
        <div class="slide"><img src="img/banner3.jpg" alt=""></div>
        <div class="slide"><img src="img/banner4.jpg" alt=""></div>-->
    </div>
    <ul class="focus">
        <!--<li class="active"></li>
        <li></li>
        <li></li>
        <li></li>-->
    </ul>
    <a href="javascript:;" class="arrow arrowLeft"></a>
    <a href="javascript:;" class="arrow arrowRight"></a>
</section>

<script src="js/jquery-1.11.3.min.js"></script>
<script src="js/banner-fade.js"></script>
</body>
</html>
.container {
    position: relative;
    margin: 20px auto;
    width: 1000px;
    height: 300px;
    overflow: hidden;
}

.container .wrapper {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

.container .wrapper .slide {
    position: absolute;
    top: 0;
    left: 0;
    z-index: 0;
    opacity: 0;

    width: 100%;
    height: 100%;
    overflow: hidden;
}

/*默认展示第一个*/
.container .wrapper .slide:nth-of-type(1) {
    z-index: 1;
    opacity: 1;
}

.container .wrapper .slide img {
    display: block;
    width: 100%;
    height: 100%;
}

.container .focus {
    position: absolute;
    z-index: 999;
    bottom: 10px;
    left: 50%;
    transform: translateX(-50%);

    padding: 4px;
    height: 12px;
    background: rgba(0, 0, 0, .5);
    border-radius: 10px;
    font-size: 0;
}

.container .focus li {
    display: inline-block;
    margin: 0 4px;
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #FFF;
    cursor: pointer;
}

.container .focus li.active {
    background: #DB192A;
}

.container .arrow {
    display: none;
    position: absolute;
    top: 50%;
    z-index: 999;
    margin-top: -22.5px;
    width: 28px;
    height: 45px;
    background: url("../img/pre.png") no-repeat;
    opacity: 0.3;
}

.container .arrow:hover {
    opacity: 1;
}

.container .arrow.arrowLeft {
    left: 0;
    background-position: 0 0;
}

.container .arrow.arrowRight {
    right: 0;
    background-position: -50px 0;
}
$(function () {
    let bannerRender = (function anonymous() {
        let $container = $('#container'),
            $wrapper = $container.children('.wrapper'),
            $focus = $container.children('.focus'),
            $arrowLeft = $container.children('.arrowLeft'),
            $arrowRight = $container.children('.arrowRight'),
            $slideList = null,
            $focusList = null;

        //=>QUERY-DATA:获取数据
        let queryData = function queryData() {
            return new Promise((resolve, reject) => {
                $.ajax({
                    url: 'json/banner.json',//=>请求API地址
                    method: 'get',//=>请求方式
                    dataType: 'json',//=>把获取的JSON字符串转为对象
                    async: true,//=>TRUE:异步 FALSE:同步
                    success: resolve,
                    error: reject
                    /*success: data => {
                        //=>成功后执行的回调函数,DATA从服务器端获取的数据(对象)
                        resolve(data);
                    },
                    error: msg => {
                        //=>失败后执行的回调函数,MSG存储了失败的信息
                        reject(msg);
                    }*/
                });
            });
        };

        //=>BIND-HTML:数据绑定
        let bindHTML = function bindHTML(data) {
            let strSlide = ``,
                strFocus = ``;
            $.each(data, (index, item) => {
                let {img, desc} = item;
                strSlide += `<div class="slide">
                    <img src="${img}" alt="${desc}">
                </div>`;
                strFocus += `<li class="${index === 0 ? 'active' : ''}"></li>`;
            });
            $wrapper.html(strSlide);
            $focus.html(strFocus);

            $slideList = $wrapper.find('.slide');
            $focusList = $focus.find('li');
        };

        //=>SLIDE切换的公共方法
        let changeSlide = function changeSlide() {
            /*
             * 切换思路:
             *   1.让当前的Z-INDEX=1,并且让上一个的Z-INDEX=0(这样是为了保证不管结构是靠前还是靠后,始终当前这个都是层级最高的,也是优先展示的)
             *   2.让当前的实现出渐现的效果(OPACITY从0~1)
             *   3.当前这个已经渐现出来(动画结束),我们再让上一个透明度为零(为了下一次展示它的时候,透明度是从零开始渐现的)
             *   4.让当前的索引变为下一次对应的上一次索引
             */
            let $cur = $slideList.eq(_index),
                $last = $slideList.eq(_lastIndex);
            $cur.css('zIndex', 1);
            $last.css('zIndex', 0);
            $cur.stop().animate({
                opacity: 1
            }, _speed, () => {
                $last.css({opacity: 0});
                _lastIndex = _index;
            });
            changeFocus();
        };

        //=>AUTO-MOVE:自动轮播
        let _index = 0,//=>当前展示SLIDE的索引
            _lastIndex = 0,//=>上一次展示SLIDE的索引
            _timer = null,//=>存储自动轮播的定时器
            _interval = 3000,//=>多久切换一次
            _speed = 200;//=>每一次切换动画的时间

        let autoMove = function autoMove() {
            _index++;
            //=>边界判断:如果累加的结果大于最大索引,我们展示第一个SLIDE即可
            if (_index >= $slideList.length) {
                _index = 0;
            }
            changeSlide();
        };

        //=>CHANGE-FOCUS:焦点对齐
        let changeFocus = function changeFocus() {
            $focusList.eq(_index).addClass('active');
            $focusList.eq(_lastIndex).removeClass('active');
        };

        //=>HANDLE-MOUSE:鼠标控制暂停和开启
        let handleMouse = function handleMouse() {
            $container.on('mouseenter', () => {
                clearInterval(_timer);
                $arrowLeft.add($arrowRight).css('display', 'block');//=>ADD:是在一个JQ集合中增加一些新的元素(获取新的JQ对象),有点类似乎两个集合合并
            }).on('mouseleave', () => {
                _timer = setInterval(autoMove, _interval);
                $arrowLeft.add($arrowRight).css('display', 'none');
            })
        };

        //=>HANDLE-ARROW:箭头左右切换
        let handleArrow = function handleArrow() {
            $arrowRight.on('click', autoMove);
            $arrowLeft.on('click', () => {
                _index--;
                if (_index < 0) {
                    _index = $slideList.length - 1;
                }
                changeSlide();
            });
        };

        //=>HANDLE-FOCUS:点击焦点切换
        let handleFocus = function handleFocus() {
            $focusList.on('click', function anonymous() {
                let curIndex = $(this).index();
                if (_index === curIndex) {
                    //=>当前展示的和点击的是同一个,不做任何的处理
                    return;
                }
                _index = curIndex;
                changeSlide();
            });
        };

        return {
            init: function init() {
                let promise = queryData();
                promise.then(data => {
                    //=>获取数据成功后处理的事情(DATA就是获取的数据)
                    bindHTML(data);
                    _timer = setInterval(autoMove, _interval);
                    handleMouse();
                    handleArrow();
                    handleFocus();
                });
            }
        }
    })();
    bannerRender.init();
});

插件封装 banner-plugin.js

* 封装插件

 *   1. 每一次调用插件都是独立的,互不影响的

 *

 *   2. 一些常用的方法还要是公用的

 *

 *   调取一次插件就是创建一个独立的实例,里面的很多信息是互相不干扰的,但是对于一些操作操作方法还是共同调取一个即可(这套机制就是我们的构造函数模式中类和实例的机制),所以插件、组件、类库、框架的封装一般都是基于OOP面向对象完成的

 *

 *   3. 封装插件的目的是:可以更多适配各种需求、让用户使用起来非常的方便,所以插件封装的核心和难点都在于细节的思考和处理,而不应该局限到各种模式或者装X的代码中 

//=>一个优秀的插件是尽可能支持更多的配置项(大部分配置项都是有默认值的)
// new Banner({
//     ele: '#container', //=>操作哪个容器(选择器)
//     // data: [], //=>需要绑定的数据
//     url: '',//=>获取数据的API地址(插件内部帮我们获取数据)
//     isArrow: true,//=>是否支持左右切换
//     isFocus: true,//=>是否支持焦点切换
//     isAuto: true,//=>是否支持自动切换
//     defaultIndex: 0,//=>默认展示第几张
//     interval: 3000,//=>多久切换一次
//     speed: 200,//=>切换的速度
//     moveEnd:()=>{},//=>切换完成后处理的事情
//     ...
// });

//=>支持扩展,可以让用户自己在你的插件中扩展方法
// Banner.fn.extend({xxx:()=>{}})
// ...

~function () {
    class Banner {
        constructor(options = {}) {
            //=>OPTIONS传递的配置项(解构赋值并且给更多的配置项设置默认值)
            let {
                ele,
                url,
                isArrow = true,
                isFocus = true,
                isAuto = true,
                defaultIndex = 0,
                interval = 3000,
                speed = 200,
                moveEnd
            } = options;

            //=>把所有的配置项信息都挂载到实例上(这样以后在原型的任何方法中都可以调取这些属性获取值了)
            ['ele', 'url', 'isArrow', 'isFocus', 'isAuto', 'defaultIndex', 'interval', 'speed', 'moveEnd'].forEach(item => {
                this[item] = eval(item);
            });

            this.container = document.querySelector(ele);
            let _con = this.container;
            this.wrapper = _con.querySelector('.wrapper');
            this.focus = _con.querySelector('.focus');
            this.arrowLeft = _con.querySelector('.arrowLeft');
            this.arrowRight = _con.querySelector('.arrowRight');
            this.slideList = null;
            this.focusList = null;
            this.stepIndex = defaultIndex;
            this.autoTimer = null;

            //=>调取INIT开启轮播图
            this.init();
        }

        //=>BANNER的主入口(在INIT中规划方法的执行顺序)
        init() {
            let {isAuto, interval} = this;
            let promise = this.queryData();
            promise.then(() => {
                this.bindHTML();
            }).then(() => {
                if (isAuto) {
                    this.autoTimer = setInterval(() => {
                        this.autoMove();
                    }, interval);
                }
            });
        }

        //=>获取数据(PROMISE)
        queryData() {
            let {url} = this;
            return new Promise((resolve, reject) => {
                let xhr = new XMLHttpRequest;
                xhr.open('GET', url);
                xhr.onreadystatechange = () => {
                    if (xhr.readyState === 4 && xhr.status === 200) {
                        //=>把获取的数据也挂载到实例上了
                        this.data = JSON.parse(xhr.responseText);
                        resolve();
                    }
                };
                xhr.send(null);
            });
        }

        //=>数据绑定
        bindHTML() {
            //属性可以结构,方法不可以 
            let {data, wrapper, focus, slideList, focusList} = this;
            let strSlide = ``,
                strFocus = ``;
            data.forEach((item, index) => {
                let {img = 'img/banner1.jpg', desc = '珠峰培训'} = item;
                strSlide += `<div class="slide">
                    <img src="${img}" alt="${desc}">
                </div>`;
                strFocus += `<li class="${index === 0 ? 'active' : ''}"></li>`;
            });
            wrapper.innerHTML = strSlide;
            focus.innerHTML = strFocus;

            //->获取所有的SLIDE和LI
            this.slideList = wrapper.querySelectorAll('.slide');
            this.focusList = focus.querySelectorAll('li');
            wrapper.appendChild(this.slideList[0].cloneNode(true));
            this.slideList =wrapper.querySelectorAll('.slide');
            utils.css(wrapper, 'width', this.slideList.length * 1000);
        };

        //=>自动轮播
        autoMove() {
            this.stepIndex++;
            if (this.stepIndex >= this.slideList.length) {
                utils.css(this.wrapper, 'left', 0);
                this.stepIndex = 1;
            }
            //->基于自主封装的ANIMATE实现切换动画
            animate(this.wrapper, {
                left: -this.stepIndex * 1000
            }, this.speed);
            this.changeFocus();
        };

        changeFocus() {
            let tempIndex = this.stepIndex;
            tempIndex === this.slideList.length - 1 ? tempIndex = 0 : null;
            [].forEach.call(this.focusList, (item, index) => {
                item.className = index === tempIndex ? 'active' : '';
            });
        };
    }

    window.Banner = Banner;
}();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值