摘要:綁定輪播事件然后是鼠標移入移出事件的綁定鼠標移入移出事件移入時停止輪播播放的定時器,移出后自動開始下一張的播放。
通過上一篇文章的學(xué)習,我們基本掌握了一個輪子的封裝和開發(fā)流程。那么這次將帶大家開發(fā)一個更有難度的項目——輪播圖,希望能進一步加深大家對于面向?qū)ο蟛寮_發(fā)的理解和認識。
So, Let"s begin!
目前項目使用 ES5及UMD 規(guī)范封裝,所以在前端暫時只支持標簽的引入方式,未來會逐步用 ES6 進行重構(gòu)
演示地址:carousel carousel-mobile
Github:csdwheels
如果覺得好用就點個Star吧~(〃"▽"〃)
老規(guī)矩,在寫代碼之前,我們需要對要開發(fā)的東西有個感性的認識,比如你可以先在腦中大致過一遍最終的項目效果是如何的,而在這里你可以直接看上面的動態(tài)圖or項目頁面進行體驗。實際的開發(fā)階段之前,我們更要對插件的邏輯思路有一個整體的分析,這樣在開發(fā)時才會更有效率,并且可以有效避免因為思路不清晰而導(dǎo)致的問題。
首先來看看Web輪播的效果及交互有哪些:
每隔一段時間自動輪播
左右箭頭可切換輪播
圓點可切換輪播
當鼠標在輪播區(qū)域內(nèi)時,輪播暫停;離開區(qū)域后,輪播重新開始
輪播切換時,會有一個勻速運動的動畫效果
當向右切換到最后一張時,會自動循環(huán)到第一張;向左切換到第一張時,循環(huán)到最后一張
如上幾點,可以說都是一個輪播圖必須實現(xiàn)的經(jīng)典效果了。其他效果先忽略,第六點對于新手來說明顯是最有難度的,事實上這個效果有個常見的名字——無縫輪播。“無縫”也可以理解為無限循環(huán),其實就是可以讓輪播朝著一個方向一直切換,并且自動在切換到頭尾圖片時循環(huán)。
比如現(xiàn)在有五張圖片,我們把它們編號為:
1 2 3 4 5
要實現(xiàn)上面的效果,你可能會想到在切換至頭尾時加個判斷,強制改變圖片位置,但是如果這么做的話,當你最后一張圖切換回第一張圖時就會出現(xiàn)空白,因此還需要在頭尾分別添加一個尾部和頭部的元素作為位置改變時的過渡:
5 1 2 3 4 5 1
有了這兩張輔助圖,上面的效果就能順利實現(xiàn)了。到此,項目的基礎(chǔ)思路分析完畢,讓我們進入編碼階段吧!
基本架構(gòu)正式開始之前,還是需要先把項目的基本架構(gòu)搭建起來:
(function(root, factory) { if (typeof define === "function" && define.amd) { define([], factory); } else if (typeof module === "object" && module.exports) { module.exports = factory(); } else { root.Carousel = factory(); } })(typeof self !== "undefined" ? self : this, function() { "use strict"; // ID-NAMES var ID = { CAROUSEL_WRAP: "#carouselWrap", CAROUSEL_DOTS: "#carouselDots", ARROW_LEFT: "#arrowLeft", ARROW_RIGHT: "#arrowRight" }; var CLASS = { CAROUSEL_WRAP: "carousel-wrap", CAROUSEL_IMG: "carousel-image", CAROUSEL_DOTS_WRAP: "carousel-buttons-wrap", CAROUSEL_DOTS: "carousel-buttons", CAROUSEL_DOT: "carousel-button", CAROUSEL_DOT_ON: "carousel-button on", CAROUSEL_ARROW_LEFT: "carousel-arrow arrow-left", CAROUSEL_ARROW_RIGHT: "carousel-arrow arrow-right" }; // Polyfills function 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; } } // 合并對象 function extend(o, n, override) { for (var p in n) { if (n.hasOwnProperty(p) && (!o.hasOwnProperty(p) || override)) o[p] = n[p]; } } // 輪播-構(gòu)造函數(shù) var Carousel = function (selector, userOptions) { var _this = this; // 合并配置 extend(this.carouselOptions, userOptions, true); // 獲取輪播元素 _this.carousel = document.querySelector(selector); // 初始化輪播列表 _this.carousel.appendChild(_this.getImgs()); // 獲取輪播列表 _this.carouselWrap = document.querySelector(ID.CAROUSEL_WRAP); // 每隔 50ms 檢測一次輪播是否加載完成 var checkInterval = 50; var checkTimer = setInterval(function () { // 檢測輪播是否加載完成 if (_this.isCarouselComplete()) { // 加載完成后清除定時器 clearInterval(checkTimer); // 初始化輪播 _this.initCarousel(); // 初始化圓點 _this.initDots(); // 初識化箭頭 _this.initArrows(); } }, checkInterval); }; // 輪播-原型對象 Carousel.prototype = { carouselOptions: { // 是否顯示輪播箭頭 showCarouselArrow: true, // 是否顯示輪播圓點 showCarouselDot: true, // 輪播自動播放間隔 carouselInterval: 3000, // 輪播動畫總時間 carouselAnimateTime: 150, // 輪播動畫間隔 carouselAnimateInterval: 10 }, isCarouselComplete: function () { // 檢測頁面圖片是否加載完成 var completeCount = 0; for (var i = 0; i < this.carouselWrap.children.length; i++) { if (this.carouselWrap.children[i].complete) { completeCount++; } } return completeCount === this.carouselWrap.children.length ? true : false; } }; return Carousel; });
addEvent()和extend()函數(shù)上篇已經(jīng)介紹過了,構(gòu)造函數(shù)中各種配置項也都是項目中要用到的,不必多說。這里的重點是checkTimer定時器,它的作用是每隔一定時間去檢查頁面上的圖片元素是否全部加載完畢,如果加載完畢再進行項目的初始化。
為什么需要這么做呢?因為我們的圖片元素是在JS中使用DOM動態(tài)加載的,所以可能會出現(xiàn)圖片還沒加載完成就執(zhí)行了JS中的一些邏輯語句,導(dǎo)致不能通過DOM API正確獲取到圖片和對應(yīng)元素屬性的現(xiàn)象。因此,在isCarouselComplete()函數(shù)中我們利用img元素的complete屬性,判斷當前頁面上的所有圖片是否加載完成,然后就能保證DOM屬性的正確獲取了。
完成initCarousel()函數(shù):
initCarousel: function(selector, userOptions) { // 獲取輪播數(shù)量 this.carouselCount = this.carouselWrap.children.length; // 設(shè)置輪播 this.setCarousel(); // 初始化輪播序號 this.carouselIndex = 1; // 初始化定時器 this.carouselIntervalr = null; // 每次位移量 = 總偏移量 / 次數(shù) this.carouselAnimateSpeed = this.carouselWidth / (this.carouselOptions.carouselAnimateTime / this.carouselOptions.carouselAnimateInterval); // 判斷是否處于輪播動畫狀態(tài) this.isCarouselAnimate = false; // 判斷圓點是否點擊 this.isDotClick = false; // 綁定輪播圖事件 this.bindCarousel(); // 播放輪播 this.playCarousel(); }
通過this.carouselWidth / (this.carouselOptions.carouselAnimateTime / this.carouselOptions.carouselAnimateInterval)這個公式,可以計算出每次輪播動畫位移的偏移量,后面完成動畫函數(shù)時會用到。
在setCarousel()里進行輪播基本屬性的設(shè)置:
setCarousel: function () { // 復(fù)制首尾節(jié)點 var first = this.carouselWrap.children[0].cloneNode(true); var last = this.carouselWrap.children[this.carouselCount - 1].cloneNode(true); // 添加過渡元素 this.carouselWrap.insertBefore(last, this.carouselWrap.children[0]); this.carouselWrap.appendChild(first); // 設(shè)置輪播寬度 this.setWidth(this.carousel, this.carouselOptions.carouselWidth); // 設(shè)置輪播高度 this.setHeight(this.carousel, this.carouselOptions.carouselHeight); // 獲取輪播寬度 this.carouselWidth = this.getWidth(this.carousel); // 設(shè)置初始位置 this.setLeft(this.carouselWrap, -this.carouselWidth); // 設(shè)置輪播長度 this.setWidth(this.carouselWrap, this.carouselWidth * this.carouselWrap.children.length); }
添加首尾的過渡元素、設(shè)置高度寬度等。
綁定輪播事件然后是鼠標移入移出事件的綁定:
playCarousel: function () { var _this = this; this.carouselIntervalr = window.setInterval(function() { _this.nextCarousel(); }, this.carouselOptions.carouselInterval); }, bindCarousel: function () { var _this = this; // 鼠標移入移出事件 addEvent(this.carousel, "mouseenter", function(e) { clearInterval(_this.carouselIntervalr); }); addEvent(this.carousel, "mouseleave", function(e) { _this.playCarousel(); }); }
移入時停止輪播播放的定時器,移出后自動開始下一張的播放。
完成nextCarousel()和prevCarousel()函數(shù):
prevCarousel: function () { if (!this.isCarouselAnimate) { // 改變輪播序號 this.carouselIndex--; if (this.carouselIndex < 1) { this.carouselIndex = this.carouselCount; } // 設(shè)置輪播位置 this.moveCarousel(this.isFirstCarousel(), this.carouselWidth); if (this.carouselOptions.showCarouselDot) { // 顯示當前圓點 this.setDot(); } } }, nextCarousel: function () { if (!this.isCarouselAnimate) { this.carouselIndex++; if (this.carouselIndex > this.carouselCount) { this.carouselIndex = 1; } this.moveCarousel(this.isLastCarousel(), -this.carouselWidth); if (this.carouselOptions.showCarouselDot) { // 顯示當前圓點 this.setDot(); } } }
功能是一樣的,改變輪播序號,然后進行輪播的移動。在moveCarousel()中完成對過渡元素的處理:
moveCarousel: function (status, carouselWidth) { var left = 0; if (status) { left = -this.carouselIndex * this.carouselWidth; } else { left = this.getLeft(this.carouselWrap) + carouselWidth; } this.setLeft(this.carouselWrap, left); }
輪播相關(guān)屬性和事件的設(shè)置就完成了。
綁定圓點事件接下來是小圓點的事件綁定:
bindDots: function () { var _this = this; for (var i = 0, len = this.carouselDots.children.length; i < len; i++) { (function(i) { addEvent(_this.carouselDots.children[i], "click", function (ev) { // 獲取點擊的圓點序號 _this.dotIndex = i + 1; if (!_this.isCarouselAnimate && _this.carouselIndex !== _this.dotIndex) { // 改變圓點點擊狀態(tài) _this.isDotClick = true; // 改變圓點位置 _this.moveDot(); } }); })(i); } }, moveDot: function () { // 改變當前輪播序號 this.carouselIndex = this.dotIndex; // 設(shè)置輪播位置 this.setLeft(this.carouselWrap, -this.carouselIndex * this.carouselWidth); // 重設(shè)當前圓點樣式 this.setDot(); }, setDot: function () { for (var i = 0, len = this.carouselDots.children.length; i < len; i++) { this.carouselDots.children[i].setAttribute("class", CLASS.CAROUSEL_DOT); } this.carouselDots.children[this.carouselIndex - 1].setAttribute("class", CLASS.CAROUSEL_DOT_ON); }
功能很簡單,點擊圓點后,跳轉(zhuǎn)到對應(yīng)序號的輪播圖,并重設(shè)小圓點樣式。
綁定箭頭事件最后,還需要綁定箭頭事件:
bindArrows: function () { var _this = this; // 箭頭點擊事件 addEvent(this.arrowLeft, "click", function(e) { _this.prevCarousel(); }); addEvent(this.arrowRight, "click", function(e) { _this.nextCarousel(); }); }
這樣,一個沒有動畫的無縫輪播效果就完成了,見下圖:
上一節(jié)我們分析后的思路基本是實現(xiàn)了,但是輪播切換時的動畫效果又該怎么實現(xiàn)呢?
既然要實現(xiàn)動畫,那我們先要找到產(chǎn)生動畫的源頭——即讓輪播發(fā)生切換的moveCarousel()函數(shù)。因此,我們需要先對它進行修改:
moveCarousel: function (target, speed) { var _this = this; _this.isCarouselAnimate = true; function animateCarousel () { if ((speed > 0 && _this.getLeft(_this.carouselWrap) < target) || (speed < 0 && _this.getLeft(_this.carouselWrap) > target)) { _this.setLeft(_this.carouselWrap, _this.getLeft(_this.carouselWrap) + speed); timer = window.setTimeout(animateCarousel, _this.carouselOptions.carouselAnimateInterval); } else { window.clearTimeout(timer); // 重置輪播狀態(tài) _this.resetCarousel(target, speed); } } var timer = animateCarousel(); }
改造之后的moveCarousel()函數(shù)接受兩個參數(shù),target表示要移動到的輪播的位置,speed即為我們前面計算出的那個偏移量的值。然后通過animateCarousel()函數(shù)進行setTimeout()的遞歸調(diào)用,模擬出一種定時器的效果,當判斷未到達目標位置時繼續(xù)遞歸,如果到達后就重置輪播狀態(tài):
// 不符合位移條件,把當前l(fā)eft值置為目標值 this.setLeft(this.carouselWrap, target); //如當前在輔助圖上,就歸位到真的圖上 if (target > -this.carouselWidth ) { this.setLeft(this.carouselWrap, -this.carouselCount * this.carouselWidth); } if (target < (-this.carouselWidth * this.carouselCount)) { this.setLeft(this.carouselWrap, -this.carouselWidth); }
重置的過程和前面的實現(xiàn)是一樣的,不再贅述。完成新的moveCarousel()函數(shù)之后,還需要對prevCarousel()及nextCarousel()進行改造:
prevCarousel: function () { if (!this.isCarouselAnimate) { // 改變輪播序號 this.carouselIndex--; if (this.carouselIndex < 1) { this.carouselIndex = this.carouselCount; } // 設(shè)置輪播位置 this.moveCarousel(this.getLeft(this.carouselWrap) + this.carouselWidth, this.carouselAnimateSpeed); if (this.carouselOptions.showCarouselDot) { // 顯示當前圓點 this.setDot(); } } }, nextCarousel: function () { if (!this.isCarouselAnimate) { this.carouselIndex++; if (this.carouselIndex > this.carouselCount) { this.carouselIndex = 1; } this.moveCarousel(this.getLeft(this.carouselWrap) - this.carouselWidth, -this.carouselAnimateSpeed); if (this.carouselOptions.showCarouselDot) { // 顯示當前圓點 this.setDot(); } } },
其實就替換了一下moveCarousel()調(diào)用的參數(shù)而已。完成這幾個函數(shù)的改造后,動畫效果初步實現(xiàn)了:
在頁面上進行實際測試的過程中,我們可能偶爾會發(fā)現(xiàn)有卡頓的情況出現(xiàn),這主要是因為用setTimeout()遞歸后模擬動畫的時候產(chǎn)生的(直接用setInterval()同樣會出現(xiàn)這種情況),所以我們需要用requestAnimationFrame這個HTML5的新API進行動畫效率的優(yōu)化,再次改造moveCarousel()函數(shù):
moveCarousel: function (target, speed) { var _this = this; _this.isCarouselAnimate = true; function animateCarousel () { if ((speed > 0 && _this.getLeft(_this.carouselWrap) < target) || (speed < 0 && _this.getLeft(_this.carouselWrap) > target)) { _this.setLeft(_this.carouselWrap, _this.getLeft(_this.carouselWrap) + speed); timer = window.requestAnimationFrame(animateCarousel); } else { window.cancelAnimationFrame(timer); // 重置輪播狀態(tài) _this.resetCarousel(target, speed); } } var timer = window.requestAnimationFrame(animateCarousel); }
兩種方法的調(diào)用方式是類似的,但是在實際看起來,動畫卻流暢了不少,最重要的,它讓我們動畫的效率得到了很大提升。
到這里,我們的開發(fā)就結(jié)束了嗎?
用上面的方式實現(xiàn)完動畫后,當你點擊圓點時,輪播的切換是跳躍式的,并沒有達到我們開頭gif中那種完成后的效果。要讓任意圓點點擊后的切換效果仍然像相鄰圖片一樣的切換,這里還需要一種新的思路。
假如我們當前在第一張圖片,這時候的序號為1,而點擊的圓點對應(yīng)圖片序號為5的話,我們可以這么處理:在序號1對應(yīng)圖片節(jié)點的后面插入一個序號5對應(yīng)的圖片節(jié)點,然后讓輪播切換到這張新增的圖片,切換完成后,立即改變圖片位置為真正的序號5圖片,最后刪除新增的節(jié)點,過程如下:
第一步:插入一個新節(jié)點 5 1 5 2 3 4 5 1第二步:改變圖片位置,節(jié)點順序不變
第三步:刪除新節(jié)點,還原節(jié)點順序 5 1 2 3 4 5 1
用代碼實現(xiàn)出來就是這樣的:
moveDot: function () { // 改變輪播DOM,增加過渡效果 this.changeCarousel(); // 改變當前輪播序號 this.carouselIndex = this.dotIndex; // 重設(shè)當前圓點樣式 this.setDot(); }, changeCarousel: function () { // 保存當前節(jié)點位置 this.currentNode = this.carouselWrap.children[this.carouselIndex]; // 獲取目標節(jié)點位置 var targetNode = this.carouselWrap.children[this.dotIndex]; // 判斷點擊圓點與當前的相對位置 if (this.carouselIndex < this.dotIndex) { // 在當前元素右邊插入目標節(jié)點 var nextNode = this.currentNode.nextElementSibling; this.carouselWrap.insertBefore(targetNode.cloneNode(true), nextNode); this.moveCarousel(this.getLeft(this.carouselWrap) - this.carouselWidth, -this.carouselAnimateSpeed); } if (this.carouselIndex > this.dotIndex) { // 在當前元素左邊插入目標節(jié)點 this.carouselWrap.insertBefore(targetNode.cloneNode(true), this.currentNode); // 因為向左邊插入節(jié)點后,當前元素的位置被改變,導(dǎo)致畫面有抖動現(xiàn)象,這里重置為新的位置 this.setLeft(this.carouselWrap, -(this.carouselIndex + 1) * this.carouselWidth); this.moveCarousel(this.getLeft(this.carouselWrap) + this.carouselWidth, this.carouselAnimateSpeed); } }
需要注意的是,這里要判斷點擊的圓點序號與當前序號的關(guān)系,也就是在當前序號的左邊還是右邊,如果是左邊,還需要對位置進行重置。最后一步,完成新增節(jié)點的刪除函數(shù)resetMoveDot():
resetCarousel: function (target, speed) { // 判斷圓點是否點擊 if (this.isDotClick) { // 重置圓點點擊后的狀態(tài) this.resetMoveDot(speed); } else { // 重置箭頭或者自動輪播后的狀態(tài) this.resetMoveCarousel(target); } this.isDotClick = false; this.isCarouselAnimate = false; }, resetMoveDot: function (speed) { // 如果是圓點點擊觸發(fā)動畫,需要刪除新增的過度節(jié)點并將輪播位置重置到實際位置 this.setLeft(this.carouselWrap, -this.dotIndex * this.carouselWidth); // 判斷點擊圓點和當前圓點的相對位置 if (speed < 0) { this.carouselWrap.removeChild(this.currentNode.nextElementSibling); } else { this.carouselWrap.removeChild(this.currentNode.previousElementSibling); } },
查看一下效果:
大功告成!!
H5輪播在Web版輪播的實現(xiàn)中,我們對位置的控制是直接使用元素絕對定位后的left值實現(xiàn)的,這種辦法雖然兼容性好,但是效率相對是比較低的。在移動端版本的實現(xiàn)中,我們就可以不用考慮這種兼容性的問題了,而可以盡量用更高效的方式實現(xiàn)動畫效果。
如果大家對CSS3有所了解,那想必一定知道transform這個屬性。從字面上來講,它就是變形,改變的意思,而它的值大致包括旋轉(zhuǎn)rotate、扭曲skew、縮放scale和移動translate以及矩陣變形matrix等幾種類型。我們今天需要用到的就是translate,通過使用它以及transition等動畫屬性,可以更高效簡潔的實現(xiàn)移動端圖片輪播的移動。
由于基本思路與架構(gòu)和Web版是差不多的,而H5版是基于Web版重寫的,所以這里只說下需要改變的幾個地方。
替換Left的操作方法既然是用新屬性來實現(xiàn),那首先就要重寫setLeft()和getLeft()方法,這里我們直接替換為兩個新方法:
setLeft: function (elem, value) { elem.style.left = value + "px"; }, getLeft: function (elem) { return parseInt(elem.style.left); } setTransform: function(elem ,value) { elem.style.transform = "translate3d(" + value + "px, 0px, 0px)"; elem.style["-webkit-transform"] = "translate3d(" + value + "px, 0px, 0px)"; elem.style["-ms-transform"] = "translate3d(" + value + "px, 0px, 0px)"; }, getTransform: function() { var x = this.carouselWrap.style.transform || this.carouselWrap.style["-webkit-transform"] || this.carouselWrap.style["-ms-transform"]; x = x.substring(12); x = x.match(/(S*)px/)[1]; return Number(x); }
新版的方法功能與老版完全一直,只是實現(xiàn)所用到的方法不一樣了。接下來我們需要一個transition值的設(shè)置方法,通過這個動畫屬性,連requestAnimationFrame的相關(guān)操作也不需要了:
setTransition: function(elem, value) { elem.style.transition = value + "ms"; }
有了這三個方法,接下來就可以重寫moveCarousel()、resetCarousel()和resetMoveCarousel()方法了:
moveCarousel: function(target) { this.isCarouselAnimate = true; this.setTransition(this.carouselWrap, this.carouselOptions.carouselDuration); this.setTransform(this.carouselWrap, target); this.resetCarousel(target); }, resetCarousel: function(target) { var _this = this; window.setTimeout(function() { // 重置箭頭或者自動輪播后的狀態(tài) _this.resetMoveCarousel(target); _this.isCarouselAnimate = false; }, _this.carouselOptions.carouselDuration); }, resetMoveCarousel: function(target) { this.setTransition(this.carouselWrap, 0); // 不符合位移條件,把當前l(fā)eft值置為目標值 this.setTransform(this.carouselWrap, target); //如當前在輔助圖上,就歸位到真的圖上 if (target > -this.carouselWidth) { this.setTransform(this.carouselWrap, -this.carouselCount * this.carouselWidth); } if (target < -this.carouselWidth * this.carouselCount) { this.setTransform(this.carouselWrap, -this.carouselWidth); } }
之所以在每次setTransform()改變位置之前都要重新設(shè)置transition的值,是因為transition會使每次位置的改變都帶上動畫效果,而我們在代碼中做的過渡操作又不希望用戶直接看到,因此,重設(shè)它的值后才能和以前的實現(xiàn)效果保持一致。
添加touch事件在移動端上我們通常習慣用手指直接觸摸屏幕來操作應(yīng)用,所以Web端圓點和箭頭的交互方式這時候就顯得不那么合適了,取而代之的,我們可以改寫成觸摸的交互方式,也就是touch事件實現(xiàn)的效果:
bindCarousel: function() { var _this = this; // 鼠標移入移出事件 addEvent(this.carousel, "touchstart", function(e) { if (!_this.isCarouselAnimate) { clearInterval(_this.carouselIntervalr); _this.carouselTouch.startX = _this.getTransform(); _this.carouselTouch.start = e.changedTouches[e.changedTouches.length - 1].clientX; } }); addEvent(this.carousel, "touchmove", function(e) { if (!_this.isCarouselAnimate && _this.carouselTouch.start != -1) { clearInterval(_this.carouselIntervalr); _this.carouselTouch.move = e.changedTouches[e.changedTouches.length - 1].clientX - _this.carouselTouch.start; _this.setTransform(_this.carouselWrap, _this.carouselTouch.move + _this.carouselTouch.startX); } }); addEvent(this.carousel, "touchend", function(e) { if (!_this.isCarouselAnimate && _this.carouselTouch.start != -1) { clearInterval(_this.carouselIntervalr); _this.setTransform(_this.carouselWrap, _this.carouselTouch.move + _this.carouselTouch.startX); var x = _this.getTransform(); x += _this.carouselTouch.move > 0 ? _this.carouselWidth * _this.carouselTouch.offset : _this.carouselWidth * -_this.carouselTouch.offset; _this.carouselIndex = Math.round(x / _this.carouselWidth) * -1; _this.moveCarousel( _this.carouselIndex * -_this.carouselWidth ); if (_this.carouselIndex > _this.carouselCount) { _this.carouselIndex = 1; } if (_this.carouselIndex < 1) { _this.carouselIndex = _this.carouselCount; } _this.playCarousel(); } }); }
簡單來說,我們把觸摸事件分為三個過程——開始、移動、結(jié)束,然后在這三個過程中,就可以分別實現(xiàn)對應(yīng)的邏輯與操作了:
touchmove獲取觸摸的起始點
touchmove計算觸摸后的偏移量
判斷偏移的方向,改變圖片位置
通過這套邏輯,我們模擬的移動設(shè)備的觸摸效果就能成功實現(xiàn)了:
文章本身只是對項目整體思路和重點部分的講解,一些細節(jié)點也不可能面面俱到,還請大家對照源碼自行理解學(xué)習~
最后我想說的是,類似輪播這樣的優(yōu)秀插件其實已經(jīng)有很多了,但這并不妨礙我們寫一個自己的版本。因為只有自己寫一遍,并在腦中走一遍自己的思維過程,然后在學(xué)習一些優(yōu)秀的源碼及實現(xiàn)時才不至于懵圈。
到止為止,我們第二個輪子的開發(fā)也算順利完成了,所有源碼已同步更新到github,如果大家發(fā)現(xiàn)有bug或其他問題,可以回復(fù)在項目的issue中,咱們后會有期!(挖坑不填,逃。。
更新(2018-8-14)已更新使用Webpack打包后的ES6版本,支持ES6模塊化引入方式。
import { Carousel } from "csdwheels" import { CarouselMobile } from "csdwheels"
具體的使用方法請參考README
To be continued...
參考內(nèi)容JS 跑馬燈(輪播圖)效果中,最后一張滾回到第一張,怎樣優(yōu)化“視覺倒退”?
用原生的javascript 實現(xiàn)一個無限滾動的輪播圖
原生JavaScript實現(xiàn)輪播圖
原生js實現(xiàn)輪播圖
手把手原生js簡單輪播圖
vue-swiper
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/96851.html
摘要:使用構(gòu)造函數(shù)那么有沒有一種辦法,可以不寫函數(shù)名,直接聲明一個函數(shù)并自動調(diào)用它呢答案肯定的,那就是使用自執(zhí)行函數(shù)。 日常工作中經(jīng)常會發(fā)現(xiàn)有大量業(yè)務(wù)邏輯是重復(fù)的,而用別人的插件也不能完美解決一些定制化的需求,所以我決定把一些常用的組件抽離、封裝出來,形成一套自己的插件庫。同時,我將用這個教程系列記錄下每一個插件的開發(fā)過程,手把手教你如何一步一步去造出一套實用性、可復(fù)用性高的輪子。 So, ...
摘要:很久沒上掘金發(fā)現(xiàn)草稿箱里存了好幾篇沒發(fā)的文章最近梳理下發(fā)出來往期面試官系列如何實現(xiàn)深克隆面試官系列的實現(xiàn)面試官系列前端路由的實現(xiàn)面試官系列基于數(shù)據(jù)劫持的雙向綁定優(yōu)勢所在面試官系列你為什么使用前端框架前言設(shè)計前端組件是最能考驗開發(fā)者基本功的測 很久沒上掘金,發(fā)現(xiàn)草稿箱里存了好幾篇沒發(fā)的文章,最近梳理下發(fā)出來 往期 面試官系列(1): 如何實現(xiàn)深克隆 面試官系列(2): Event Bus...
摘要:模塊化是隨著前端技術(shù)的發(fā)展,前端代碼爆炸式增長后,工程化所采取的必然措施。目前模塊化的思想分為和。特別指出,事件不等同于異步,回調(diào)也不等同于異步。將會討論安全的類型檢測惰性載入函數(shù)凍結(jié)對象定時器等話題。 Vue.js 前后端同構(gòu)方案之準備篇——代碼優(yōu)化 目前 Vue.js 的火爆不亞于當初的 React,本人對寫代碼有潔癖,代碼也是藝術(shù)。此篇是準備篇,工欲善其事,必先利其器。我們先在代...
摘要:前言這個輪子已經(jīng)有很多人造過了,為了不重復(fù)造輪子,我將本項目以三階段實現(xiàn)大家可以在中的查看純前端后端前端后端前端希望能給大家一個漸進學(xué)習的經(jīng)驗。 前言 Vue+Socket.io這個輪子已經(jīng)有很多人造過了,為了不重復(fù)造輪子,我將本項目以三階段實現(xiàn)(大家可以在github中的Releases查看): 純前端(Vuex) 后端+前端(JavaScript) 后端+前端(TypeScrip...
摘要:更新日志更新完成靜態(tài)頁面原型修復(fù)使用的正確姿勢更新添加靜態(tài)頁面更新添加使用方法請戳我主要作用就是在你開發(fā)環(huán)節(jié)在后端同學(xué)還未開發(fā)完成的情況下,提供一個。 底下評論說是標題黨,或者是光扔個github地址上來的同學(xué)我就不說什么了。你們有看看倉庫的提交記錄么?我還沒有吃撐到開個倉庫去騙star.我的出發(fā)點就是每天更新一部分代碼,教大家用我所提到的技術(shù)棧搭建一個blog,我的出發(fā)點就是這么簡單...
閱讀 1964·2023-04-25 15:45
閱讀 1197·2021-09-29 09:34
閱讀 2497·2021-09-03 10:30
閱讀 2000·2019-08-30 15:56
閱讀 1456·2019-08-29 15:31
閱讀 1267·2019-08-29 15:29
閱讀 3196·2019-08-29 11:24
閱讀 3047·2019-08-26 13:45