簡(jiǎn)單的輪播圖,你會(huì)嗎?

開(kāi)始做第一個(gè)輪播圖

第一個(gè)輪播圖比較簡(jiǎn)單,顯示隱藏的方式輪播圖片。廢話少說(shuō)直接看效果。

效果
https://gksdnfla.github.io/banner/easy_banner/easy_banner.html
源碼
https://github.com/gksdnfla/banner/tree/master/easy_banner

我們先看一下 HTML 結(jié)構(gòu):

<ul class="banner-list">
    <li active>
        <a href="#" class="active"><img src="<!-- 圖片路徑 -->"></a>
    </li>
    <li>
        <a href="#"><img src="<!-- 圖片路徑 -->"></a>
    </li>
    <li>
        <a href="#"><img src="<!-- 圖片路徑 -->"></a>
    </li>
    <li>
        <a href="#"><img src="<!-- 圖片路徑 -->"></a>
    </li>
    <li>
        <a href="#"><img src="<!-- 圖片路徑 -->"></a>
    </li>
</ul>

我們的圖片是多個(gè)的,所以他是一個(gè)無(wú)序的列表圖片,用 ul,li 標(biāo)簽來(lái)搭結(jié)構(gòu)。然后輪播圖的片都是有一個(gè)鏈接的,可以進(jìn)行點(diǎn)擊,這時(shí)候用的是 a 標(biāo)簽。(其實(shí)我們可以用 javascript 來(lái)模擬 a 標(biāo)簽。但是一般的輪播圖都是放一些活動(dòng),廣告圖片的。是為了吸引顧客,所以我們需要做一個(gè)語(yǔ)義標(biāo)簽讓這些活動(dòng)頁(yè),能讓搜索引擎找到他,并能在搜索引擎提高優(yōu)先級(jí)。所以我們選 a 標(biāo)簽來(lái)讓這個(gè)鏈接在搜索引擎提高優(yōu)先級(jí)。)然后用 img 標(biāo)簽來(lái)顯示圖片。(其實(shí)用背景圖更方便,如果不想提高圖片的搜索引擎優(yōu)先級(jí)可以選擇用背景圖的方式去顯示,如果圖片需要提高搜索引擎的優(yōu)先級(jí),那需要用語(yǔ)義的標(biāo)簽來(lái)去顯示這張圖片。

再來(lái)看 CSS

body, ul, li {
    margin: 0;
    padding: 0;
    list-style: none;
}
.banner-list {
    width: 100%;
    height: 100%;
    overflow: hidden;
    position: absolute;
    left: 0;
    top: 0;
}
.banner-list li {
    width: 100%;
    height: 100%;
    transition: opacity 0.3s ease;
    opacity: 0;
    position: absolute;
    left: 0;
    top: 0;
}
.banner-list li[active] {
    opacity: 1;
}
.banner-list li img {
    width: 100%;
}

body,ul,li 的樣式是為了清掉默認(rèn)樣式。我給.banner-list給了絕對(duì)定位,因?yàn)槲业膱D片是全屏顯示的,要想給.banner-list{ height: 100%; },需要這個(gè)元素是的絕對(duì)定位或者是固定定位。這里顯然不適合用固定定位,所以選擇了絕對(duì)定位。然后用了 overflow 隱藏了多余的部分。然后為了 li 疊加到一個(gè)地方,再用絕對(duì)定位移動(dòng)到 left=0,top=0 的位置并把它隱藏掉。下一個(gè)就是用屬性選擇器來(lái)選擇有 active 屬性的 li 標(biāo)簽,并把顯示出來(lái)。最后把圖片的寬度設(shè)置為 100%,為了讓整張圖片顯示出來(lái)。

最后是 JavaScript

window.onload = function() {
    var aLi = document.querySelector(".banner-list li");
    var count = 0; //當(dāng)前顯示圖片的索引
    var length = aLi.length;

    window.setInterval(function() {
        count++; //每過(guò)5秒+1
        if (count >= length) {
            //當(dāng)count大于等于5的時(shí)候讓他變成0
            count = 0;
        }
        // 清楚所有的li的active屬性
        for (var i = 0; i < length; i++) {
            aLi[i].removeAttribute("active");
        }
        // 給要顯示的li加active屬性
        aLi[count].setAttribute("active", "");
    }, 5000);
};

