vue2.0:(十)、外賣App商品組件部分和better-scroll

本篇中繼續來給你們介紹外賣App製做中的商品組件的部分。javascript

好,第一步,咱們把商品的大體框架在goods.vue中搭建出來:vue

  

  menu-wrapper是左邊菜單欄,foods-wrapper是右邊商品欄。java

第二步,咱們須要取到咱們須要的數據。json

  

  可能有人這時候會有一點疑問,由於他們記得header裏面的seller是這麼取得:api

  

  那咱們如今來看一下data.json:數組

  

    一個是json 一個是數組,因此,如上述代碼可取出咱們所須要的goods。瀏覽器

 第三步,寫側邊欄menu-wrapper,並填入數據app

  

  代碼:框架

<div class="menu-wrapper" ref="menuWrapper">     <!-------point3-3
     <ul>
         <li v-for='(item,index) in goods' class="menu-item" :class="{'current':currentIndex === index}" @click="selectMenu(index,$event)">   <!-------point3-2
             <span class="text border-1px">
                   <span v-show="item.type>0" class="icon" :class="classMap[item.type]"></span>   <!-------point3-1
                   {{item.name}}
             </span>
         </li>
     </ul>
</div>

  一、由於左側邊欄一看就是一個列表,因此用ul和li來佈局。並用v-for來循環由於goods的數據裏每一條都是一個li。dom

  二、point3-1 :有的菜單有icon,有的沒有,這些取決於data中的一個type。type>0,可能爲1,2,3,4就表明有icon圖片。type=-1就表明沒有圖片。因此,v-show="item.type>0" 就表示,只有當item.type>0時,才讓它顯示。

 第四步,寫商品欄,goods-wrapper:

  

  基本商品佈局搭建出來後,咱們須要實現一個滾動的功能。a.menu-wrapper和goods-wrapper都能滾動。b.滾動goods-wrapper,也就是滾動過程當中,這一條商品是屬於菜單的哪一條li,讓這個菜單被選中。c.點擊menu-wrapper,讓goods-wrapper跳到相應菜單中。

第五步:實現滾動:

  在這裏,咱們運用better-scroll來作。

  實現這個效果以前,須要解釋幾個知識點:

  a.ref和$refs:

    ref在官網上的解釋簡單來講就是用來綁定某個dom元素,或者來講用來綁定某個組件,而後在$refs裏面調用,具體實例以下:

<div class="touchscroll">

</div>
若是咱們想要獲取這個div的某個值,好比scrollTop的值,常規的作法是咱們必須用document.querySelector(".touchScroll")獲取這個dom節點,而後在獲取scrollTop的值。

  可是用ref綁定以後,咱們就不須要在獲取dom節點了,直接在上面的div上綁定div,而後$refs裏面調用就行所以上面能夠寫成:

<div class="touchscroll" ref='div'>
</div>
而後在javascript裏面這樣調用:this.$refs.div.scrollTop.這樣就能夠減小獲取dom節點的消耗了。若是還不清楚建議嘗試打印下 this.$refs 就知道了。

  b.$nextTick:

    Vue 實現響應式並非數據發生變化以後 DOM 當即變化,而是按必定的策略進行 DOM 的更新。$nextTick 是在下次 DOM 更新循環結束以後執行延遲迴調,在修改數據以後使用 $nextTick,則能夠在回調中獲取更新後的 DOM。也許有人會問,在 Vue 實例方法中修改了數據,而後再在 $nextTick 回調中獲取該數據在相應 DOM 元素所綁定的內容(或屬性),這是沒有必要的事情,那爲何還須要這樣的 API 呢?考慮這樣一種場景,你有一個 jQuery 插件,但願在 DOM 元素中某些屬性發生變化以後從新應用該插件,這時候就須要在 $nextTick 的回調函數中執行從新應用插件的方法。例子見下述5-1:

  5-1.menu-wrapper和goods-wrapper都能滾動。

    step1.分別給menu-wrappergoods-wrapper 標籤加上ref=「他們的別名」;如point3-3,這樣方便咱們獲取,像上述說減小獲取dom節點的消耗。

    step2.初始化better-scroll。

