利用jquery实现轮播图组件-e6 class实现

用了好久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>

3、友情链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容

  • 一、流式布局 1.1 什么是流式布局 流式布局就是百分比布局,通过盒子的宽度设置成百分比来根据屏幕的宽度来进行伸缩...
    福尔摩鸡阅读 4,257评论 2 15
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,489评论 1 45
  • 开篇废话是一种情怀 上一篇说到Less预处理语言,这节就着重讲下Bootstrap框架,这款框架应该是现在前端人手...
    西巴撸阅读 748评论 1 4
  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,754评论 1 92
  • 我一直是个谨慎的人,从不做自己会后悔的事,所以我现在很后悔。
    肖纯洁阅读 92评论 0 0