接下來(lái)把這個(gè)代碼封裝成插件,創(chuàng)建一個(gè)對(duì)象就可以讓這個(gè) banner 做出來(lái)。

<div id="banner-box"></div>

用面相對(duì)象的方式來(lái)封裝,并對(duì)參數(shù)進(jìn)行限制。

function Banner(obj, images, options) {
  this.element = null;
  this.images = images;

  // 沒(méi)有傳options的時(shí)候?yàn)榱朔乐箞?bào)錯(cuò),給一個(gè)空的對(duì)象
  this.options = options || {};
  // 播放間隔默認(rèn)為5秒
  this.options.time = this.options.time || 5000;
  // 默認(rèn)為自動(dòng)播放
  this.options.autoPlay =
    "autoPlay" in this.options ? this.options.autoPlay : true;
  // 判斷obj是個(gè)字符串還是對(duì)象,如果是字符串就選擇這個(gè)元素,如果是對(duì)象賦給element,如果不是字符串或者是對(duì)象就停止運(yùn)行代碼
  switch (typeof obj) {
    case "string":
      this.element = document.querySelector(obj);
      break;
    case "object":
      this.element = obj;
      break;
    default:
      console.error("first argument is not selector or element!");
      return;
  }
  // 判斷images是不是數(shù)組,如果不是就停止運(yùn)行代碼
  if (!this.images instanceof Array) {
    console.error("second argument is not Array!");
    return;
  }

  // 當(dāng)前顯示圖片的索引
  this.count = 0;
  // 創(chuàng)建元素 *下面有這個(gè)方法的封裝
  this.createElement();
}

然后我們要?jiǎng)?chuàng)建 ul,li,a,img 元素,并添加樣式和屬性。

Banner.prototype.createElement = function() {
  var oUl = document.createElement("ul");

  // 給ul樣式和class
  oUl.className = "banner-list";
  oUl.style.width = "100%";
  oUl.style.height = "100%";
  oUl.style.listStyle = "none";
  oUl.style.margin = "0";
  oUl.style.padding = "0";
  oUl.style.overflow = "hidden";
  oUl.style.position = "absolute";
  oUl.style.left = "0";
  oUl.style.top = "0";

  // li,a,img標(biāo)簽都是跟圖片的數(shù)量一樣,所以我們用循環(huán)創(chuàng)建元素
  for (var i = 0; i < this.images.length; i++) {
    // 創(chuàng)建li,a,img元素
    var oLi = document.createElement("li");
    var oAchor = document.createElement("a");
    var oImg = document.createElement("img");

    // 給li加樣式
    oLi.style.width = "100%";
    oLi.style.height = "100%";
    i == 0 ? (oLi.style.opacity = "1") : (oLi.style.opacity = "0");
    oLi.style.transition = "0.3s ease";
    oLi.style.position = "absolute";
    oLi.style.left = "0";
    oLi.style.top = "0";

    // 給a元素加href  *this.images是傳進(jìn)來(lái)的參數(shù)
    oAchor.href = this.images[i].link;

    // 給img元素加樣式并給圖片路徑  *this.images是傳進(jìn)來(lái)的參數(shù)
    oImg.style.width = "100%";
    oImg.src = this.images[i].src;

    // li元素添加在ul元素里,a元素添加在li元素里,img元素添加在a元素里
    oUl.appendChild(oLi);
    oLi.appendChild(oAchor);
    oAchor.appendChild(oImg);
  }

  // 把ul元素添加到傳進(jìn)來(lái)的元素里
  this.element.appendChild(oUl);

  // 自動(dòng)播放
  if (this.options.autoPlay === true) this.autoPlay();
};

最后寫(xiě)一個(gè)自動(dòng)播放方法。

