vue實現indexlist 組件

 使用

1 使用我的 scroll 組件,並且安裝依賴

2 複製代碼

3 需要給 indexlist 組件一個固定的高度

4 可以根據 class 名,自定義樣式,修改dom 結構的話就要修改源碼了 

預覽

 

<template>
    <div>
       <scroll class="scroll"
             :data='singerList'
             :pullUpLoad='upload'
             @pullUpLoad='pullUpLoad'
             :pullDownRefresh='refresh'
             @pullDownRefresh='downRefresh'
             @getScroll='getScroll'
          >
          <div class="singList" ref="singList">
             <dl class="singItem" 
                    v-for="(item,idx) in singerList" 
                    :key=item.title
                    :id='"dom"+idx'>
                 <dt :class='idx == index&&idx!=0?"hidden":""'>{{item.title}}</dt>
                 <dd v-for="ite in item.list" :key='ite.Fsinger_id'>
                    <img v-lazy="'//y.gtimg.cn/music/photo_new/T001R150x150M000'+ite.Fsinger_mid+'.jpg?max_age=2592000'">
                    <span>{{ite.Fsinger_name}}</span>
                 </dd>
             </dl>
          </div>
        </scroll>
        <!-- 右側bar -->
        <div class="navBar" ref="navBar" @touchstart="touchstartNavBar"  @touchmove='touchmoveNavBar'>
            <span v-for="(item,idx) in navBar" :class="idx==index?'active':''"
                                :key='idx' ref='navBarItem'
                                :indexId = 'idx'
                                >
               {{item}}
            </span>
        </div>
         <!-- 頂部fixbar -->
         <div class="fixBar" v-show="showFixBar">{{navBar[index]}}</div>

    </div>
</template>

<script>
import { getSingerList } from "@/api/singer.js";
import scroll from "@/util/components/BScroller.vue";