/*其餘代碼暫時不考慮*/
created(){
this.classMap = ['decrease','discount','special','invoice','guarantee']; this.$http.get('/api/goods').then((response) => { response = response.body; if(response.errno === ERR_OK){ this.goods = response.data; this.$nextTick(() => { <!-------------------------point5-1-2 this._initScroll(); this._calculateHeight(); }); } }); }, methods:{ _initScroll(){ <!-------------------------point5-1-1 this.menuScroll = new BScroll(this.$refs.menuWrapper,{ click:true }); this.foodsScroll = new BScroll(this.$refs.foodsWrapper,{ probeType:3 }); } }

  5-2.滾動goods-wrapper,也就是滾動過程當中,這一條商品是屬於菜單的哪一條li,讓這個菜單被高亮。

    step1.計算高度:那這段代碼寫在哪裏呢?寫在$nextTick裏面,就是point5-1-2。由於這時候,拿到數據,dom更新,咱們再去計算每個菜單區間的高度。並把它們push進一個數組裏面。組成一個遞增的數組。就是listHeight。

 

data(){
      return{
          goods:[],
          listHeight:[],/*每個菜單區間的高度*/
          scrollY:0
      };
},
methods:{
_initScroll(){ this.menuScroll = new BScroll(this.$refs.menuWrapper,{ click:true }); this.foodsScroll = new BScroll(this.$refs.foodsWrapper,{ probeType:3 }); }, _calculateHeight(){ let foodList =this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');/*每個菜單*/ let height = 0; this.listHeight.push(height); for(let i=0;i<foodList.length;i++){ let item = foodList[i]; height += item.clientHeight; this.listHeight.push(height); } } }

 

    step2.左右兩側的區間做比較。須要一個vue的calculate的屬性。

    先捋一下這一步的思路:

    當右邊滾動的實時位置scrollY在this.listHeight[i]和this.listHeight[i+1]之間,也就是說這個實時位置,在這個菜單和下一個菜單之間的時候,咱們須要獲取此時的index是第幾個來和左邊菜單的index做對比。

 

 <div class="menu-wrapper" ref="menuWrapper">
            <ul>
                <li v-for='(item,index) in goods' class="menu-item" :class="{'current':currentIndex === index}" @click="selectMenu(index,$event)"><!-------------------------point5-2-5
                    <span class="text border-1px">
                        <span v-show="item.type>0" class="icon" :class="classMap[item.type]"></span>
                        {{item.name}}
                    </span>
                </li>
            </ul>
        </div>


return{ goods:[], listHeight:[],/*每個菜單區間的高度*/ scrollY:0 }; }, computed:{                                <!-------------------------point5-2-4 currentIndex(){ for(let i=0;i<this.listHeight.length;i++){ let height1 = this.listHeight[i]; let height2 = this.listHeight[i + 1]; if(!height2 || (this.scrollY >= height1 && this.scrollY < height2)){ return i; } } return 0; } }, created(){ this.classMap = ['decrease','discount','special','invoice','guarantee']; this.$http.get('/api/goods').then((response) => { response = response.body; if(response.errno === ERR_OK){ this.goods = response.data; this.$nextTick(() => { this._initScroll(); this._calculateHeight(); }); } }); }, methods:{ selectMenu(index,event){ if(!event._constructed){ return; } let foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook'); let el =foodList[index]; this.foodsScroll.scrollToElement(el,300); }, _initScroll(){ this.menuScroll = new BScroll(this.$refs.menuWrapper,{ click:true }); this.foodsScroll = new BScroll(this.$refs.foodsWrapper,{ probeType:3 <!-------------------------point5-2-1 }); this.foodsScroll.on('scroll',(pos)=>{             <!-------------------------point5-2-2 this.scrollY = Math.abs(Math.round(pos.y));        <!-------------------------point5-2-3 }); }, _calculateHeight(){ let foodList =this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');/*每個菜單*/ let height = 0; this.listHeight.push(height); for(let i=0;i<foodList.length;i++){ let item = foodList[i]; height += item.clientHeight; this.listHeight.push(height); } } }

 

    tip1.point5-2-1probeType:3,是betterscroll的一個屬性,表明着在滾動的時候能實時地告訴咱們滾動的位置,至關於探針的一個效果,

       tip2.point5-2-2this。foodsScroll能夠有一個監聽scroll的操做,也就是當foodsScroll滾動的時候,能指明實時位置。因此,這個point5-2-2的回調函數裏面的參數,就是這個實時位置pos.

    tip3.point5-2-3pos.y是小數,須要給它整數化。而後再取絕對值。轉化爲正值。

    tip4.point5-2-4computed:是一個vue自帶的,叫計算屬性。!height2 || (this.scrollY >= height1 && this.scrollY < height2)這個判斷就說明若是是最後一個,或者落在這個區間,就返回這個菜單的索引。

    tip5.point5-2-5綁定一個class,若是.point5-2-4 返回的索引和本次循環的index相同,則給index加上current class。而後給current一個樣式便可。

