亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > JavaScript > 正文

詳解vue mint-ui源碼解析之loadmore組件

2019-11-19 15:11:38
字體:
來源:轉載
供稿:網友

本文介紹了vue mint-ui源碼解析之loadmore組件,分享給大家,具體如下:

接入

官方接入文檔mint-ui loadmore文檔

接入使用Example

html

<div id="app">  <mt-loadmore :top-method="loadTop" :bottom-method="loadBottom" :bottom-all-loaded="allLoaded" :max-distance="150"         @top-status-change="handleTopChange" ref="loadmore">    <div slot="top" class="mint-loadmore-top">      <span v-show="topStatus === 'pull'" :class="{ 'rotate': topStatus === 'drop' }">↓</span>      <span v-show="topStatus === 'loading'">Loading...</span>      <span v-show="topStatus === 'drop'">釋放更新</span>    </div>    <ul class="scroll-wrapper">      <li v-for="item in list" @click="itemClick(item)">{{ item }}</li>    </ul>  </mt-loadmore></div>

css

<link rel="stylesheet"  rel="external nofollow" >*{  margin: 0;  padding: 0;}html, body{  height: 100%;}#app{  height: 100%;  overflow: scroll;}.scroll-wrapper{  margin: 0;  padding: 0;  list-style: none;}.scroll-wrapper li{  line-height: 120px;  font-size: 60px;  text-align: center;}

js 