export default {
  data() {
    return {
      singerList:[],
      scroll:null,
      navBar:[],
      index:0,
      showFixBar:false
    }
  },
  // 在這裏定義一個變量和在data 裏面的區別是,這裏面的不需要動態渲染到頁面上
  created(){
     this.toTopHeight = 0;
     this.firstPageY = 0;
     this.itemHeight = 0;
     this.navBarSize = 0;
  },
  mounted() {
    getSingerList().then(res => {
      let list = res.data.list;
      let singerList = {};
      list.forEach(e => {
        if(singerList[e.Findex] == undefined){
           singerList[e.Findex] = [];
        }
        singerList[e.Findex].push(e);
      });

       let array =  [];
       for(let i in singerList){
         array.push({
           title:i,
           list:singerList[i]
         })
       }

      // 排序
      array = array.sort((a,b)=>{
        // return a.title.charCodeAt(0) - b.title.charCodeAt(0)
        if(a.title>b.title){
          return 1;
        }else if(a.title<b.title){
          return -1;
        }else{
          return 0; 
        }
      })
      this.singerList = array;
      // console.log(this.singerList);
      // 獲取navBar
       this.getNavBar();

    }); 

  },
  methods:{
    downRefresh(finash){
      this.$emit('Refresh',finash);
    },

    pullUpLoad(finash){
       this.$emit('UpLoad',finash);
    },

    getScroll(scroll){
      this.scroll = scroll;
      // 動態計算滾動區域距離頂部的高度
      this.toTopHeight = this.$refs.singList.getBoundingClientRect().top;
      
      this.scroll.on("scroll",(pos)=>{
        // console.log(this.index)
        console.log(pos.y)
        // 判斷滾動的方向
        //根據index 獲取當前顯示在屏幕中的dom
        let nowDom = document.getElementById("dom"+this.index);
        // 如果向下滑動的時候,最上面的dom 是 nowdom的時候,也就是id 是0
        // 的dom是nowdom的時候,繼續往下滑,border-top距離頂部的距離會由負數變成正數,這個時候
        // index 會別 --,變爲-1,然後再次執行getElementById 的時候會是null
        // 這個時候可以通過一些判斷來結局
        // if(nowDom===null&&this.index<0){
        //    this.index =0;
        //    nowDom = document.getElementById("dom"+this.index);
        // }

        // 一旦滾動到最頂端的時候,還往下拉這個時候就要隱藏fixBar
        // 到了最頂端還往下拉那麼說明y是>0 的,並且還會增大
        /**
         *    思考過程 
         * 1 向下滾動的過程中,內部dom的頂部距離外層上邊框的距離是在增加的,
         * 2 滾動到最頂部的時候, 距離是0,再往下拉是下拉刷新,距離是整數,
         * 3 往上滑動距離是負數,
         * 4 往上滑動的過程中,pos 的值是減小的,所以一旦 <n (小於某個值)的時候可以做一個判斷
         * 
         * **/ 
        if(pos.y > -10){
            if(pos.y>10)return;
            this.showFixBar = false;
        }
        else{
            if(pos<-30)return;
            this.showFixBar = true;
        }


        // console.log(nowDom);
        if(this.scroll.movingDirectionY===1){
            // console.log("上化")
            // 向上滾動
            // 如果dom 底部 距離窗口頂部的距離<0, 那麼說明這個dom
            // 被徹底的滾動到屏幕外面了
            let distanceToTop = nowDom.getBoundingClientRect().bottom - this.toTopHeight;
            // console.log(distanceToTop)
            if(distanceToTop<0){
                // 已經滾動到屏幕外面了,下一個進入nowdom,
                // 這個時候改變index ,這樣就可以隨着向下滾動,獲取的nowdom 始終是在屏幕中dom
                this.index ++;
            }
        }else{
            // console.log("下")
           // 向下滾動
           let distanceToTop = nowDom.getBoundingClientRect().top - this.toTopHeight;
            // 這個時候這個dom 被下滾動,並且上邊框已經從窗口頂部出來了,他的上一個元素
            // 變爲nowdom
            // console.log(distanceToTop)
            // 防止出現負數
           if(distanceToTop>0&&this.index>0){
               this.index --;
           }
        }
      })

    },
    getNavBar(){
      this.navBar = this.singerList&&this.singerList.map((item)=>{
         return item.title;
      })
      let allNum = this.singerList.length;
      this.navBarSize = allNum;
      // 獲取一個navbarItem 的高度
      let height = this.$refs.navBar.offsetHeight;
      this.itemHeight = height/allNum;
    },
    touchstartNavBar(e){
        //獲取 pageY的值,給touchmove 用
        this.firstPageY = e.touches[0].pageY; 
        let id = e.target.getAttribute("indexId");
        // 有時候會點擊到dom的空白處,沒有點擊到這個dom 也就獲取不到id
        if(id===null)return;
        // 修改index 標誌,告訴其他的dom 目前是這個item是nowdom,
        // 相關的地方會自動的修改
        this.index = Number(id);
        setTimeout(()=>{
            this.scrollTo();
            console.log(this.index);
        },30)
        console.log(id)
        console.log("觸發move 事件")
       
    },
    touchmoveNavBar(e){
      // e.currentTarget 綁定事件的dom
      // e.target 被點擊的最內層的dom
      // e.target 也可以是移動之前觸碰的第一個dom
      // 獲取當前手指的pageY
      // 計算出偏移量
      // 獲取絕對值
      let offetY =  e.touches[0].pageY - this.firstPageY;
      // 獲取第一個位置的dom 和 index 值
      let firstIndex =e.target.getAttribute('indexId');
      if(firstIndex === null)return;//沒有點擊在字母上,點擊在了其他地方
      //獲取一個字母的高度
      // 偏移量除以一個item的高度,計算出偏移了幾個item
      let num = Math.floor(offetY/this.itemHeight);
      // console.log("偏移的個數"+num);
      // console.log("firstIndex===="+firstIndex);
      // console.log(this.index);
      console.log(e.target.getAttribute('indexId'))
      
      // console.log(this.itemHeight)
      let index = Number(firstIndex)+num;
      // 如果滑到最頂部和最後一個的時候不
      if(index+1>this.navBarSize)return;
      if(index<0)return;
      this.index = index;
      this.scrollTo();


    },
    scrollTo(){
         // 根據index 獲取dom
        let dom = document.getElementById(`dom${this.index}`);
        // 滾動到dom 所在的位置
        this.scroll.scrollToElement(dom);
    }


  },
  computed:{
    
  },
  components:{
    scroll,
  },
   props:{
      singerList:{
          type:Array,
          default(){
            return []
          }
      },
      refresh:{
        default:false
      },
      upload:{
        default:false
      }

  },
  
};
</script>

