欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

移動(dòng)端效果之Swiper詳解

寫在前面

成都創(chuàng)新互聯(lián)自2013年創(chuàng)立以來,先為汾西等服務(wù)建站,汾西等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為汾西企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。

最近在做移動(dòng)端方面運(yùn)用到了餓了么的vue前端組件庫,因?yàn)椴幌雴渭冇媒M件而使用它,故想深入了解一下實(shí)現(xiàn)原理。后續(xù)將會(huì)繼續(xù)研究一下其他的組件實(shí)現(xiàn)原理,有興趣的可以關(guān)注下。

移動(dòng)端效果之Swiper詳解

代碼在這里:戳我

1. 說明

父容器overflow:hidden;,子頁面transform:translateX(-100%);width:100%;

2. 核心解析

2.1 頁面初始化

由于所有頁面都在手機(jī)屏幕左側(cè)一個(gè)屏幕寬度的位置,因此最開始的情況是頁面中看不到任何一個(gè)子頁面,所以第一步應(yīng)該設(shè)置應(yīng)該顯示的子頁面,默認(rèn)情況下defaultIndex:0

function reInitPages() {
  // 得出頁面是否能夠被滑動(dòng)
  // 1. 子頁面只有一個(gè)
  // 2. 用戶手動(dòng)設(shè)置不能滑動(dòng) noDragWhenSingle = true
  noDrag = children.length === 1 && noDragWhenSingle;

  var aPages = [];
  var intDefaultIndex = Math.floor(defaultIndex);
  var defaultIndex = (intDefaultIndex >= 0 && intDefaultIndex < children.length) 
    ? intDefaultIndex : 0;
  
  // 得到當(dāng)前被激活的子頁面索引
  index = defaultIndex;

  children.forEach(function(child, index) {
    aPages.push(child);
    // 所有頁面移除激活class
    child.classList.remove('is-active');

    if (index === defaultIndex) {
      // 給激活的子頁面加上激活class
      child.classList.add('is-active');
    }
  });

  pages = aPages;
}

2.2 容器滑動(dòng)開始(onTouchStart)

在低版本的android手機(jī)上,設(shè)置event.preventDefault()會(huì)起到一定的性能提升作用,使得滑動(dòng)起來不是那么卡。

前置工作:

  • 如果用戶設(shè)置了 prevent:true, 滑動(dòng)時(shí)阻止默認(rèn)行為
  • 如果用戶設(shè)置了stopPropagation:true, 滑動(dòng)時(shí)阻止事件向上傳播
  • 如果動(dòng)畫尚未結(jié)束,阻止滑動(dòng)
  • 設(shè)置dragging:true,滑動(dòng)開始
  • 設(shè)置用戶滾動(dòng)為false

滑動(dòng)開始:

使用一個(gè)全局對(duì)象記錄信息,這些信息包括:

dragState = {
  startTime      // 開始時(shí)間
  startLeft      // 開始的X坐標(biāo)
  startTop      // 開始的Y坐標(biāo)(相對(duì)于整個(gè)頁面viewport pageY)
  startTopAbsolute  // 絕對(duì)Y坐標(biāo)(相對(duì)于文檔頂部 clientY)
  pageWidth      // 一個(gè)頁面寬度
  pageHeight     // 一個(gè)頁面的高度
  prevPage      // 上一個(gè)頁面
  dragPage      // 當(dāng)前頁面
  nextPage      // 下一個(gè)頁面
};

2.3 容器滑動(dòng)(onTouchMove)

套用全局dragState,記錄新的信息

dragState = {
  currentLeft     // 開始的X坐標(biāo)
  currentTop     // 開始的Y坐標(biāo)(相對(duì)于整個(gè)頁面viewport pageY)
  currentTopAbsolute // 絕對(duì)Y坐標(biāo)(相對(duì)于文檔頂部 clientY)
};

那么我們就可以通過開始和滑動(dòng)中的信息來計(jì)算出一些東西:

滑動(dòng)的水平位移(offsetLeft = currentLeft - startLeft)

滑動(dòng)的垂直位移(offsetTop = currentTopAbsolute - startTopAbsolute)

是否是用戶的自然滾動(dòng),這里的自然滾動(dòng)說的是用戶并不是想滑動(dòng)swiper,而是想滑動(dòng)頁面

// 條件
// distanceX = Math.abs(offsetLeft);
// distanceY = Math.abs(offsetTop);
distanceX < 5 || ( distanceY >= 5 && distanceY >= 1.73 * distanceX )

判斷是左移還是右移(offsetLeft < 0 左移,反之,右移)

重置位移