<!-- 先引入 Vue --><script src="https://unpkg.com/vue/dist/vue.js"></script><!-- 引入組件庫 --><script src="https://unpkg.com/mint-ui/lib/index.js"></script><script>  new Vue({    el: '#app',    data: {      list: [],      allLoaded: false,      topStatus: ''    },    created: function () {      var i =0, len=20;      for (; i< len; i++){        this.list.push('demo' + i);      }    },    methods: {      loadTop: function () { // 刷新數據的操作        var self = this;        setTimeout(function () {          self.list.splice(0, self.list.length);          var i =0, len=20;          for (; i< len; i++){            self.list.push('demo' + i);          }          self.$refs.loadmore.onTopLoaded();        }, 2000);      },      loadBottom: function () { // 加載更多數據的操作        //load data        //this.allLoaded = true;// 若數據已全部獲取完畢        var self = this;        setTimeout(function () {          var i =0; len = 10;          for (; i< len; i++){            self.list.push('dddd' + i);          }          self.$refs.loadmore.onBottomLoaded();        }, 2000);      },      handleTopChange: function (status) {        this.topStatus = status;      },      itemClick: function (data) {        console.log('item click, msg : ' + data);      }    }  });</script>

實現原理解析

布局原理

  • loadmore組件內部由三個slot組成,分別為name=top,name=bottom,default;
  • top用于展示下拉刷新不同狀態展示的內容,初始設置margin-top為-top的高度來將自己隱藏
  • bottom同top,用于展示上拉加載更多不同狀態展示的內容
  • default填充滾動詳細內容

實現原理

  • 主要是通過js的touch事件的監聽來實現
  • 在touchmove事件,如果是向下滑動并且滾動的dom的scrollTop為0,那么整個組件向下偏移(滑動的距離/比值)展示出top solt的內容
  • 在touchmove時間,如果是向上滑動并且滑動到了底部,再繼續滑動整個組件向上偏移(滑動的距離/比值)展示出bottom solt的內容

源碼解析

組件的template html

 <div class="mint-loadmore">  <div class="mint-loadmore-content" :class="{ 'is-dropped': topDropped || bottomDropped}" :style="{ 'transform': 'translate3d(0, ' + translate + 'px, 0)' }">   <slot name="top">    <div class="mint-loadmore-top" v-if="topMethod">     <spinner v-if="topStatus === 'loading'" class="mint-loadmore-spinner" :size="20" type="fading-circle"></spinner>     <span class="mint-loadmore-text">{{ topText }}</span>    </div>   </slot>   <slot></slot>   <slot name="bottom">    <div class="mint-loadmore-bottom" v-if="bottomMethod">     <spinner v-if="bottomStatus === 'loading'" class="mint-loadmore-spinner" :size="20" type="fading-circle"></spinner>     <span class="mint-loadmore-text">{{ bottomText }}</span>    </div>   </slot>  </div> </div>

關于 上面的spinner標簽,是一個組件,這里不做詳細介紹。top solt和bottom slot中的內容是展示的內容,可以通過外部自定義的方式傳入。

其實它的實現有一個很嚴重的弊端,就是寫死了top solt和bottom slot的高度為50px,而且js中的處理也是使用50px進行的邏輯處理。所以并滿足我們開發中自定義top slot 和bottom slot的需求。

js核心解析

  • props解析:關于props的解析,可以參考mint-ui的官方文檔
  • data解析
data() { return {  translate: 0, // 此變量決定當前組件上下移動,  scrollEventTarget: null, // 滾動的dom節點  containerFilled: false, // 當前滾動的內容是否填充完整,不完成會調用 loadmore的回調函數  topText: '', // 下拉刷新,顯示的文本  topDropped: false, // 記錄當前drop狀態,用給組件dom添加is-dropped class(添加回到原點的動畫)  bottomText: '', // 上拉加載更多 顯示的文本  bottomDropped: false, // 同topDropped  bottomReached: false, // 當前滾動是否滾動到了底部  direction: '', // touch-move過程中, 當前滑動的方向  startY: 0, // touch-start 起始的y的坐標值  startScrollTop: 0, // touch-start 起始的滾動dom的 scrollTop  currentY: 0, // touch-move 過程中的 y的坐標  topStatus: '', // 下拉刷新的狀態: pull(下拉) drop(釋放) loading(正在加載數據)  bottomStatus: '' // 上拉加載更多的狀態: 狀態同上 };}

上面的關于每個data數據的具體作用通過注釋做了詳細說明。

watch解析

watch: { topStatus(val) {  this.$emit('top-status-change', val);  switch (val) {   case 'pull':    this.topText = this.topPullText;    break;   case 'drop':    this.topText = this.topDropText;    break;   case 'loading':    this.topText = this.topLoadingText;    break;  } }, bottomStatus(val) {  this.$emit('bottom-status-change', val);  switch (val) {   case 'pull':    this.bottomText = this.bottomPullText;    break;   case 'drop':    this.bottomText = this.bottomDropText;    break;   case 'loading':    this.bottomText = this.bottomLoadingText;    break;  } }}

上面是組件通過watch監聽的兩個變量,后面我們能看到他們的改變是在touchmove事件中進行處理改變的。它的作用是通過它的變化來改變top slot和bottom slot的文本內容;

同時發出status-change事件給外部使用,因為可能外部自定義top slot 和bottom slot的內容,通過此事件來通知外部當前狀態以便外部進行處理。

核心函數的解析

這里就不將所有的method列出,下面就根據處理的所以來解析對應的method函數。

首先,入口是在組件mounted生命周期的鉤子回調中執行init函數

mounted() { this.init();// 當前 vue component掛載完成之后, 執行init()函數}

init函數:

init() {  this.topStatus = 'pull';  this.bottomStatus = 'pull';  this.topText = this.topPullText;  this.scrollEventTarget = this.getScrollEventTarget(this.$el); // 獲取滾動的dom節點  if (typeof this.bottomMethod === 'function') {   this.fillContainer(); // 判斷當前滾動內容是否填滿,沒有執行外部傳入的loadmore回調函數加載數據   this.bindTouchEvents(); // 為當前組件dom注冊touch事件  }  if (typeof this.topMethod === 'function') {   this.bindTouchEvents();  } }, fillContainer() {  if (this.autoFill) {   this.$nextTick(() => {    if (this.scrollEventTarget === window) {     this.containerFilled = this.$el.getBoundingClientRect().bottom >=      document.documentElement.getBoundingClientRect().bottom;    } else {     this.containerFilled = this.$el.getBoundingClientRect().bottom >=      this.scrollEventTarget.getBoundingClientRect().bottom;    }    if (!this.containerFilled) { // 如果沒有填滿內容, 執行loadmore的操作     this.bottomStatus = 'loading';     this.bottomMethod();// 調用外部的loadmore函數,加載更多數據    }   });  } }

init函數主要是初始化狀態和事件的一些操作,下面著重分析touch事件的回調函數的處理。

首先touchstart事件回調處理函數

 handleTouchStart(event) {  this.startY = event.touches[0].clientY; // 手指按下的位置, 用于下面move事件計算手指移動的距離  this.startScrollTop = this.getScrollTop(this.scrollEventTarget); // 起始scroll dom的 scrollTop(滾動的距離)  //下面重置狀態變量  this.bottomReached = false;  if (this.topStatus !== 'loading') {   this.topStatus = 'pull';   this.topDropped = false;  }  if (this.bottomStatus !== 'loading') {   this.bottomStatus = 'pull';   this.bottomDropped = false;  } }

主要是記錄初始位置和重置狀態變量。

下面繼續touchmove的回調處理函數

 handleTouchMove(event) {  //確保當前touch節點的y的位置,在當前loadmore組件的內部  if (this.startY < this.$el.getBoundingClientRect().top && this.startY > this.$el.getBoundingClientRect().bottom) {   return;  }  this.currentY = event.touches[0].clientY;  let distance = (this.currentY - this.startY) / this.distanceIndex;  this.direction = distance > 0 ? 'down' : 'up';  // 下拉刷新,條件(1.外部傳入了刷新的回調函數 2.滑動方向是向下的 3.當前滾動節點的scrollTop為0 4.當前topStatus不是loading)  if (typeof this.topMethod === 'function' && this.direction === 'down' &&   this.getScrollTop(this.scrollEventTarget) === 0 && this.topStatus !== 'loading') {   event.preventDefault();   event.stopPropagation();   //計算translate(將要平移的距離), 如果當前移動的距離大于設置的最大距離,那么此次這次移動就不起作用了   if (this.maxDistance > 0) {    this.translate = distance <= this.maxDistance ? distance - this.startScrollTop : this.translate;   } else {    this.translate = distance - this.startScrollTop;   }   if (this.translate < 0) {    this.translate = 0;   }   this.topStatus = this.translate >= this.topDistance ? 'drop' : 'pull';// drop: 到達指定的閾值,可以執行刷新操作了  }  // 上拉操作, 判斷當前scroll dom是否滾動到了底部  if (this.direction === 'up') {   this.bottomReached = this.bottomReached || this.checkBottomReached();  }  if (typeof this.bottomMethod === 'function' && this.direction === 'up' &&   this.bottomReached && this.bottomStatus !== 'loading' && !this.bottomAllLoaded) {   event.preventDefault();   event.stopPropagation();   // 判斷的邏輯思路同上   if (this.maxDistance > 0) {    this.translate = Math.abs(distance) <= this.maxDistance     ? this.getScrollTop(this.scrollEventTarget) - this.startScrollTop + distance : this.translate;   } else {    this.translate = this.getScrollTop(this.scrollEventTarget) - this.startScrollTop + distance;   }   if (this.translate > 0) {    this.translate = 0;   }   this.bottomStatus = -this.translate >= this.bottomDistance ? 'drop' : 'pull';  }  this.$emit('translate-change', this.translate); }

上面的代碼邏輯挺簡單,注釋也就相對不多。

重點談一下checkBottomReached()函數,用來判斷當前scroll dom是否滾動到了底部。

 checkBottomReached() {  if (this.scrollEventTarget === window) {   return document.body.scrollTop + document.documentElement.clientHeight >= document.body.scrollHeight;  } else {   return this.$el.getBoundingClientRect().bottom <= this.scrollEventTarget.getBoundingClientRect().bottom + 1;  } }

經過我的測試,上面的代碼是有問題:

當scrollEventTarget是window的條件下,上面的判斷是不對的。因為document.body.scrollTop總是比正常值小1,所以用于無法滿足到達底部的條件;

當scrollEventTarget不是window的條件下,上面的判斷條件也不需要在this.scrollEventTarget.getBoundingClientRect().bottom后面加1,但是加1也不會有太大視覺上的影響。

最后來看下moveend事件回調的處理函數

 handleTouchEnd() {  if (this.direction === 'down' && this.getScrollTop(this.scrollEventTarget) === 0 && this.translate > 0) {   this.topDropped = true; // 為當前組件添加 is-dropped class(也就是添加動畫處理)   if (this.topStatus === 'drop') { // 到達了loading的狀態    this.translate = '50'; // top slot的高度    this.topStatus = 'loading';    this.topMethod(); // 執行回調函數   } else { // 沒有到達,回調原點    this.translate = '0';    this.topStatus = 'pull';   }  }  // 處理邏輯同上  if (this.direction === 'up' && this.bottomReached && this.translate < 0) {   this.bottomDropped = true;   this.bottomReached = false;   if (this.bottomStatus === 'drop') {    this.translate = '-50';    this.bottomStatus = 'loading';    this.bottomMethod();   } else {    this.translate = '0';    this.bottomStatus = 'pull';   }  }  this.$emit('translate-change', this.translate);  this.direction = ''; }}

總結

  1. 下拉刷新和上拉加載更多的實現原理可以借鑒
  2. getScrollEventTarget()獲取滾動對象,getScrollTop()獲取滾動距離,checkBottomReached()判斷是否滾動到底部;這三種方式可以借鑒
  3. 缺點: 寫死了top slot 和 bottom slot的高度,太不靈活;這個地方可以優化

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
亚洲级视频在线观看免费1级| 国产精品免费在线免费| 国产精品xxx视频| 日韩av在线免费观看一区| 美女av一区二区三区| 91国内在线视频| 亚洲аv电影天堂网| 日韩在线视频观看正片免费网站| 久久精品视频一| 7m第一福利500精品视频| 日本午夜在线亚洲.国产| 日韩在线观看免费网站| 国产成人涩涩涩视频在线观看| 欧美一级成年大片在线观看| 在线成人中文字幕| 国产精品www色诱视频| 欧美午夜视频一区二区| 欧美一级淫片丝袜脚交| 国产91ⅴ在线精品免费观看| 日日骚久久av| 一本大道香蕉久在线播放29| 亚洲精品自拍偷拍| 国产精品极品在线| 国产成人精品一区二区三区| 亚洲精品在线91| 久久中文精品视频| 欧美精品电影免费在线观看| 亚洲色图校园春色| 国语自产偷拍精品视频偷| 97在线视频观看| 综合激情国产一区| 日本不卡高字幕在线2019| 久久久久久久999精品视频| 成人黄色免费在线观看| 色777狠狠综合秋免鲁丝| 亚洲免费av片| 一区二区三区视频观看| 亚洲老头同性xxxxx| 亚洲自拍欧美色图| 亚洲男人天堂2024| 欧美日韩在线观看视频| 亚洲成av人影院在线观看| 国模精品系列视频| 国产v综合v亚洲欧美久久| 国产精品一区二区性色av| 亚洲欧美日韩第一区| 国产不卡视频在线| 国产精品一区=区| 久久免费高清视频| 亚洲精品国产综合区久久久久久久| 亚洲一区二区三区香蕉| 5252色成人免费视频| 国产欧洲精品视频| 在线亚洲午夜片av大片| 亚洲精品视频网上网址在线观看| 欧美综合在线第二页| 91精品国产91久久久久久久久| 亚洲精品成人免费| 亚洲一二在线观看| 欧美刺激性大交免费视频| 538国产精品一区二区免费视频| 精品国产31久久久久久| 2019中文字幕免费视频| 九九热最新视频//这里只有精品| 久久久久亚洲精品成人网小说| 秋霞午夜一区二区| 国产成人精品av| 久久综合久中文字幕青草| 狠狠躁夜夜躁人人爽天天天天97| 欧美专区中文字幕| 综合激情国产一区| 91福利视频在线观看| 26uuu久久噜噜噜噜| 欧美日韩中文字幕在线视频| 欧美日韩午夜视频在线观看| 2020国产精品视频| 91av网站在线播放| 亚洲福利在线看| 91在线看www| 欧美成人黑人xx视频免费观看| 欧美日韩国产综合视频在线观看中文| 日韩av一区二区在线观看| 亚洲石原莉奈一区二区在线观看| 久久香蕉国产线看观看av| 久久久久久综合网天天| 精品亚洲国产视频| 欧美乱人伦中文字幕在线| 日韩成人高清在线| 成人在线激情视频| 这里只有精品视频在线| 国产精品91免费在线| 91精品国产91久久| 成人黄色片网站| 精品久久久视频| 国产福利视频一区二区| 日韩av一区在线| 不卡伊人av在线播放| 亚洲成人精品视频在线观看| 日韩精品免费在线| 精品偷拍各种wc美女嘘嘘| 国语自产精品视频在线看| 亚洲人高潮女人毛茸茸| 欧美人交a欧美精品| 91视频国产高清| 久久久精品2019中文字幕神马| 国产精品久久久久久久久男| 国产精品欧美风情| 91黄色8090| 亚洲色在线视频| 日韩av在线播放资源| 成人午夜激情免费视频| 亚洲美女av电影| 精品人伦一区二区三区蜜桃免费| 久久综合五月天| 韩国福利视频一区| 欧美丝袜美女中出在线| 最近2019中文字幕第三页视频| 九九九热精品免费视频观看网站| 亚洲香蕉成视频在线观看| 日本不卡高字幕在线2019| 日本午夜在线亚洲.国产| 日韩国产欧美区| 蜜月aⅴ免费一区二区三区| 国产又爽又黄的激情精品视频| 国产精品一区电影| 久久久免费精品视频| 久久精品国产v日韩v亚洲| 精品一区二区三区四区| 亚洲视频第一页| 久久亚洲国产精品成人av秋霞| 中文字幕精品—区二区| 国产91ⅴ在线精品免费观看| 在线精品视频视频中文字幕| 91免费版网站入口| 亚洲a区在线视频| 国内外成人免费激情在线视频| 国产精品第一区| 大胆人体色综合| 55夜色66夜色国产精品视频| 欧美精品www| 热99久久精品| 人妖精品videosex性欧美| 欧美日韩一区二区在线| 精品视频久久久| 国内揄拍国内精品少妇国语| 亚洲性视频网站| 色多多国产成人永久免费网站| 国产精品91在线| 欧美精品www在线观看| 欧美国产中文字幕| 久久6免费高清热精品| 亚洲视频999| 91夜夜未满十八勿入爽爽影院| 国产拍精品一二三| 成人国产在线视频| 亚洲欧美制服中文字幕| 日韩在线视频一区| 成人国产精品日本在线| 色偷偷综合社区| 成人免费黄色网| 国内精品模特av私拍在线观看| 欧美激情综合色综合啪啪五月| 国产区亚洲区欧美区| 中文字幕亚洲自拍|