让前端飞

利用jquery实现轮播图-e6写法

2019-01-02  本文已影响1人  家里有棵核桃树

用了好久bootstrap提供的轮播图插件,刚好最近很闲,就自己实现轮播图效果练练手。bootstrap较早的版本都是利用原型的办法(有些也用了es6的proxy)实现的,最新的版本用的是es6的类(class)和一些模块的思想来实现。本文实现轮播效果的方法和bootstrap的实现不一样,但是会借鉴相关的代码风格。由于是练手demo在轮播插件的封装上考虑的不是太多。

1、使用

1.1 静态资源

使用js前要提前引用jquery,可以把该js文件放在</body>前引用。

1.2 HTML模板

把内容按如下格式装填好后,就可以出现轮播效果了(跟bootstrap类似)。轮播项的格式要和指示器的个数一一对应,轮播项和指示器缺一不可。

<!--carousel 轮播容器-->
<div class="carousel">
    <!--carousel-inner 每一个轮播项内容-->
    <div class="carousel-inner">
        <div class="carousel-item active" data-index="0">
            <a href="#">
                <img class="carousel-img" src="img/1.png" alt="">
            </a>
            <div class="carousel-caption">
                <h3>HEADER 1~</h3>
            </div>
        </div>
        ...
    </div>

    <!--carousel-indicators 指示器,指示当前显示的是第几项 也可以点击切换当前的显示项-->
    <ul class="carousel-indicators">
        <li class="active" data-index="0">1</li>
        <li data-index="1">2</li>
        <li data-index="2">3</li>
        <li data-index="3">4</li>
        <li data-index="4">5</li>
    </ul>

    <!--carousel-ctrl* 左右箭头,可上下切换当前的显示项【可省略】-->
    <span class="carousel-ctrlPrev">&lt;</span>
    <span class="carousel-ctrlNext">&gt;</span>
</div>

1.3 效果

轮播呈现
功能:

2、实现

2.1 轮播设计思想

(1)下一张的操作
    让当前项的下一张显示出来,然后通过jquery提供的animate方法将盒子左移一个盒子的宽度(left: -盒子宽度;),移动效果完成后,将最开始显示的那一项隐藏且设置当前盒子的左移大小为0(left: 0;)。
    如果当前项没有下一张,那么就把第一张插入到当前项的后面。再继续上面的操作。

(2)上一张的操作
    让当前项的上一张显示出来,设置盒子的左移大小为一个盒子的宽度(left: -盒子宽度;),然后通过jquery提供的animate方法将盒子的左移大小设置为0(left: 0;),移动效果完成后,将最开始显示的那一项隐藏。
    如果当前项没有上一张,那么就把最后一张插入到当前项的前面。再继续上面的操作。

(3)指示器指定显示项
    先拿到显示项的序号,再判断滚动方向(next还是prev),通过滚动方向来确定下一项是插入到当前项的后面还是前面,继续上一张或下一张的操作。

2.2 代码

(1)js

// 用到的轮播图的相关模块
const Selector = {
    BOX: '.carousel',
    INNER: '.carousel-inner',
    ACTIVE: '.active',
    ACTIVE_ITEM: '.active.carousel-item',
    ITEM: '.carousel-item',
    NEXT: '.carousel-ctrlNext',
    PREV: '.carousel-ctrlPrev',
    INDICATORS: '.carousel-indicators',
};

// 轮播框大小
const Box = {
    WIDTH: $(Selector.BOX).width(),
    HEIGHT: $(Selector.BOX).height()
};

// 相关类名
const ClassName = {
    ACTIVE : 'active'
};

// 方向
const Direction = {
    NEXT: 'next',
    PREV: 'prev'
};

// 默认配置
const Default = {
    interval: 3000,
    gap: 600
};

class Carousel {
    constructor(selector, config) {

        this._isPaused = false;
        this._interval = null;
        this._enableClickAction = true;
        this._activeElement = $(Selector.ACTIVE_ITEM);
        this._targetElement = null;
        this._element = $(selector);
        this._inner = $(Selector.INNER);
        this._indicatorsElement = $(Selector.INDICATORS);

        this._init();
    }

    pause() {

        this._isPaused = true;
        clearInterval(this._interval);
    }