Banner.prototype.autoPlay = function() {
  // 因?yàn)槎〞r(shí)器里的匿名函數(shù),把this指向改成了window,所以我們用變量來(lái)存起來(lái),在定時(shí)器里調(diào)用。
  var that = this;
  // 獲取li元素
  var aLi = this.element.querySelectorAll(".banner-list li");

  // 定時(shí)器
  window.setInterval(function() {
    // 先把當(dāng)前顯示的圖片隱藏
    aLi[that.count].style.opacity = 0;
    // 把count改成要顯示的圖片索引
    that.count++;
    // 防止count多余images的長(zhǎng)度 *其實(shí)可以用if來(lái)判斷,我這里用這方式來(lái)防止
    that.count %= that.images.length;
    // 把要顯示的圖片顯示出來(lái)
    aLi[that.count].style.opacity = 1;
  }, this.options.time);
};

我們給樣式的時(shí)候是一個(gè)一個(gè)給的,這么給樣式很麻煩,所以模仿 jquery 里的 css 方法封裝一個(gè)函數(shù)

function css(elements) {
    // 判斷elements是不是數(shù)組
    if (elements instanceof Array) {
        // 判斷elements的第一個(gè)元素是不是對(duì)象
        if (typeof elements[0] !== "object") {
            console.error("first argument is not Dom element list!");
            return;
        }
    } else {
        // 判斷elements是不是對(duì)象
        if (typeof elements !== "object") {
            console.error("first argument is not Dom element!");
            return;
        }
    }
    // 判斷elements參數(shù)是不是一個(gè)數(shù)組或者是偽數(shù)組,這個(gè)時(shí)候就直接用elements參數(shù)
    // 如果elements參數(shù)不是數(shù)組和偽數(shù)組,就裝進(jìn)數(shù)據(jù)里
    // 這是為了讓elements保持是一個(gè)數(shù)組
    var elements = elements[0] ? elements : [elements];
    // 為了內(nèi)部函數(shù)也能訪問(wèn)到當(dāng)前函數(shù)的arguments,賦值到arg
    var arg = arguments;

    // 在JQuery里傳進(jìn)一個(gè)參數(shù)的時(shí)候是有兩種情況的
    if (arg.length === 2) {
        switch (typeof arg[1]) {
            // 參數(shù)是string的時(shí)候,就返回第一個(gè)元素的樣式
            case "string":
                return elements[0].style[arg[1]];
                break;
            // 參數(shù)是json的時(shí)候,把樣式賦給所有元素
            case "object":
                // forEach 封裝在下面,這個(gè)跟Es6的封裝差不多,不同的地方就是可以循環(huán)對(duì)象
                forEach(elements, function(element) {
                    forEach(arg[1], function(styleVal, styleName) {
                        element.style[styleName] = styleVal;
                    });
                });
                break;
            // 如果參數(shù)是其他類(lèi)型就報(bào)錯(cuò),這是自己加的錯(cuò)誤提示
            default:
                console.error(
                    "The second argument is not string or object!"
                );
                return;
        }
        // Jquery里兩個(gè)參數(shù)的時(shí)候是只有一個(gè)樣式的,只要把一個(gè)樣式賦值到所有元素就好
    } else if (arg.length === 3) {
        // 因?yàn)榈谝粋€(gè)參數(shù)肯定是一個(gè)string
        // 第二個(gè)參數(shù)有兩種類(lèi)型,string和number
        // 我自己給了限制,并提示錯(cuò)誤
        if (
            typeof arg[1] !== "string" &&
            (typeof arg[2] !== "string" || typeof arg[2] !== "number")
        ) {
            console.error("The second and third arguments is not string!");
            return;
        }

        forEach(elements, function(element) {
            element.style[arg[1]] = arg[2];
        });
    }
}

forEach 封裝