5-3.點擊menu-wrapper,讓goods-wrapper跳到相應菜單中:

 

<div class="menu-wrapper" ref="menuWrapper">
        <ul>
           <li v-for='(item,index) in goods' class="menu-item" :class="{'current':currentIndex === index}" @click="selectMenu(index,$event)"> <!--------加點擊事件>
               <span class="text border-1px">
                     <span v-show="item.type>0" class="icon" :class="classMap[item.type]"></span>
                        {{item.name}}
               </span>
            </li>
         </ul>
  </div>
<script>
export default{ props:{ seller:{ type:Object } }, data(){ return{ goods:[], listHeight:[],/*每個菜單區間的高度*/ scrollY:0 }; }, computed:{ currentIndex(){ for(let i=0;i<this.listHeight.length;i++){ let height1 = this.listHeight[i]; let height2 = this.listHeight[i + 1]; if(!height2 || (this.scrollY >= height1 && this.scrollY < height2)){ return i; } } return 0; } }, created(){ this.classMap = ['decrease','discount','special','invoice','guarantee']; this.$http.get('/api/goods').then((response) => { response = response.body; if(response.errno === ERR_OK){ this.goods = response.data; this.$nextTick(() => { this._initScroll(); this._calculateHeight(); }); } }); }, methods:{ selectMenu(index,event){ if(!event._constructed){                      <!-------------------------point5-3-3 return; } let foodList = this.$refs.foodsWrapper.getElementsByClassName('food-list-hook'); let el =foodList[index]; this.foodsScroll.scrollToElement(el,300); <!-------------------------point5-3-1 }, _initScroll(){ this.menuScroll = new BScroll(this.$refs.menuWrapper,{ click:true                 <!-------------------------point5-3-2 }); this.foodsScroll = new BScroll(this.$refs.foodsWrapper,{ probeType:3 }); this.foodsScroll.on('scroll',(pos)=>{ this.scrollY = Math.abs(Math.round(pos.y)); }); }, _calculateHeight(){ let foodList =this.$refs.foodsWrapper.getElementsByClassName('food-list-hook');/*每個菜單*/ let height = 0; this.listHeight.push(height); for(let i=0;i<foodList.length;i++){ let item = foodList[i]; height += item.clientHeight; this.listHeight.push(height); } } } }
</script>

 

    tip1.point5-3-1滾動到相應的位置,須要用到better-scroll的一個接口。scrollToElement(el,300);即經過index值,去找到右側滾動到了哪個節點。

    tip2.point5-3-2:不只有原生的點擊事件,還會有初始化時,咱們默認的加的clicktrue事件,因此,當模式切換到電腦時,會有兩個事件加上。因此,咱們還須要在點擊事件裏再傳一個$event。

       tip3.point5-3-3:當咱們本身有一個默認派發的事件的時候,這個_constructed就是true。而瀏覽器的原生則是沒有_constructed這個屬性的。也就是當咱們在日常檢測到這個屬性的時候,就把他return掉,不執行。

相關文章
相關標籤/搜索