手把手和你用原生JS写一个轮回播放图片轮播

前段时间进修了淘宝首页的静态页面,个中收成较大的的就是这个轮回播放的图片轮播组件,本文就将相干制造履历分享给人人。

先看看在线DEMO:原生JS轮回播放图片轮播组件 (支撑IE8+,本文中的在线demo均未经由紧缩,能够直接在浏览器中调试

以及GitHub堆栈地点及完全代码:JuniorTour/simple-standard-js-carousel

一、思绪解说:

1.先说基础的非轮回无过渡图片轮播:

这个思绪照样很简朴的,经由过程视察一些图片轮播就能够发明,图片轮播平常是以一个尺寸较小的父元素作为窗口,包裹住一组较长的长条状的项目(item)子元素,再应用 overflow: hidden; ,将父元素作为“窗口”,只显示出的项目子元素的一部份,并经由过程转变项目子元素的定位或translate3d属性,完成多张图片项目动态播放。

基础道理能够参考这个demo:图片轮播基础道理演示

2.比较有意思的实际上是轮回的功用:

然则如许简朴的轮播是不会轮回播放的,也就是说当一轮图片项目(item)播放到末端;或许当在第一张图(第一个项目)继承向前时,就会超越内容子元素,涌现空缺部份,这平常不是我们想要的结果。

有多种思绪能够完成轮回播放,我视察到淘宝网首页的图片轮播是如许的思绪:

复制开首和末端的项目,并离别放在开首和末端,当播放到开首或末端的项目,继承播放,须要轮回时,暂时作废transition属性,并马上用定位跳转至响应的真正的开首或末端以后,再恢复本来的transition,继承一般转动播放,从而应用视觉上的“诳骗”,完成带有过渡结果的轮回播放。

响应的道理能够参考这个demo:图片轮播轮回道理演示

二、HTML标记部份

核心理念是简约、语义化。这部份由于我学过bootstrap框架所以自创了bootstrap的HTML标记构造

团体构造为:

外层的.carousel-wrapper包裹着轮播的三个主要部份,离别是:

.carousel-item-wrapper:项目内容部份(作为演示,本文中的demo运用a标签替换了图片,人人能够自行尝试替换为图片;同时增加了笔墨序号标记,以便于视察明白,特别要注意两个复制的开首和末端项目copy-1和copy-5)

.carousel-control-wrapper:掌握按钮部份,即两个用于掌握摆布挪动的按钮。

.carousel-index-wrapper:索引按钮部份,即图片轮播中的那一排“小圆点”。为了便于用JS操控,我增加了id作为“钩子”。而bootstrap在这里用的是自定义的data属性。

<div class="carousel-wrapper">
    <div class="carousel-item-wrapper" style="left: -520px;">
        <div class="carousel-item">
            <a href="#">
                <!--作为演示,用a标签替换了图片。-->
                <!--<img src="img/carousel-img-5" alt="">-->
            </a>
            <div class="carousel-index-mark">
                copy-5
            </div>
        </div>
        <div class="carousel-item">
            <a href="#">
                <!--<img src="img/carousel-img-1" alt="">-->
            </a>
            <div class="carousel-index-mark">
                1
            </div>
        </div>
        <div class="carousel-item">
            <a href="#">
                <!--<img src="img/carousel-img-2" alt="">-->
            </a>
            <div class="carousel-index-mark">
                2
            </div>
        </div>
        <div class="carousel-item">
            <a href="#">
                <!--<img src="img/carousel-img-3" alt="">-->
            </a>
            <div class="carousel-index-mark">
                3
            </div>
        </div>
        <div class="carousel-item">
            <a href="#">
                <!--<img src="img/carousel-img-4" alt="">-->
            </a>
            <div class="carousel-index-mark">
                4
            </div>
        </div>
        <div class="carousel-item">
            <a href="#">
                <!--<img src="img/carousel-img-5" alt="">-->
            </a>
            <div class="carousel-index-mark">
                5
            </div>
        </div>
        <div class="carousel-item">
            <a href="#">
                <!--<img src="img/carousel-img-1" alt="">-->
            </a>
            <div class="carousel-index-mark">
                copy-1
            </div>
        </div>
    </div>
    <div class="carousel-control-wrapper">
        <button id="prev">
            <!--prev-->
            <i>&lt;</i>
        </button>
        <button id="next">
            <!--next-->
            <i>&gt;</i>
        </button>
    </div>
    <div class="carousel-index-wrapper">
        <ul>
            <li class="carousel-index-btn active-carousel-index-btn" id="carousel-to-1">carousel-index-1</li>
            <li class="carousel-index-btn" id="carousel-to-2">carousel-index-2</li>
            <li class="carousel-index-btn" id="carousel-to-3">carousel-index-3</li>
            <li class="carousel-index-btn" id="carousel-to-4">carousel-index-4</li>
            <li class="carousel-index-btn" id="carousel-to-5">carousel-index-5</li>
        </ul>
    </div>
</div>

三、CSS款式部份

总的来说比较简朴,主要的处所我加上了诠释,有存疑的处所,迎接和我交换。

    /*reset*/
    * {
        border: none;
        padding: 0;
        margin: 0;
    }
    button {
        outline: none;
    }
    li {
        list-style: none;
    }

    .carousel-wrapper {
        width:520px;
        height:280px;
        overflow: hidden;   /*症结*/
        position: relative;
        margin: 100px auto;
    }
    .carousel-item-wrapper {
        width:3640px;
        height:280px;
        position: absolute;
        top: 0;
        left: -520px;
        transition: left .2s ease-in;
    }
    .carousel-item a {
        display: block;
        background-color: red;
        width:520px;
        height: 280px;
    }

    /*运用差别背景色的a替换图片。*/
    .carousel-item:nth-child(1) a {
        background-color: rgb(129,194,214);
        /*第五张图片的复制*/
    }
    .carousel-item:nth-child(2) a {
        background-color: rgb(129,146,214);
    }
    .carousel-item:nth-child(3) a {
        background-color: rgb(217,179,230);
    }
    .carousel-item:nth-child(4) a {
        background-color: rgb(220,247,161);
    }
    .carousel-item:nth-child(5) a {
        background-color: rgb(131,252,216);
    }
    .carousel-item:nth-child(6) a {
        background-color: rgb(129,194,214);
    }
    .carousel-item:nth-child(7) a {
        background-color: rgb(129,146,214);
        /*第一张图片的复制*/
    }

    .carousel-item {
        float: left;
    }
    .carousel-index-mark {
        font-size:60px;
        color: black;
        position: absolute;
        top: 0;
    }
    .carousel-control-wrapper {
        transition: all .2s;
    }
    .carousel-wrapper:hover button {
        display: block;
    }
    .carousel-control-wrapper button {
        transition: all .2s linear;
        display: none;
        width:24px;
        height:36px;
        line-height:36px;
        background-color: rgba(0,0,0,.3);
        color: #fff;
        position: absolute;
        top: 50%;
        cursor: pointer;
    }
    button#prev {
        left:0;
    }
    button#next {
        right:0;
    }
    button i {
        font-size: 18px;
    }
    .carousel-index-wrapper {
        width:65px;
        height:13px;
        overflow: hidden;
        position: absolute;
        bottom:15px;
        left:50%;
        margin-left: -33px;
    }
    .carousel-index-btn {
        width:9px;
        height:9px;
        float: left;
        margin:2px;
        background-color: #b7b7b7;
        border-radius: 50%;
        text-indent: -999em;
        /*这个-999em的笔墨对齐声明有助于加强可接见性。*/
        cursor: pointer;
    }
    .active-carousel-index-btn {
        background-color: #f44103;
    }