function forEach(obj, fn) {
    // 判斷obj是不是一個(gè)對(duì)象,如果不是報(bào)錯(cuò)
    if (typeof obj !== "object") {
        console.error("The first argument is not Array or Object!");
        return;
    }

    // 循環(huán)的時(shí)候其實(shí)都可以用 for in 循環(huán), 但是 for in 循環(huán)比f(wàn)or循環(huán)慢
    // 為了性能優(yōu)化我去判斷了是不是數(shù)組
    // 沒(méi)有用instanceof來(lái)判斷的原因是因?yàn)镴s里偽數(shù)組,偽數(shù)組也可以用for循環(huán)來(lái)循環(huán)。
    if (obj.length) {
        for (var i = 0; i < obj.length; i++) {
            //判斷fn是不是函數(shù),如果是函數(shù)就運(yùn)行fn
            typeof fn==='function' && fn(obj[i], i, obj);
        }
    } else {
        for (var key in obj) {
            typeof fn==='function' && fn(obj[key], key, obj);
        }
    }
}

封裝好 css 方法一以后可以用這個(gè)方法了

oUl.className = "banner-list";
oUl.style.width = "100%";
oUl.style.height = "100%";
oUl.style.listStyle = "none";
oUl.style.margin = "0";
oUl.style.padding = "0";
oUl.style.overflow = "hidden";
oUl.style.position = "absolute";
oUl.style.left = "0";
oUl.style.top = "0";
// 改成
css(oUl, {
    width: "100%",
    height: "100%",
    listStyle: "none",
    margin: 0,
    padding: 0,
    overflow: "hidden",
    position: "absolute",
    left: 0,
    top: 0
});

oLi.style.width = "100%";
oLi.style.height = "100%";
i == 0 ? (oLi.style.opacity = "1") : (oLi.style.opacity = "0");
oLi.style.transition = "0.3s ease";
oLi.style.position = "absolute";
oLi.style.left = "0";
oLi.style.top = "0";
// 改成
css(oLi, {
    width: "100%",
    height: "100%",
    opacity: i == 0 ? 1 : 0,
    transition: "0.3s ease",
    position: "absolute",
    left: 0,
    top: 0
});

oImg.style.width = "100%";
// 改成
css(oImg, "width", "100%");
// 最后一個(gè)改不改感覺(jué)沒(méi)啥區(qū)別,封裝了這種方式就這么用吧。。。。

2018.01.31日 添加切換輪播圖的功能

按照2018-01-30號(hào)的約定,更新了切換圖片的代碼。雖然自己給自己定的約定,希望以后是未來(lái)的讀者約定的。。。。。
forEach和css方法因?yàn)橛袀螖?shù)組這個(gè)坑爹東西稍微改動(dòng)了一下,我在上面改了代碼,如果31號(hào)前看過(guò)代碼的,就再去看一下改動(dòng)吧

開(kāi)始封裝滾輪事件綁定

如果這是上沒(méi)有兼容問(wèn)題,那我們的開(kāi)發(fā)就很簡(jiǎn)單。碰到這種每個(gè)瀏覽器用的別的事件的時(shí)候是最讓我們前端蛋疼的。只能吐槽吐槽。

function wheelEvent(obj, upFn, downFn) {
    var prefix = "";
    var _addEventListener = "";
    var support = "";

    // 判斷瀏覽器支不支持addEventListener方法
    if (window.addEventListener) {
        // 如果支持用addEventListener
        _addEventListener = "addEventListener";
    } else {
        // 如果不支持用attachEvent
        _addEventListener = "attachEvent";
        // *attachEvent綁定事件的時(shí)候前面要加on,所以給prefix存on,等綁定的時(shí)候用
        prefix = "on";
    }

    // 判斷該用哪一個(gè)事件
    support =
        "onwheel" in document.createElement("div")
            ? "wheel"
            : document.onmousewheel !== undefined
                ? "mousewheel"
                : "DOMMouseScroll";

    // 事件綁定
    obj[_addEventListener](prefix + support, function(ev) {
        var oEvent = ev || window.event;

        // 滾動(dòng)方向 true為往下,false為網(wǎng)上
        var down = oEvent.wheelDelta
            ? oEvent.wheelDelta < 0
            : oEvent.wheelDelta > 0;

        // 往下滾動(dòng),調(diào)用downFn callback
        // 網(wǎng)上滾動(dòng),調(diào)用upFn callback
        if (down) downFn && downFn(oEvent);
        else upFn && upFn(oEvent);
    });
}