<style scoped lang='scss'>
   .scroll{
     height:88vh;
     border: 1px solid darkgoldenrod;

     .singList{
           border: 1px solid springgreen;
       .singItem{
         border-bottom: 1px solid darkorange;
          dt{
               height: 8vmin;
              //  border: 1px solid darkcyan;
               line-height: 8vmin;
               padding-left: 12vmin;
          }
          dd{
             margin:5vmin;
             display: flex;
             align-items: center;

             img{
               height: 18vmin;
               width: 18vmin;
               border-radius: 50%;

             }

             span{
               margin-left:5vmin;

             }
          }
       }
     }
   }

   .navBar{
     position: absolute;
     top:20vh;
     right: 0px;
     display: flex;
     flex-direction: column;
     background-color: rgba(0,0,0,0.3);
     padding:0vmin 1.2vmin;
     border-radius:50px;
     height:60vh;
     justify-content: space-around;

    .active{
       color:rgb(112, 213, 49);
     }
   }

   .fixBar{
      height: 8vmin;
      width: 100vw;
      line-height: 8vmin;
      padding-left: 12vmin;
      background-color:rgba(255,255,255,0.1);
      position: fixed;
      top:12vh;
   }

   .hidden{
     opacity: 0;
   }
  
</style>

 

 

使用案例

 <template>
    <div>
       <indexList  class="indexList" 
                  :singerList = 'singerList'
                  @Refresh='downRefresh'
                  @UpLoad='pullUpLoad'
                  :upload =true
                  :refresh = {stop:50}
                   />
    </div>
</template>

<script>
import indexList from '@/util/components/indexList.vue';
import {getSingerList} from '@/api/singer.js';
 
export default {
  data(){
    return{
      singerList:[]
    }
  },
   components:{
     indexList,
   },
   mounted(){
     this.getList();
   },
   methods:{
      getList(){
          getSingerList().then(res => {
              let list = res.data.list;
              let singerList = {};
              list.forEach(e => {
                if(singerList[e.Findex] == undefined){
                  singerList[e.Findex] = [];
                }
                singerList[e.Findex].push(e);
              });

              let array =  [];
              for(let i in singerList){
                array.push({
                  title:i,
                  list:singerList[i]
                })
              }

              // 排序
              array = array.sort((a,b)=>{
                // return a.title.charCodeAt(0) - b.title.charCodeAt(0)
                if(a.title>b.title){
                  return 1;
                }else if(a.title<b.title){
                  return -1;
                }else{
                  return 0; 
                }
              })
              this.singerList = array;
              // console.log(this.singerList);
              // 獲取navBar
              // this.getNavBar();

          }); 
      },
      downRefresh(finash){
          console.log("刷新");
          setTimeout(()=>{
            finash();
          },2000)
      },
      pullUpLoad(finash){
          console.log("加載更多");
          setTimeout(()=>{
            finash(true);
          },2000)
      }

   }
}
</script>

<style lang="scss">
   .indexList{
     height: 88vh;
   }
    //最外層
    .singList{
      // 包括title 在內的一個item
       .singItem{
        //  標題
          dt{
          }
          // 一個字母對應的循環列表
          dd{
            // 右邊圖片
             img{
             }
            //  左邊文字
             span{
             }
          }
       }
     }

</style>
 

 

 

 

注意:

1 使用fastclick 可以防止右側導航欄的穿透。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章