    cycle() {

        this._isPaused = false;
        if (this._interval) {
            // 重新开始轮播时 要重新更新定时器 不然显示有误差,因为有多个定时器存在
            clearInterval(this._interval);
            this._interval = null;
        }
        this._interval = setInterval(() => this._slide('next'), Default.interval);
    }

    _init() {

        this._addEventListeners();
        this.cycle();
    }

    _addEventListeners() {

        // 下一页
        $(Selector.NEXT).on('click', () => this._slide('next'));
        // 上一页
        $(Selector.PREV).on('click', () => this._slide('prev'));
        // 鼠标移入轮播暂停
        $(Selector.BOX).on('mouseenter', () => this.pause());
        // 鼠标移除轮播继续
        $(Selector.BOX).on('mouseleave', () => this.cycle());
        // 轮播框框被点击切换时 阻止过快点击出现的问题
        $(Selector.BOX).on('click', () => {

            if (this._enableClickAction) {
                this._enableClickAction = false;
                setTimeout(() => this._enableClickAction = true, Default.gap);
            }
        });
        // 指示器点击时 切换到对应的图片
        $(Selector.INDICATORS).on('click', 'li', event => this._slide('', $(event.target)));
    }

    _getActiveItemIndex() {
        return this._activeElement.data('index');
    }

    _getTargetItemIndex($indicator = null, direction = 'next') {

        if ($indicator) {
            return $indicator.data('index');
        } else {
            let itemsLength = $(Selector.ITEM).length;
            let index = Direction.NEXT == direction ? this._getActiveItemIndex() + 1 : this._getActiveItemIndex() - 1;
            itemsLength == index && (index = 0);
            -1 == index && (index = itemsLength - 1);
            return index;
        }
    }

    _isActiveItemOnEdge(direction) {

        let $items = this._inner.find(Selector.ITEM);
        return Direction.NEXT == direction
            ? this._activeElement[0] == $items.last()[0]
            : this._activeElement[0] == $items.first()[0];
    }

    _setActiveIndicatorElement() {

        this._indicatorsElement
            .find(Selector.ACTIVE).removeClass(ClassName.ACTIVE)
            .end()
            .find(`[data-index='${this._getActiveItemIndex()}']`).addClass(ClassName.ACTIVE);
    }

    // $indicator 只有通过指示器改变轮播图才会用到
    _slide(direction = 'next', $indicator = null) {

        // 如果在短时间内多次点击 不进行任何操作
        if(!this._enableClickAction) return false;

        // 当轮播图开始滚动时 得到当前项和需要滚动到的指定目标项
        this._activeElement = $(Selector.ACTIVE_ITEM);
        this._targetElement = this._inner.find(`${Selector.ITEM}[data-index='${this._getTargetItemIndex($indicator, direction)}']`);

        // 根据方向 将targetElement插入到对应的位置
        $indicator && (direction =  this._getTargetItemIndex($indicator) > this._getActiveItemIndex() ? 'next' : 'prev');

        // 需要将目标项显示出来
        this._targetElement.addClass('active');

        // 根据不同的prev、next操作 判断是否需要改变列表排列顺序
        let isActiveItemOnEdge = this._isActiveItemOnEdge(direction);
        if (Direction.NEXT == direction && (isActiveItemOnEdge || $indicator)) {

            // 将第一项放到最后一项
            this._targetElement.appendTo(this._inner);
        }
        if (Direction.PREV == direction) {

            // 将最后一项放到第一项,原始的盒子inner的left需要修改
            (isActiveItemOnEdge || $indicator) && this._targetElement.prependTo(this._inner);
            this._inner.css('left', -Box.WIDTH);
        }

        // 切换轮播图的显示
        this._inner.animate({
            left: Direction.NEXT == direction ? -Box.WIDTH : 0
        }, Default.gap, 'linear',() => {

            // 轮播图切换成功后的操作
            this._activeElement.removeClass('active');
            this._activeElement = this._targetElement;
            this._inner.css('left', 0);

            // 修改对应的指示器
            this._setActiveIndicatorElement();
        });
    }

    static _jQueryInterface(config) {
        return new Carousel(this, config);
    }
}

// new Carousel(Selector.BOX);
$.fn.carousel = Carousel._jQueryInterface;
$.fn.carousel.Constructor = Carousel;

$('.carousel').carousel();