封裝下一個(gè)輪播圖方法

這個(gè)代碼其實(shí)已經(jīng)寫(xiě)過(guò)了,我們?cè)谧詣?dòng)播放的時(shí)候,定時(shí)器里的代碼就是切換圖片的代碼,所以直接復(fù)制粘貼就好了

Banner.prototype.next = function() {
    var aLi = this.element.querySelectorAll(".banner-list li");

    // 先把當(dāng)前顯示的圖片隱藏
    aLi[this.count].style.opacity = 0;
    // 把count改成要顯示的圖片索引
    this.count++;
    // 防止count多余images的長(zhǎng)度 *其實(shí)可以用if來(lái)判斷,我這里用這種方式來(lái)防止
    this.count %= this.images.length;
    // 把要顯示的圖片顯示出來(lái)
    aLi[this.count].style.opacity = 1;
};

封裝上一個(gè)輪播圖方法,這個(gè)方法跟 next 方法很想,只需要在 next 方法稍微改一改就好了

Banner.prototype.prev = function() {
    var aLi = this.element.querySelectorAll(".banner-list li");

    // 先把當(dāng)前顯示的圖片隱藏
    aLi[this.count].style.opacity = 0;
    // 把count改成要顯示的圖片索引
    this.count--;
    // 防止count少于0
    if (this.count < 0) this.count = this.images.length - 1;
    // 把要顯示的圖片顯示出來(lái)
    aLi[this.count].style.opacity = 1;
};

我們要根據(jù)滾輪的方向決定播放上一張圖片還是下一張圖片

Banner.prototype.wheelEvent = function() {
    var that = this;

    wheelEvent(
        window,
        function(ev) {
            window.clearTimeout(that.upTimer);
            // 給一個(gè)定時(shí)器,防止?jié)L一次換多張圖片
            that.upTimer = window.setTimeout(function() {
                that.prev();
                // 自動(dòng)播放的方法有一些變化
                that.options.autoPlay && that.autoPlay();
            }, 50);
        },
        function(ev) {
            window.clearTimeout(that.downTimer);
            // 給一個(gè)定時(shí)器,防止?jié)L一次換多張圖片
            that.downTimer = window.setTimeout(function() {
                that.next();
                that.options.autoPlay && that.autoPlay();
            }, 50);
        }
    );
};

用滾輪切換圖片的時(shí)候,我們得關(guān)掉自動(dòng)播放的定時(shí)器,等圖片切換以后重新開(kāi)啟定時(shí)器。所以需要改一下自動(dòng)播放的方法

Banner.prototype.autoPlay = function() {
    // 因?yàn)槎〞r(shí)器里的匿名函數(shù),把this指向改成了window,所以我們用變量來(lái)存起來(lái),在定時(shí)器里調(diào)用。
    var that = this;

    // 定時(shí)器運(yùn)行之前需要先清一下定時(shí)器,這樣第二次調(diào)用這個(gè)方法的時(shí)候會(huì)清掉定時(shí)器重新計(jì)時(shí)。
    window.clearInterval(this.autoTimer);
    // 定時(shí)器
    this.autoTimer = window.setInterval(function() {
        that.next();
    }, this.options.time);
};

再做一個(gè) Icon,顯示播放到第幾張圖片了

創(chuàng)建 icon 的方法跟創(chuàng)建圖片很想,所以沒(méi)有加注釋