// 如果存在上一個(gè)頁面并且是左移
if (dragState.prevPage && towards === 'prev') {
  // 重置上一個(gè)頁面的水平位移為 offsetLeft - dragState.pageWidth
  // 由于 offsetLeft 一直在變化,并且 >0
  // 那么也就是說 offsetLeft - dragState.pageWidth 的值一直在變大,但是仍未負(fù)數(shù)
  // 這就是為什么當(dāng)連續(xù)屬性存在的時(shí)候左滑會(huì)看到上一個(gè)頁面會(huì)跟著滑動(dòng)的原因
  // 這里的 translate 方法其實(shí)很簡單,在滑動(dòng)的時(shí)候去除了動(dòng)畫效果`transition`,單純改變位移
  // 而在滑動(dòng)結(jié)束的時(shí)候,加上`transition`,使得滑動(dòng)到最后釋放的過渡更加自然
  translate(dragState.prevPage, offsetLeft - dragState.pageWidth);
}

// 當(dāng)前頁面跟著滑動(dòng)
translate(dragState.dragPage, offsetLeft);

// 后一個(gè)頁面同理
if (dragState.nextPage && towards === 'next') {
  translate(dragState.nextPage, offsetLeft + dragState.pageWidth);
}

2.4 滑動(dòng)結(jié)束(onTouchEnd)

前置工作:

在滑動(dòng)中,我們是可以實(shí)時(shí)地來判斷到底是不是用戶的自然滾動(dòng)userScrolling,如果是用戶自然滾動(dòng),那么swiper的滑動(dòng)信息就不算數(shù),因此要做一些清除操作:

dragging = false;
dragState = {};

當(dāng)然如果userScrolling:false,那么就是滑動(dòng)子頁面,執(zhí)行doOnTouchEnd方法

判斷是否是tap事件

// 時(shí)間小于300ms,click事件延遲300ms觸發(fā)
// 水平位移和垂直位移棟小于5像素
if (dragDuration < 300) {
  var fireTap = Math.abs(offsetLeft) < 5 && Math.abs(offsetTop < 5);
  if (isNaN(offsetLeft) || isNaN(offsetTop)) {
    fireTap = true;
  }
  if (fireTap) {
    console.log('tap');
  }
}

判斷方向

// 如果事件間隔小于300ms但是滑出屏幕,直接返回
if (dragDuration < 300 && dragState.currentLeft === undefined) return;

// 如果事件間隔小于300ms 或者 滑動(dòng)位移超過屏幕寬度 1/2, 根據(jù)位移判斷方向
if (dragDuration < 300 || Math.abs(offsetLeft) > pageWidth / 2) {
  towards = offsetLeft < 0 ? 'next' : 'prev';
}

// 如果非連續(xù),當(dāng)處于第一頁,不會(huì)出現(xiàn)上一頁,當(dāng)處于最后一頁,不會(huì)出現(xiàn)下一頁
if (!continuous) {
  if ((index === 0 && towards === 'prev') 
    || (index === pageCount - 1 && towards === 'next')) {
    towards = null;
  }
}

// 子頁面數(shù)量小于2時(shí),不執(zhí)行滑動(dòng)動(dòng)畫
if (children.length < 2) {
  towards = null;
}

執(zhí)行動(dòng)畫

