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 可以防止右侧导航栏的穿透。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值