Banner.prototype.createIcon = function() {
    var oOl = document.createElement("ol");
    var width = 20;

    css(oOl, {
        width: width + "px",
        listStyle: "none",
        padding: 0,
        margin: 0,
        position: "absolute",
        right: "30px",
        top: "50%",
        transform: "translateY(-50%)"
    });
    oOl.className = "icon-list";

    forEach(this.images, function(item, index) {
        var oLi = document.createElement("li");

        css(oLi, {
            width: width + "px",
            height: width + "px",
            backgroundColor:
                index === 0
                    ? "rgba(255, 255, 255, 0.8)"
                    : "rgba(255, 255, 255, 0.3)",
            margin: "10px 0",
            borderRadius: "50%"
        });

        oOl.appendChild(oLi);
    });

    this.element.appendChild(oOl);
};
Banner.prototype.activeIcon = function() {
    if (!this.options.showIcon) return;

    // 獲取所有icon標(biāo)簽
    var aLi = this.element.querySelectorAll(".icon-list li");

    // 所有icon改成沒(méi)有點(diǎn)亮
    css(aLi, "background", "rgba(255, 255, 255, 0.3)");
    // 給當(dāng)前顯示的icon點(diǎn)亮
    css(aLi[this.count], "background", "rgba(255, 255, 255, 0.8)");
};

然后再 options 里添加別的參數(shù)

window.Banner = function(obj, images, options) {
    this.element = null;
    this.images = images;
    this.options = options || {};
    // 播放間隔默認(rèn)為5秒
    this.options.time = this.options.time || 5000;
    // 默認(rèn)為自動(dòng)播放
    this.options.autoPlay =
      "autoPlay" in this.options ? this.options.autoPlay : true;
    // 是否調(diào)用滾輪事件,默認(rèn)為false
    this.options.wheelEvent =
      "wheelEvent" in this.options ? this.options.wheelEvent : true;
    // 是否顯示icon
    this.options.showIcon =
      "showIcon" in this.options ? this.options.showIcon : true;

    // 判斷obj是個(gè)字符串還是對(duì)象,如果是字符串就選擇這個(gè)元素,如果是對(duì)象賦給element,如果不是字符串或者是對(duì)象就停止運(yùn)行代碼
    switch (typeof obj) {
        case "string":
            this.element = document.querySelector(obj);
            break;
        case "object":
            this.element = obj;
            break;
        default:
            console.error("The first argument is not selector or element!");
            return;
    }
    // 判斷images是不是數(shù)組,如果不是就停止運(yùn)行代碼
    if (!this.images instanceof Array) {
        console.error("The second argument is not Array!");
        return;
    }

    // 當(dāng)前播放圖片的索引
    this.count = 0;
    // 創(chuàng)建元素 *下面有這個(gè)方法的封裝
    this.createElement();

    // 自動(dòng)播放
    if (this.options.autoPlay === true) this.autoPlay();
    // 滾輪事件
    if (this.options.wheelEvent === true) this.wheelEvent();
};

這期的輪播圖結(jié)束了,這期主要講了封裝,下期也是用這種面向?qū)ο蟮膶?xiě)法,寫(xiě)一個(gè)經(jīng)常見(jiàn)到的輪播圖

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1.開(kāi)發(fā)環(huán)境 xcode 8.3.2、swift 3.1 pod: Kingfisher-swift版SDWebI...
    Devin_閱讀 389評(píng)論 0 0
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,188評(píng)論 25 708
  • 輪播圖(Carousels)這樣的交互方式,在web時(shí)代似乎已經(jīng)司空見(jiàn)慣。當(dāng)一群人在會(huì)議室里爭(zhēng)奪首屏焦點(diǎn)圖的優(yōu)先級(jí)...
    J_WongH閱讀 1,415評(píng)論 0 2
  • 輪播圖(Carousels)這樣的交互方式,在web時(shí)代似乎已經(jīng)司空見(jiàn)慣。當(dāng)一群人在會(huì)議室里爭(zhēng)奪首屏焦點(diǎn)圖的優(yōu)先級(jí)...
    Doria2016閱讀 1,335評(píng)論 1 22
  • 是會(huì)有那么一些時(shí)候 自己一個(gè)人 在凌晨三四點(diǎn)鐘 聽(tīng)音樂(lè) 看朋友圈 在床上輾轉(zhuǎn)反側(cè) 打了好多哈欠 眼睛也疲憊的出了汗...
    陰陽(yáng)先生閱讀 1,027評(píng)論 0 1

友情鏈接更多精彩內(nèi)容