// 當(dāng)沒有options的時(shí)候,為自然滑動(dòng),也就是定時(shí)器滑動(dòng)
function doAnimate(towards, options) {
  if (children.length === 0) return;
  if (!options && children.length < 2) return;

  var prevPage, nextPage, currentPage, pageWidth, offsetLeft;
  var pageCount = pages.length;

  // 定時(shí)器滑動(dòng)
  if (!options) {
    pageWidth = element.clientWidth;
    currentPage = pages[index];
    prevPage = pages[index - 1];
    nextPage = pages[index + 1];
    if (continuous && pages.length > 1) {
      if (!prevPage) {
        prevPage = pages[pages.length - 1];
      }

      if (!nextPage) {
        nextPage = pages[0];
      }
    }

    // 計(jì)算上一頁與下一頁之后
    // 重置位移
    // 參看doOnTouchMove
    // 其實(shí)這里的options 傳與不傳也就是獲取上一頁信息與下一頁信息
    if (prevPage) {
      prevPage.style.display = 'block';
      translate(prevPage, -pageWidth);
    }

    if (nextPage) {
      nextPage.style.display = 'block';
      translate(nextPage, pageWidth);
    }
  } else {
    prevPage = options.prevPage;
    currentPage = options.currentPage;
    nextPage = options.nextPage;
    pageWidth = options.pageWidth;
    offsetLeft = options.offsetLeft;
  }

  var newIndex;
  var oldPage = children[index];

  // 得到滑動(dòng)之后的新的索引
  if (towards === 'prev') {
    if (index > 0) {
      newIndex = index - 1;
    }
    if (continuous && index === 0) {
      newIndex = pageCount - 1;
    }
  } else if (towards === 'next') {
    if (index < pageCount - 1) {
      newIndex = index + 1;
    }
    if (continuous && index === pageCount - 1) {
      newIndex = 0;
    }
  }

  // 動(dòng)畫完成之后的回調(diào)
  var callback = function() {
    // 得到滑動(dòng)之后的激活頁面,添加激活class
    // 重新賦值索引
    if (newIndex !== undefined) {
      var newPage = children[newIndex];
      oldPage.classList.remove('is-active');
      newPage.classList.add('is-active');
      index = newIndex
    }

    if (isDone) {
      end();
    }

    if (prevPage) {
      prevPage.style.display = '';
    }

    if (nextPage) {
      nextPage.style.display = '';
    }
  }

  setTimeout(function() {
    // 向后滑動(dòng)
    if (towards === 'next') {
      isDone = true;
      before(currentPage);
      // 當(dāng)前頁執(zhí)行動(dòng)畫,完成后執(zhí)行callback
      translate(currentPage, -pageWidth, speed, callback);
      if (nextPage) {
        // 下一面移動(dòng)視野中
        translate(nextPage, 0, speed)
      }
    } else if (towards === 'prev') {
      isDone = true;
      before(currentPage);
      translate(currentPage, pageWidth, speed, callback);
      if (prevPage) {
        translate(prevPage, 0, speed);
      }
    } else {
     // 如果既不是左滑也不是右滑
     isDone = true;
     // 當(dāng)前頁面依舊處于視野中
     // 上一頁和下一頁滑出
     translate(currentPage, 0, speed, callback);
     if (typeof offsetLeft !== 'undefined') {
       if (prevPage && offsetLeft > 0) {
          translate(prevPage, pageWidth * -1, speed);
       }
       if (nextPage && offsetLeft < 0) {
          translate(nextPage, pageWidth, speed);
       }
     } else {
      if (prevPage) {
       translate(prevPage, pageWidth * -1, speed);
      }

      if (nextPage) {
       translate(nextPage, pageWidth, speed);
      }
     }
    }
  }, 10);
}
​

后置工作:

清除一次滑動(dòng)周期中保存的狀態(tài)信息

dragging = false;
dragState = {};

總結(jié)

整體來說實(shí)現(xiàn)原理還是比較簡單的,滑動(dòng)開始記錄初始位置,計(jì)算上一頁與下一頁的應(yīng)該展示的頁面;滑動(dòng)中計(jì)算位移,計(jì)算上一頁下一頁的位移;滑動(dòng)結(jié)束根據(jù)位移結(jié)果執(zhí)行相應(yīng)的動(dòng)畫。

有一個(gè)細(xì)節(jié)就是,在滑動(dòng)中transition的效果置為空,是為了防止在滑動(dòng)中上一頁與下一頁因?yàn)檫^渡存在而位移得不自然,在滑動(dòng)結(jié)束后再給他們加上動(dòng)畫效果。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。

文章標(biāo)題:移動(dòng)端效果之Swiper詳解
網(wǎng)頁地址:http://aaarwkj.com/article38/isgisp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站收錄云服務(wù)器、網(wǎng)站建設(shè)網(wǎng)站設(shè)計(jì)、網(wǎng)站維護(hù)、域名注冊

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

h5響應(yīng)式網(wǎng)站建設(shè)
亚洲中文字幕精品视频乱码| 黄色录像黄色片黄色片| av国产一区二区在线| 黑人巨大精品欧美一区免| 美腿丝袜亚洲综合一区| 成人黄色av免费在线观看| 美女丝袜美腿魅惑男人| 欧美日韩国产另类久久| 久久99热最新地址获取| 真人国产一级美女免费视频| 婷婷av一区二区三区| 欧美亚洲另类日韩综合网| 国产精品视频一区二区三区网站| 日韩欧美乱码一区二区| 欧美αv一区二区三区| 久久精品亚洲精品国产| 国产男女免费视频观看| 国内丰满少妇嗷嗷叫在线播放| 黄色av免费播放网站| 综合激情网激情五月天 | 国产在线自拍一区二区| 日本午夜在线观看视频| 国产一区二区三区精品女同| 国产精品毛片视频一区二区| 国产精品免费视频一区二区三区| 亚洲精品欧美激情专区| 一区二区三区av夏目彩春| 久久精品欧美日韩视频| 亚洲人成免费在线观看| 国产精品视频不卡免费看| 日本在线免费高清观看| 国产成人性生交大片免费| 精品福利视频蜜臀91| 好吊视频在线免费观看| 色哟哟网站之中文字幕| 精品视频美女肉体亚洲| av在线男人社区日韩| 视频二区国产欧美日韩| 久久国产精品午夜视频| 亚洲成人有码在线观看| 久久99精品综合国产女同|