国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

深入解析js輪播插件核心代碼的實現過程

2019-11-19 16:49:37
字體:
來源:轉載
供稿:網友

輪播效果在網頁中用的很多,swiper是其中最有代表性的作品,它支持水平和豎直滑動,還有反彈效果,兼容移動端和pc端。當然代碼量也是相當大的,單是js就有5300行(3.4.0的未縮版本),若不考慮代碼利用率和加載速度直接就用了,在移動端比較慎重,比如京東(m.jd.com)的輪播就沒有用它,而是自己實現了類似的功能,代碼量很少的樣子(格式化之后看起來二三百行左右的樣子)。那么這個功能如果自己來實現,要怎么做呢?

準備工作

1. 準備幾張圖片(我這里放了四張)

2. 搭建目錄結構(html+css+images+js)

3. 編寫代碼,實現初始狀態的顯示效果

<!DOCTYPE html><html><head>  <meta charset="utf-8">  <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />  <title>輪播</title>  <link rel="stylesheet" type="text/css" href="slider.css" rel="external nofollow" >  <script src="slider.js"></script></head><body>  <div id="slider" class="slider-wrapper">    <ul class="slider-items">      <li class="slider-item"><img src="images/pic21.gif" alt="1"></li>      <li class="slider-item"><img src="images/pic22.gif" alt="2"></li>      <li class="slider-item"><img src="images/pic23.gif" alt="3"></li>      <li class="slider-item"><img src="images/pic24.gif" alt="4"></li>    </ul>  </div>  <script>    Slider('#slider',{});  </script></body></html>

寫幾行樣式,先讓頁面有一種滾動前的初始畫面。