四、JS部份

这一块是主要部份,内容较多,因而我们逐渐来完成各部份功用以便于明白。

0.功用和构造剖析:

依据最最先的思绪解说,我们把这个轮播的JavaScript功用大抵分为以下4个部份:
1.摆布滑动按钮功用
2.索引按钮跳转功用
3.自动播放功用
4.轮回播放功用。

我们来离别逐渐完成。

1.完成摆布滑动按钮功用:

function addLoadEvent(func) {
    var oldLoad = window.onload;
    if (typeof oldLoad != 'function') {
        window.onload = func;
    } else {
        window.onload = function () {
            oldLoad();
            func();
        }
    }
}
//给文档加载完成后的load事宜绑定响应的处置惩罚函数:
addLoadEvent(preventDefaultAnchors);
addLoadEvent(carouselControl);

/*用一个对象把轮播组件的相干参数封装起来,长处是天真便于扩大晋级;瑕玷是同时也增加了文件的体积。*/
var carouselInfo = {
    itemWidth: 520,
    trueItemNum: 5,
    itemNum: 7,
    totalWidth: 7 * 520
};

//阻挠a标签默许的点击跳转行动
function preventDefaultAnchors() {
    var allAnchors = document.querySelectorAll('a');

    for (var i = 0; i < allAnchors.length; i++) {
        allAnchors[i].addEventListener('click', function (e) {
            e.preventDefault();
        }, false);
    }
}

