開(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();
};