如何用H5實現(xiàn)一個觸屏版的輪播器?

初入前端叶雹,分享一下手機上觸屏版輪播器的實現(xiàn)過程财饥,大致功能如下:

  1. 支持循環(huán)滑動
  2. 寬度可任意設置,不需要與屏幕等寬
  3. 頁面可縱向滾動
  4. 可設置回調(diào)監(jiān)聽元素的切換
  5. 純js折晦,不借助任何第三方庫

原理

  1. 假設子元素.itemwidth為375px钥星,使用絕對定位將所有子元素放在父元素內(nèi)
  2. 將父元素.carouselwidth設置為375px,與子元素.item寬度相同
  3. 為父元素.carousel添加觸摸事件:touchstart, touchmove, touchend
  4. 手指按下時满着,保存初始位置(clientX
  5. 手指滑動時谦炒,通過滑動距離判斷滑動的方向:
  6. 手指向左滑動,則同時移動當前元素和當前元素右邊的元素
  7. 手指向右滑動风喇,則同時移動當前元素和當前元素左邊的元素
  8. 手指抬起時宁改,通過滑動距離判斷是否切換到下一頁
  9. 移動距離未超過子元素寬度的50%,將當前頁面回滾到初始位置魂莫,不切換當前元素还蹲。
  10. 移動距離超過子元素寬度的50%,切換當前元素為下一個元素。
  11. 將當前元素的transform屬性設置為translate3d(0px, 0px, 0px)谜喊,并將z-index屬性+1
  12. 將下一個子元素的transform屬性設置為translate3d(375px, 0px, 0px)潭兽,并將z-index屬性+1
  13. 將上一個子元素的transform屬性設置為translate3d(-375px, 0px, 0px),并將z-index屬性+1
  14. 將其他所有子元素的z-index屬性設置為默認值
  15. 第一個子元素的上一個元素是最后一個元素斗遏,最后一個元素的下一個元素是第一個元素山卦,該步驟通過循環(huán)鏈表實現(xiàn)。

移動時設置的是子元素.item的transform屬性诵次,而不是父元素.carousel

實現(xiàn)步驟

html&css

//html
<div class="carousel" ontouchstart="" >  
  <div class="item" style="background: #3b76c0" >    
    <h3 >item-1</h3>  
  </div>  
  <div class="item" style="background: #58c03b;">    
    <h3>item-2</h3>  
  </div>  
  <div class="item" style="background: #c03b25;">    
    <h3>item-3</h3>
  </div> 
  <div class="item" style="background: #e0a718;">  
    <h3>item-4</h3>  
  </div>  
  <div class="item" style="background: #c03eac;">    
    <h3>item-5</h3>  
  </div>
</div>

//css
.carousel{
  height: 50%;
  position: relative;
  overflow: hidden;
}

.item {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
}

js

設置初始狀態(tài)

首先實現(xiàn)一個雙向鏈表账蓉,用于維護輪播組件中的元素。

function Node(data) {
    this.data = data;
    this.prev = null;
    this.next = null;
    this.index = -1;
}

//雙向循環(huán)列表
function LinkList() {
    var _nodes = [];
    this.head = null;
    this.last = null;

    if (typeof this.append !== "function") {
        LinkList.prototype.append = function (node) {
            if (this.head == null) {
                this.head = node;
                this.last = this.head;
            }
            else {
                this.head.prev = node;
                this.last.next = node;

                node.prev = this.last;
                node.next = this.head;

                this.last = node;
            }

            node.index = _nodes.length; //務必在push前設置node.index
            _nodes.push(node);
        }
    }
}

有了鏈表之后逾一,創(chuàng)建一個鏈表實例铸本,將子元素添加進鏈表內(nèi),并設置一些初始狀態(tài)

var _container = document.querySelector("." + containerClass);
var _items = document.querySelectorAll("." + itemClass);

var list = loop ? new LinkList() : new SingleList();
for(var i = 0; i < _items.length; i++) {
  list.append(new Node(_items[i]));
}

var _prev = null;  //保存之前顯示的元素
var _current = list.head;  //保存當前顯示的元素嬉荆,默認為第一個元素

var _normalZIndex = _current.data.style.zIndex;  //未顯示元素的z-index值
var _activeZIndex = _normalZIndex + 1;  //當前顯示元素的z-index值

var _itemWidth = _current.data.offsetWidth; //子元素寬度

positionItems(); //初始化元素位置
zindexItems(_current, _activeZIndex); //將當前元素及其左右元素的z-index加1

綁定觸摸事件

touchstart事件

手指按下時归敬,保存初始位置

_container.addEventListener("touchstart", function(e) {
  // e.preventDefault();//取消此行代碼的注釋會在該元素內(nèi)阻止頁面縱向滾動
  var touch = e.touches[0];
  startX = touch.clientX;   //保存手指按下時的位置
  startY = touch.clientY;
  _container.style.webkitTransition = ""; //取消動畫效果
  startT = new Date().getTime();          //記錄手指按下的開始時間
  isMove = false;
  transitionItems(_prev, false);             //取消之前元素的過渡
  transitionItems(_current, false);          //取消當前元素的過渡
}, false);

touchmove事件

手指在屏幕上滑動酷含,頁面跟隨手指移動

_container.addEventListener("touchmove", function(e) {
    // e.preventDefault();//取消此行代碼的注釋會在該元素內(nèi)阻止頁面縱向滾動
    var touch = e.touches[0];
    var deltaX = touch.clientX - startX;  //計算手指在X方向滑動的距離
    var deltaY = touch.clientY - startY;  //計算手指在Y方向滑動的距離
    //如果X方向上的位移大于Y方向鄙早,則認為是左右滑動
    if (Math.abs(deltaX) > Math.abs(deltaY)){
        translate = deltaX > _itemWidth ? _itemWidth : deltaX;
        translate = deltaX < -_itemWidth ? -_itemWidth : deltaX;

        //同時移動當前元素及其左右元素
        moveItems(translate); 

        isMove = true;
    }
}, false);

touchend事件

手指離開屏幕時,計算最終需要停留在哪一頁

_container.addEventListener("touchend",function(e) {
    // e.preventDefault();//取消此行代碼的注釋會在該元素內(nèi)阻止頁面縱向滾動

    //是否會滾
    var isRollback = false;

    //計算手指在屏幕上停留的時間
    var deltaT = new Date().getTime() - startT;
    if (isMove) { //發(fā)生了左右滑動
        //如果停留時間小于300ms,則認為是快速滑動椅亚,無論滑動距離是多少限番,都停留到下一頁
        if(deltaT < 300){
            translate = translate < 0 ? -_itemWidth : _itemWidth;
        }else {
            //如果滑動距離小于屏幕的50%,則退回到上一頁
            if (Math.abs(translate) / _itemWidth < 0.5){
                isRollback = true;
            }else{
                //如果滑動距離大于屏幕的50%呀舔,則滑動到下一頁
                translate = translate < 0 ? -_itemWidth : _itemWidth;
            }
        }

        moveTo(translate, isRollback);
    }
}, false);

Carousel庫

為了方便使用弥虐,我將整個實現(xiàn)過程封裝成了一個庫,并添加了prev()媚赖, next()方法霜瘪,使用非常簡單:

<script src="lib/carousel.js"></script>

CreateCarousel("carousel", "item", true)
  .bindTouchEvent()
  .setItemChangedHandler(onPageChanged);

//參數(shù)"carousel"為容器的類名
//參數(shù)"item"為子元素的類名
//第三個參數(shù)設置是否需要循環(huán)播放,true為循環(huán)播放

該庫可到github下載

參考

H5單頁面手勢滑屏切換原理

good night惧磺!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颖对,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子磨隘,更是在濱河造成了極大的恐慌缤底,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件番捂,死亡現(xiàn)場離奇詭異个唧,居然都是意外死亡,警方通過查閱死者的電腦和手機设预,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門徙歼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事魄梯『艄桑” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵画恰,是天一觀的道長彭谁。 經(jīng)常有香客問我,道長允扇,這世上最難降的妖魔是什么缠局? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮考润,結(jié)果婚禮上狭园,老公的妹妹穿的比我還像新娘。我一直安慰自己糊治,他們只是感情好唱矛,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著井辜,像睡著了一般绎谦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粥脚,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天窃肠,我揣著相機與錄音,去河邊找鬼刷允。 笑死冤留,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的树灶。 我是一名探鬼主播纤怒,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼天通!你這毒婦竟也來了泊窘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤土砂,失蹤者是張志新(化名)和其女友劉穎州既,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萝映,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡吴叶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了序臂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蚌卤。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡实束,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逊彭,到底是詐尸還是另有隱情咸灿,我是刑警寧澤,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布侮叮,位于F島的核電站避矢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏囊榜。R本人自食惡果不足惜审胸,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卸勺。 院中可真熱鬧砂沛,春花似錦、人聲如沸曙求。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽悟狱。三九已至静浴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芽淡,已是汗流浹背马绝。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工豆赏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留挣菲,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓掷邦,卻偏偏與公主長得像白胀,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子抚岗,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • Documentation Supported OS & SDK Versions 支持的OS & SDK版本 S...
    c5550ea746f8閱讀 4,322評論 0 2
  • 文章轉(zhuǎn)自原文鏈接 背景 隨著近幾年移動營銷頁的火爆或杠,催生了一個中國式的名詞「H5」。而 H5 最常見的形態(tài)就是類似...
    Zakerberg閱讀 1,133評論 0 4
  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案宣蔚? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標簽默認的外補...
    _Yfling閱讀 13,728評論 1 92
  • typedef NS_ENUM(NSInteger, iCarouselType){ iCarouselTypeL...
    黎明s閱讀 1,627評論 0 1
  • 《奇葩說》第4季第11集的辯題是:婚禮真的有必要嗎胚委? 很多女生都幻想過自己婚禮的場景挟鸠,穿著婚紗,像一個移動的華麗城...
    樂意樂讀閱讀 1,885評論 0 2