function carouselControl () {
    var prev = document.querySelector("#prev");
    var next = document.querySelector("#next");
    var carouselWrapper = document.querySelector(".carousel-wrapper");

    prev.onclick = function () {
        slide(-1);
    };
    next.onclick = function () {
        slide(1);
    };
}

function slide(slideItemNum) {
    var itemWrapper=document.querySelector(".carousel-item-wrapper");
    var currentLeftOffset=(itemWrapper.style.left)?parseInt(itemWrapper.style.left): 0,
        targetLeftOffset=currentLeftOffset-(slideItemNum*carouselInfo.itemWidth);

    itemWrapper.style.left=targetLeftOffset+'px';
}

第1步的demo:carousel-step-1

2.完成索引按钮跳转功用:

function carouselControl() {
    var prev = document.querySelector("#prev");
    var next = document.querySelector("#next");
    var carouselWrapper = document.querySelector(".carousel-wrapper");
    //增加索引按钮的援用
    var indexBtns = document.querySelectorAll(".carousel-index-btn");

    //标记当前地点的图片编号,用于合营掌握.index-btn。
    var currentItemNum = 1;
    prev.onclick = function () {
        //把滑动功用和切换索引按钮功用装入一个函数当中,以便于猎取当前索引:
        currentItemNum=prevItem(currentItemNum);
    };
    next.onclick = function () {
        //把滑动功用和切换索引按钮功用装入一个函数当中,以便于猎取当前索引:
        currentItemNum=nextItem(currentItemNum);
    };

    for (var i = 0; i < indexBtns.length; i++) {
        //应用马上挪用函数,处理闭包的副作用,传入响应的index值
        (function (i) {
            indexBtns[i].onclick = function () {
                slideTo(i+1);
                currentItemNum=i+1;
            }
        })(i);
    }
}

function nextItem(currentItemNum) {
    slide(1);
    currentItemNum += 1;
    if (currentItemNum == 6) currentItemNum = 1;
    switchIndexBtn(currentItemNum);

    return currentItemNum;
}

function prevItem(currentItemNum) {
    slide(-1);
    currentItemNum -= 1;
    if (currentItemNum == 0) currentItemNum = 5;
    switchIndexBtn(currentItemNum);

    return currentItemNum;
}

//增加直接跳转函数:
function slideTo(targetNum) {
    var itemWrapper=document.querySelector(".carousel-item-wrapper");
    itemWrapper.style.left=-(targetNum*carouselInfo.itemWidth)+'px';
    switchIndexBtn(targetNum);
}

function slide(slideItemNum) {
    var itemWrapper = document.querySelector(".carousel-item-wrapper");
    var currentLeftOffset = (itemWrapper.style.left) ? parseInt(itemWrapper.style.left) : 0,
        targetLeftOffset = currentLeftOffset - (slideItemNum * carouselInfo.itemWidth);

    itemWrapper.style.left = targetLeftOffset + 'px';
}

function switchIndexBtn(targetNum) {
    //切换当前的索引按钮
    //删除过去激活的.active-carousel-index-btn
    var activeBtn=document.querySelector(".active-carousel-index-btn");
    activeBtn.className=activeBtn.className.replace(" active-carousel-index-btn","");

    //增加新的激活索引按钮
    var targetBtn=document.querySelectorAll(".carousel-index-btn")[targetNum-1];
    targetBtn.className+=" active-carousel-index-btn";
}

第2步的demo:carousel-step-2

3.完成自动播放功用:

function carouselControl() {
    //省略前面反复的代码......

    for (var i = 0; i < indexBtns.length; i++) {
        //应用马上挪用函数,处理闭包的副作用,传入响应的index值
        (function (i) {
            indexBtns[i].onclick = function () {
                slideTo(i+1);
                currentItemNum=i+1;
            }
        })(i);
    }

    //增加定时器
    var scrollTimer;
    function play() {
        scrollTimer=setInterval(function () {
            currentItemNum=nextItem(currentItemNum);
        },2000);
    }
    play();

    function stop() {
        clearInterval(scrollTimer);
    }

    //绑定事宜
    carouselWrapper.addEventListener('mouseover',stop);
    carouselWrapper.addEventListener('mouseout',play,false);

    /*DOM二级的addEventListener相对于on+sth的长处是:
     * 1.addEventListener能够前后增加多个事宜,同时这些事宜还不会互相掩盖。
     * 2.addEventListener能够掌握事宜触发阶段,经由过程第三个可选的useCapture参数挑选冒泡照样捕捉。
     * 3.addEventListener对任何DOM元素都有用,而不仅仅是HTML元素。*/
}

第3步的demo:carousel-step-3

4.症结点:完成轮回播放功用:

function slide(slideItemNum) {
    var itemWrapper = document.querySelector(".carousel-item-wrapper");
    var currentLeftOffset = (itemWrapper.style.left) ? parseInt(itemWrapper.style.left) : 0,
        targetLeftOffset = currentLeftOffset - (slideItemNum * carouselInfo.itemWidth);

    /*不在这里跳转了。先处置惩罚偏移值,完成轮回,再跳转。*/
    //itemWrapper.style.left = targetLeftOffset + 'px';

    switch (true) {
            /*switch 的语法是:当case当中的表达式即是switch (val)的val时,实行背面的statement(语句)。*/
        case (targetLeftOffset>0):
            itemWrapper.style.transition="none";
            itemWrapper.style.left=-carouselInfo.trueItemNum*carouselInfo.itemWidth+'px';
            /*此处即相当于:itemWrapper.style.left='-2600px';*/
            targetLeftOffset=-(carouselInfo.trueItemNum-1)*carouselInfo.itemWidth;
            //相当于:targetLeftOffset=-2080;
            break;
        case (targetLeftOffset<-(carouselInfo.totalWidth-carouselInfo.itemWidth)):
            //此处即相当于:targetLeftOffset<-3120
            itemWrapper.style.transition="none";
            itemWrapper.style.left=-carouselInfo.itemWidth+'px';
            //相当于:itemWrapper.style.left='-520px';
            targetLeftOffset=-carouselInfo.itemWidth*2;
            //相当于:targetLeftOffset=-1040;
            break;
    }

    /*这里我运用了setTimeout(fn,0)的hack
     * 参考bootstrap的carousel.js源码,好像也应用了setTimeout(fn,0)这一hack。
     *
     * stackoverflow上有对这一hack的议论和诠释:
     * http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful
     * 依据第二个回复,我个人的明白是:setTimeout(fn,0)相当于异步实行内部的代码fn,
     * 详细到这个轮播,就是在上一轮非过渡定位的页面衬着事情(switch语句内部的case)完毕以后,再实行setTimeout内部的过渡位移事情。
     * 从而避免了,非过渡的定位还未完毕,就恢复了过渡属性,使得这一次非过渡的定位也带有过渡结果。
     **/

    //列位能够试一试,把setTimeout内部的代码放在外部,“轮回”时会有什么样的毛病结果。
    //itemWrapper.style.transition="left .2s ease-in";
    //itemWrapper.style.left=targetLeftOffset+'px';

    setTimeout(function () {
        itemWrapper.style.transition="left .2s ease-in";
        itemWrapper.style.left=targetLeftOffset+'px';
    },20);
}

第4步的demo:carousel-step-4

至此,就完成了一个完全的轮回播放图片轮播,浏览一下本身的佳构吧~~~ヾ(✿゚▽゚)ノ

五、源码及示例:

1.GitHub堆栈地点及完全代码:JuniorTour/simple-standard-js-carousel

2.在线demo:原生JS轮回播放图片轮播组件 

很忸捏,只做了一点简朴的事情。假如以为本文不错的话,迎接给我的GitHub点赞!

    原文作者:帅到被人砍
    原文地址: https://segmentfault.com/a/1190000009706391
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