body {  padding: 0;  min-width: 300px;  max-width: 640px;  margin: 0 auto;} ul {  list-style: none;} ul,li {  margin: 0;  padding: 0;} .slider-wrapper {  position: relative;  width: 100%;  height: 220px;  overflow: hidden;  -webkit-user-select: none;  -ms-user-select: none;  user-select: none;} .slider-items {  position: relative;  height: 100%;} .slider-item {  float: left;  text-align: center;  cursor: pointer;} .slider-item > img {  width: 100%;  pointer-events: none;} .slider-pagination {  position: absolute;  bottom: 10px;  left: 0;  width: 100%;  text-align: center;  -webkit-transition-duration: .3s;  -moz-transition-duration: .3s;  -o-transition-duration: .3s;  transition-duration: .3s;} .slider-bullet {  width: 8px;  height: 8px;  margin: 0 5px;  display: inline-block;  border-radius: 100%;  background-color: black;  opacity: .2;  cursor: pointer;} .slider-bullet-active {  opacity: 1;  background-color: #007aff;} .slider-button {  position: absolute;  top: 50%;  width: 50px;  height: 50px;  text-align: center;  line-height: 50px;  margin-top: -25px;  z-index: 10;  font-size: 4rem;  color: gray;  -webkit-user-select:none;  user-select:none;} .next {  right: 0px;} .prev {  left: 0px;}

做好這個靜態頁,可以幫助我們在開發過程中預覽效果,方便查找問題,欣賞制作過程帶來的樂趣。

在線預覽

搭建程序骨架

接下來就是寫js了,先搭一個程序的架子,我這里仿一下jQ的無new式設計(其實是自己在內部自動實現new的過程)

;( function( global, factory ) {  "use strict";  if ( typeof module === "object" && typeof module.exports === "object" ) {    module.exports = global.document ?      factory( global, true ) :      function( w ) {        if ( !w.document ) {          throw new Error( "Slider requires a window with a document" );        }        return factory( w );      };  } else {    factory( global );  } // Pass this if window is not defined yet} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ){  "use strict";     function Slider( selector, options ) {    return new Slider.init( selector, options );  }  Slider.init=function(selector, params){        next:function(){},<br>       prev:function(){},<br>       move:function(){}    }<br>     Slider.init.prototype = Slider.prototype = {<br>      <br>    };    return Slider});

架子搭好之后,先停下來喝口水,思考一下,最終我們的這個插件要實現哪些功能。

    1. 點擊左、右箭頭可以控制滾動

    3. 可以向左、向右拖拽滑動

    4. 返彈效果

    5. 自動輪播效果

    6. 頁碼指示

這些功能該怎么實現?

先畫幾張草圖,在紙上模擬一下這個過程,搞明白之后,再用代碼來讓計算機執行。下圖中的藍框代表顯示器的寬度,紅框代表圖片所在容器的實際寬度。我只要移動紅框,那么在屏幕上看起來,就會有輪播滾動的效果。原理看起來是不是很簡單?本著先易后難,循序漸進的作戰方針,先不考慮循環滾動。假設屏幕上顯示的是圖片1,此時只要把紅框往左移一個屏的寬度,那么就會顯示2,再移一屏,就可以顯示3. 向右滾動正好相反。

移動可以用css3的transform:translate3d來做,也可以用js變化left/top來做,用translate3d來做的核心代碼如下:

function translate3d(element,x,y) {    x = x === undefined ? 0 : x;    y = y === undefined ? 0 : x;    element.style['-webkit-transform'] = 'translate3d(-'+x+'px,'+y+'px,0px)';    element.style['transform'] = 'translate3d(-'+x+'px,'+y+'px,0px)';  }     function transition(element,time){    element.style['-webkit-transition-duration'] = time+'ms';    element.style['transition-duration'] = time+'ms';  }

x控制左右移動,y控制豎直移動(本例中暫不考慮),z固定(0px). 當x移動的距離是正數的時候,向左滾動--prev,為負數的時候向右滾動--next

fn.next = function(){    var activeIndex = ++this.activeIndex;    translate3d(this.wrap,activeIndex*this.slideWidth);    transition(this.wrap,this.params.speed);  } fn.prev = function(){    var activeIndex = --this.activeIndex;    translate3d(this.wrap,activeIndex*this.slideWidth);    transition(this.wrap,this.params.speed);  }

由于圖片數量是有限的,不能一直滾動,如果到頭了,需要做一些處理。因此需要判斷activeIndex(當前顯示的圖片索引)的值。如果到了最右邊就不能再允許滾動了。

var activeIndex = this.lastIndex--;<br>if(activeIndex > this.lastIndex){   return;}

同理,到了最左邊,也不能繼續往左滾動了(聽起來是一句非常正確的廢話)

var activeIndex = --this.activeIndex;if(activeIndex < this.firstIndex){  return;}

現在要考慮自動滾動的情況,如果是自動輪播的情況,那就不能直接return; 要么當到達最右邊的時候,activeIndex賦成firstIndex,當達到最左邊的時候,activeIndex賦成lastIndex; 這樣做的實際效果看起來就是像蕩秋千一樣,這顯然不是我們想要的效果。要么跳到最前面重新開始,這做出來的實際效果一點也不連續。最后決定去看看swiper是怎么實現的。swiper的做法就是把第一張復制一份放到最后面,把最后一張復制一份插到最前面。 如下圖所示:

第一張和第四張都被復制了一份。對應的代碼如下:

fn.createLoopItems = function(){    var lastItem = this.slides[this.lastIndex];    var firstItem = this.slides[this.firstIndex];    var prevItem = lastItem.cloneNode(true);    var nextItem = firstItem.cloneNode(true);    var sliderCount = this.sliderCount+2;    var slideWidth = this.slideWidth;    this.slideStack.push(this.firstIndex);    this.slideStack.unshift(this.lastIndex);    this.wrap.insertBefore(prevItem,firstItem);    this.wrap.appendChild(nextItem);    this.wrap.style.width = slideWidth * sliderCount + 'px';    translate3d(this.wrap,slideWidth);    this.activeIndex += 1;    this.sliderCount = sliderCount;    this.lastIndex += 2;  }

不得不承認這個做法很巧妙。隨便我們往哪個方向翻,都不至于出現空白期。當翻到最未尾(數字4)的時候,還可以再翻一頁,即第一張的復制品,雖然不是真的第一張,但是看起來就像是平滑的過渡到了第一張一樣。不過這是臨時的,我們需要在過渡完之后,立即回到真正的1的位置上去。因為我們實際上在未端只補了一張,翻完這一頁,如果不進一步處理,還是會到頭。這時,就是問題的關鍵了。當我們往右翻,從第四張翻到復制的第一張時,需要悄悄地,人不知,鬼不覺的把紅框的位置移到1的真身上來。同時把activeIndex也置為圖1的索引。那么問題又來了,怎么才能做到人不知鬼不覺的暗渡陳倉呢?其實很簡單,只要把移位動畫的時間改成0就可以了。關鍵代碼如下:

transition(this.wrap,0);

不過這一步要在從4變為1,剛停下來的時候,立即執行。做早了,就會感覺是直接從4跳到1,沒有動畫效果,做晚了,就會出現空白,并因為索引溢出而報錯。所以這里需要一個修復方法:

fn.fixedPrevLoop = function(){    var that = this;    setTimeout(function(){      that.fixedLoop(that.lastIndex-1)    },that.params.speed);  }

或者:

this.container.addEventListener('transitionend',function(){<br>   //監聽動畫結速之后再執行判斷是否要修復<br>},false);

做完這一步,就看起來有連續滾動的效果了,在這個基礎上實現自動輪播就好辦了,只要用定時器,每隔一段時間就執行一下自身就可以了。代碼如下:

fn.autoPlay = function(){    var that = this;    if(!this.params.autoplay){      return;    }    this.timeId = setTimeout(function(){      that.next();      //that.prev();      that.autoPlay();    },this.params.delay);  }

  我注釋掉了prev(), 默認都是向右自動輪播。如果要改成是往左輪播,只要在這里加一個一個配置選擇就好了。自動循環都做好了,在此基礎上點擊翻頁,也是很容易的事情了,給按鈕邦定一個click事件,如果是右邊的,就調用next()方法,反之則調用prev()方法。不過我這里沒有這樣做,因為考慮到后面我們還要做手勢(鼠標)拖動翻頁效果,我決定用事件代理來做。讓事件統統都冒泡到包裝容器上去處理,這也是提升性能的常用技巧之一。

fn.bindEvents = function(){    if(Device.desktop){      this.container.addEventListener('mousedown',this,false);      this.container.addEventListener('mousemove',this,false);      document.addEventListener('mouseup',this,false);    }else{      this.container.addEventListener('touchstart',this,false);      this.container.addEventListener('touchmove',this,false);      document.addEventListener('touchend',this,false);    }    this.container.addEventListener('transitionend',this,false);    this.container.addEventListener('click',this,false);  }

  為什么這里的addEventListener為什么是邦定this而不是一個函數呢?簡單說,是因為上下文中有一個handleEvent方法,可以被監聽函數自動捕獲到,這個函數名是固定的,不明白的可以自行搜索這個函數名。

fn.handleEvent = function(e){    var type = e.type;<br>  //注意這里邊的this<br>}

這樣做的好處是可以維持事件邦定的那個函數的上下文。簡單說就是不用操心this的指向會變。

做完這一步,就可以做拖動翻頁了。在pc上用鼠標,在手機上用手指,處理的方式都是一樣的,監聽按下,移動,釋放這三個事件。按下的時候,記住初始坐標,移動的時候和這個坐標進行對比,計算出移動的距離,然后更新到移動的對象上(紅框)。這里邊有幾個地方需要注意:

1. 如果移動的時候,不是直線,x坐標和y坐標都有改變,是判斷成水平拖動還是垂直拖動?

2. 在pc上,如何判斷是拖動,拖出屏幕外了怎么處理?

3. 反彈怎么做?

對于第1點,可以比較兩個方向距離的大小,誰大聽誰的。如果指向了是水平滾動,那么可以直接忽略豎直方向的變化。

對于第2點,可以把監聽mouseup放到document上去,最好加一個移動的距離大小判斷,如果超過容器的大小,就當作是釋放了,該反彈的反彈,該滑頁的滑頁。

對于第3點,在拖動釋放的時候,判斷移動距離,比如拖動的距離小于屏寬的1/3,就反方向translate相應的距離回去,甚至都不用關心這個距離,反正這時的activeIndex沒有更新的,直接回到這個activeIndex對應的頁就算是反彈了。代碼就是這樣:

fn.stop = function(){    this.axis.x = 0;    translate3d(this.wrap,this.slideWidth*this.activeIndex);    transition(this.wrap,this.params.speed);}

接下來就是做頁碼指示器了,這個簡單,翻頁成功之后就更新一下對應的小點就是了。由于我們人為的插了兩個頁面進去,索引數和頁碼數就對應不起來了,實際參與滾動的圖片有6張,但是只能顯示4個點。我做的時候走了一些彎路,現在總結起來無非就是兩點:

1. 給小

主站蜘蛛池模板: 栾城县| 泊头市| 罗定市| 鹤庆县| 剑阁县| 冷水江市| 屏边| 温泉县| 苏尼特右旗| 英吉沙县| 崇仁县| 上饶县| 龙游县| 黄石市| 云浮市| 鸡东县| 任丘市| 黄石市| 鸡泽县| 达孜县| 德化县| 石林| 靖江市| 桓仁| 辉县市| 沐川县| 江达县| 保山市| 平阴县| 淄博市| 萍乡市| 通河县| 霍州市| 呼和浩特市| 松滋市| 商南县| 集贤县| 安溪县| 商河县| 全南县| 平远县|