(2)less

* {
  margin: 0;
  padding: 0;
}
@carouselWidth: 500px;
@carouselHeight: 300px;
.container {
  box-sizing: content-box;
  width: @carouselWidth;
  height: @carouselHeight;
  padding: 10px;
  margin: 100px auto;
  border: 1px solid #ccc;
}
.carousel {
  position: relative;
  width: @carouselWidth;
  height: @carouselHeight;
  overflow: hidden;
  color: #666;
}
.carousel-inner{
  position: absolute;
  width: 200%;
  height: 100%;
}
.carousel-item {
  display: none;
  width: @carouselWidth;
  height: @carouselHeight;
  float: left;
  &.active {
    display: block;
  }
}
.carousel-caption {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 50px;
  padding: 0 10px;
  background: rgba(0, 0, 0, 0.2);
  line-height: 50px;
}
.carousel-img {
  width: 100%;
  height: 100%;
}
.carousel-indicators {
  position: absolute;
  right: 10px;
  bottom: 10px;
  text-align: center;
  li {
    display: inline-block;
    height: 20px;
    width: 20px;
    margin: 3px;
    border-radius: 10px;
    background-color: rgba(0, 0, 0, 0.3);
    font-size: 13px;
    line-height: 20px;
    cursor: pointer;
    &.active {
      background-color: rgba(0, 0, 0, 0.6);
      color: #bbb;
    }
  }
}

[class*='carousel-ctrl'] {
  position: absolute;
  top: 50%;
  width: 40px;
  height: 40px;
  border: 1px solid #fff;
  background: #fff;
  color: #000;
  opacity: 0.1;
  font-family: '黑体';
  font-weight: bold;
  font-size: 30px;
  text-align: center;
  line-height: 40px;
  transform: translateY(-50%);
  cursor: pointer;
  &:hover {
    opacity: 0.5;
  }
}
.carousel-ctrlPrev {
  left: 5px;
}
.carousel-ctrlNext {
  right: 5px;
}

(3)html

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="UTF-8">
    <title>轮播图</title>
    <link href="css/007.css" rel="stylesheet"> <!--记得替换-->
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="js/007.js" defer></script> <!--记得替换-->
</head>
<body>
<div class="container">
    <!--carousel 轮播容器-->
    <div class="carousel">
        <!--carousel-inner 每一个轮播项内容-->
        <div class="carousel-inner">
            <div class="carousel-item active" data-index="0">
                <a href="#">
                    <img class="carousel-img" src="img/1.png" alt="">
                </a>
                <div class="carousel-caption">
                    <h3>HEADER 1~</h3>
                </div>
            </div>
            <div class="carousel-item" data-index="1">
                <a href="#">
                    <img class="carousel-img" src="img/2.png" alt="">
                </a>
                <div class="carousel-caption">
                    <h3>HEADER 2~</h3>
                </div>
            </div>
            <div class="carousel-item" data-index="2">
                <a href="#">
                    <img class="carousel-img" src="img/3.png" alt="">
                </a>
                <div class="carousel-caption">
                    <h3>HEADER 3~</h3>
                </div>
            </div>
            <div class="carousel-item" data-index="3">
                <a href="#">
                    <img class="carousel-img" src="img/4.png" alt="">
                </a>
                <div class="carousel-caption">
                    <h3>HEADER 4~</h3>
                </div>
            </div>
            <div class="carousel-item" data-index="4">
                <a href="#">
                    <img class="carousel-img" src="img/5.png" alt="">
                </a>
                <div class="carousel-caption">
                    <h3>HEADER 5~</h3>
                </div>
            </div>
        </div>

        <!--carousel-indicators 指示器,指示当前显示的是第几项 也可以点击切换当前的显示项-->
        <ul class="carousel-indicators">
            <li class="active" data-index="0">1</li>
            <li data-index="1">2</li>
            <li data-index="2">3</li>
            <li data-index="3">4</li>
            <li data-index="4">5</li>
        </ul>

        <!--carousel-ctrl* 左右箭头,可上下切换当前的显示项-->
        <span class="carousel-ctrlPrev">&lt;</span>
        <span class="carousel-ctrlNext">&gt;</span>
    </div>
</div>
</body>
</html>
上一篇下一篇

猜你喜欢

热点阅读