早在幾個月前,就想本身動手寫個輪播圖組件,所以也看了許多文章,斷斷續續過了幾個月,今天終於有時間騰出手來給此插件作個總結,所以有了這篇文章。話很少說,先上 Demo, 效果以下:javascript
本文不討論html,css的實現方式,直接貼上代碼css
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Suporka Vue App</title> </head> <body> <div id="carousal"> <!--左箭頭--> <button type="button" class="suporka-carousel__arrow suporka-carousel__arrow--left" style="" id="suporka-prev-btn" > < </button> <div id="wrapper"> <div class="box"> <img src="https://zxpsuper.github.io/Demo/carousal/banner1.jpg" alt="" /> </div> <div class="box"> <img src="https://zxpsuper.github.io/Demo/carousal/banner2.jpg" alt="" /> </div> <div class="box"> <img src="https://zxpsuper.github.io/Demo/carousal/banner3.jpg" alt="" /> </div> <div class="box"> <img src="https://zxpsuper.github.io/Demo/carousal/banner4.jpg" alt="" /> </div> </div> <!--右箭頭--> <button type="button" class="suporka-carousel__arrow suporka-carousel__arrow--right" style="" id="suporka-next-btn" > > </button> </div> <script src="https://cdn.jsdelivr.net/npm/suporka-carousal@1.0.3/carousal.js"></script> <script> new Carousal({ autoScroll: true, showDot: true }); </script> </body> </html>
/*樣式代碼*/ * { margin: 0; padding: 0; } div { margin: 0; border: 0; padding: 0; } #carousal { width: 557px; overflow: hidden; position: relative; } #wrapper { display: box; /* OLD - Android 4.4- */ display: -webkit-box; /* OLD - iOS 6-, Safari 3.1-6 */ display: -moz-box; /* OLD - Firefox 19- (buggy but mostly works) */ display: -ms-flexbox; /* TWEENER - IE 10 */ display: -webkit-flex; /* NEW - Chrome */ display: flex; -webkit-flex-wrap: nowrap; -moz-flex-wrap: nowrap; -ms-flex-wrap: nowrap; -o-flex-wrap: nowrap; flex-wrap: nowrap; white-space: nowrap; position: relative; left: 0; } .suporka-carousel__arrow { /* display: none; */ border: none; outline: none; padding: 0; margin: 0; height: 36px; width: 36px; cursor: pointer; transition: 0.3s; border-radius: 50%; background-color: rgba(31, 45, 61, 0.5); color: #fff; position: absolute; top: 50%; z-index: 10; transform: translateY(-50%); text-align: center; font-size: 12px; font-weight: 600; } .suporka-carousel__arrow--right { right: -38px; } .suporka-carousel__arrow--left { left: -38px; } #carousal:hover > .suporkal-carousel__arrow { display: block; } #carousal:hover .suporka-carousel__arrow--right { right: 16px; } #carousal:hover .suporka-carousel__arrow--left { left: 16px; } #suporka-dot { position: absolute; bottom: 20px; left: 50%; transform: translate(-50%, 0); } #suporka-dot span { width: 10px; height: 10px; border-radius: 50%; margin: 0 10px; display: inline-block; background: #999; } #suporka-dot .suporka-dot--acitve { background: #fff; }
本插件是用 es6 寫的,固然考慮兼容性,你能夠選擇 babel 進行編譯,本文不作闡述。html
它有一些默認參數,如time(圖片輪播間隔),transition (轉場動畫時間),autoScroll(是否自動輪播),showDot(是否顯示底部小圓點)。而後將頁面的一些元素掛載在類的屬性上。java
class Carousal { constructor(userOption) { this.option = { time: 4000, transition: 0.8, autoScroll: true, showDot: false }; // 當前索引 this.number = 1; // 定時器 this.timer = null; this.interval = null; this.carousal = document.getElementById("carousal"); this.wrapper = document.querySelector("#wrapper"); this.childrenLength: document.getElementById("wrapper").children.length; this.init(userOption); } }
固然,默認參數是能夠修改的,因此類傳入了一個 userOption 對象, 在構造函數中將用戶設置的參數覆蓋默認參數,在this.init(userOption) 方法中執行覆蓋。node
輪播圖輪播的原理是:在輪播圖組首位添加一個末位圖片的副本,同時也在輪播圖末位添加一個首位圖片的副本,大概就是 5 1 2 3 4 5 1
, 此時共有7張圖片,當向右輪播至第七張圖片‘1’ 時, 取消transition後輪播圖定位至第二張圖片 ‘1’, 此時再度開啓transition 。同理,向左輪播至第一張圖片「5」時,也會取消transition後輪播圖定位至第六張圖片 ‘5’, 然後再度開啓 transition。git
所以,咱們須要手動在dom結構中插入這兩個首尾圖片。pushItem() 方法正是爲此而生。es6
class Carousal { //... init(userOption) { // 合併用戶配置 if (Object.assign) { Object.assign(this.option, userOption); } else { // 不支持 Object.assign 就調用 extend 方法 this.extend(this.option, userOption, true); } // 設置動畫 transition this.wrapper.style.transition = `all ${this.option.transition}s`; this.wrapper.style["-moz-transition"] = `all ${this.option.transition}s`; this.wrapper.style["-webkit-transition"] = `all ${this.option.transition}s`; this.wrapper.style["-o-transition"] = `all ${this.option.transition}s`; // 首尾添加元素 this.pushItem(); } // 合併屬性方法 extend(o, n, override) { for (var p in n) { if (n.hasOwnProperty(p) && (!o.hasOwnProperty(p) || override)) o[p] = n[p]; } } // 初始化添加首尾子元素 pushItem() { let movePx = this.carousal.offsetWidth; // 獲取輪播圖寬度 let first = this.wrapper.children[0].cloneNode(true); let last = this.wrapper.children[this.childrenLength - 1].cloneNode(true); let parent = this.wrapper; parent.appendChild(first); parent.insertBefore(last, parent.children[0]); this.wrapper.style.left = this.wrapper.offsetLeft - movePx + "px"; } }
插入輪播圖片以後,判斷是否須要插入底部小圓點。requestAnimFrame()用於實現持續的動畫效果。github
class Carousal { init() { //...續 this.pushItem(); if (this.option.showDot) { let node = document.createElement('div'); node.setAttribute('id', 'suporka-dot'); node.innerHTML = `${'<span></span>'.repeat(this.childrenLength)}`; this.carousal.appendChild(node); this.dot = document.getElementById('suporka-dot'); this.dot.firstChild.setAttribute('class', 'suporka-dot--acitve'); } // 判斷是否開啓自動輪播,如是則自動輪播 if (this.option.autoScroll) this.requestAnimFrame(this.autoMove()); } requestAnimFrame() { return ( window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); } ); } }
監聽鼠標移入事件,當鼠標移入的時候,中止自動滾動。web
監聽左右按鈕的點擊,執行上一張,下一張圖的輪播效果。npm
class Carousal { init() { //...續 if (this.option.autoScroll) this.requestAnimFrame(this.autoMove()); this.addEventListener(); } // 添加事件 addEvent(element, type, handler) { if (element.addEventListener) { element.addEventListener(type, handler, false); } else if (element.attachEvent) { element.attachEvent('on' + type, handler); } else { element['on' + type] = handler; } } // 事件監聽 addEventListener() { if (this.option.autoScroll) { this.addEvent(this.carousal, 'mouseover', event => { clearInterval(this.interval); }); this.addEvent(this.carousal, 'mouseout', event => { this.autoMove(); }); } let prev = document.getElementById('suporka-prev-btn'); let next = document.getElementById('suporka-next-btn'); if (prev && next) { this.addEvent(prev, 'click', event => { this.prev(); }); this.addEvent(next, 'click', event => { this.next(); }); } } }
定時動畫,而且若是存在底部小圓點,修改其類名,達到與輪播圖同步的效果。
// 自動輪播 class Carousal { // ... autoMove() { let movePx = this.carousal.offsetWidth; this.interval = setInterval(() => { this.number += 1; this.wrapper.style.left = 0 - movePx * this.number + 'px'; if (this.number === this.childrenLength + 1) this.startMove(); if (this.dot) this.setDotClass( this.dot.children, this.number - 1, 'suporka-dot--acitve' ); }, this.option.time); } // 開始移動 startMove() { this.number = 1; this.timer = setTimeout(() => { this.wrapper.style.transition = `none`; this.wrapper.style.left = -this.carousal.offsetWidth + 'px'; setTimeout(() => { this.wrapper.style.transition = `all ${ this.option.transition }s`; }, 100); }, this.option.transition * 1000); } // 設置小圓點樣式 setDotClass(parent, index, cls) { // 沒有小圓點就返回 if (!this.dot) return false; for (let i = 0; i < parent.length; i++) { removeClass(parent[i], cls); } addClass(parent[index], cls); } } // 三個類名操做方法 function hasClass(ele, cls) { if (ele.className) return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')); else return false; } function addClass(ele, cls) { if (!hasClass(ele, cls)) ele.className += ' ' + cls; } function removeClass(ele, cls) { if (hasClass(ele, cls)) { let reg = new RegExp('(\\s|^)' + cls + '(\\s|$)'); ele.className = ele.className.replace(reg, ' '); } }
class Carousal { //... // prev上一張 prev() { let movePx = this.carousal.offsetWidth; this.number -= 1; this.wrapper.style.left = 0 - movePx * this.number + 'px'; if (this.number === 0) this.goLastOne(); if (this.dot) this.setDotClass( this.dot.children, this.number - 1, 'suporka-dot--acitve' ); } // 下一張 next() { let movePx = this.carousal.offsetWidth; this.number += 1; this.wrapper.style.left = 0 - movePx * this.number + 'px'; if (this.number === this.childrenLength + 1) this.startMove(); if (this.dot) this.setDotClass( this.dot.children, this.number - 1, 'suporka-dot--acitve' ); } // 去到最後一張 goLastOne() { this.number = this.childrenLength; this.timer = setTimeout(() => { this.wrapper.style.transition = `none`; this.wrapper.style.left = -this.carousal.offsetWidth * this.childrenLength + 'px'; setTimeout(() => { this.wrapper.style.transition = `all ${ this.option.transition }s`; }, 100); }, this.option.transition * 1000); } }
最後,代碼需通過babel轉譯,而且以 umd 的形式支持瀏覽器直接引入,requirejs 及 commonjs 導入,詳細作法能夠參考我以前的一篇文章《ES6 手寫一個「辨色」小遊戲》。也能夠參考我在 github 上的代碼, 歡迎 fork and star .