vue单页应用中实现仿微信通信录选择功能


基础滚动:better-scroll https://github.com/ustbhuangyi/better-scroll
一名讲晒,万变不离其宗。。。
核心是window.scroll事件,获取到当前滚动y轴的scrollTop,初始化列表数据项的clientHeight,每项基于前一项增加,监听scrollY,如果满足clientHeightArray范围(可用for循环或者递归),即可取出当前索引index,然后执行对应跳转逻辑,说这么多废话,还是上代码吧。。。


<template>
    <div class="p-repay_banklist"  ref="list">
        <div class="p-repay--banklist" ref="listView">
            <ul ref="ul">
                <li v-for="group in formatBankList" class="p-banklist--item" :key="group.initial" ref="listGroup">
                    <div class="bank-item-header txt-color--6 txt-size--28">{{ group.initial }}</div>
                    <ul class="bank-item-content">
                        <li @click="selectBank(item,$event)" v-for="item in group.cells" class="o-repay_cell o-repay_cell--bdb o-repay_cell--p30 txt-size--32" :key="item.id">
                            <span class="bank-icon">
                                <img :src="item.param2" style="max-width:100%;height:auto;"/>
                            </span>
                            <span>{{item.name}}</span>
                        </li>
                    </ul>
                </li>
            </ul>
            <div class="list-shortcut p-banklist--guide">
                <ul>
                    <li v-for="(item, index) in quickguideList"
                    class="txt-size--32 txt-color--2"
                    :data-index="index"
                    :key="item.id"
                    @touchstart.prevent="onShortcutStart"
                    @touchmove.stop.prevent="onShortcutMove"
                    @touchend.stop.prevent="onShortcutEnd"
                    :class="{'current': currentIndex === index}"
                    >
                    {{ item }}
                    <span>{{item}}</span>
                    </li>
                </ul>
            </div>
            <div class="index-list-fixed txt-color--9 txt-size--28" ref="fixed" v-show="fixedTitle">
                {{fixedTitle}}
            </div>
        </div>
    </div>
</template>

<script>
import BScroll from "better-scroll";
const TITLE_HEIGHT = 30;
export default {
  data() {
    return {
      scrollY: 0,
      currentIndex: null,
      bankList: [],
      formatBankList: [],
      quickguideList: []
    };
  },
  computed: {
    fixedTitle() {
      if (this.scrollY > TITLE_HEIGHT) {
        return "";
      }
      return this.quickguideList[this.currentIndex]
        ? this.quickguideList[this.currentIndex]
        : "";
    }
  },
  watch: {
    scrollY(newVal) {
      // 向下滑动的时候 newVal 是一个负数,所以当 newVal > 0 时,currentIndex 直接为 0
      if (newVal > 0) {
        this.currentIndex = 0;
        return;
      }

      // 计算 currentIndex 的值
      var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
      var scrollHeight = this.$refs.ul.scrollHeight;
      //有滚动条的时候,当滚动距离在listheight范围内
      for (var i = 0; i < this.listHeight.length - 1; i++) {
        var height1 = this.listHeight[i];
        var height2 = this.listHeight[i + 1];
        if (-newVal >= height1 && -newVal < height2 && (-newVal + windowHeight) < scrollHeight ) {
          this.currentIndex = i;
          return;
        }
      }
    }
  },
  methods: {
    selectBank(item,e) {
        if(!e._constructed) {
          return
        }
        this.$cookies.set('bankName',item.name)
        this.$router.push(this.$store.state.wantTo)
    },
    singleUnique(arr) {
      //数组过滤相同的单个元素
      var hash = [];
      for (var i = 0; i < arr.length; i++) {
        for (var j = i + 1; j < arr.length; j++) {
          if (arr[i] === arr[j]) {
            ++i;
          }
        }
        hash.push(arr[i]);
      }
      return hash;

      //var x = new Set(arr);return [...x];
    },
    unique(arr, name) {
      //数组过滤相同的对象
      // var result = [];
      // var obj = {};
      // for(var i =0; i<arr.length; i++){
      //    if(!obj[arr[i].name]){
      //       result.push(arr[i]);
      //       obj[arr[i].name] = true;
      //    }
      // }
      // return result

      let hash = {};
      return arr.reduce(function(item, next) {
        hash[next[name]] ? "" : (hash[next[name]] = true && item.push(next));
        return item;
      }, []);
    },
    _initSrcoll() {
      this.scroll = new BScroll(this.$refs.listView, {
        probeType: 3,
        click: true
      });
      this.scroll.on("scroll", pos => {
        this.scrollY = pos.y;
      });
    },
    onShortcutStart(e) {
      e.target.children[0].classList.add('active')
      // 获取到绑定的 index
      let index = e.target.getAttribute("data-index");
      let firstTouch = e.touches[0].pageY;
      this.touch.y1 = firstTouch;
      this.touch.anchorIndex = index;
      this.scrollToElement(index);
    },
    onShortcutMove(e) {
      e.target.children[0].classList.add('active')
      // 再记录一下移动时候的 Y坐标,然后计算出移动了几个索引
      let touchMove = e.touches[0].pageY;
      this.touch.y2 = touchMove;
      // 这里的 16.7 是索引元素的高度
      let delta = Math.floor((this.touch.y2 - this.touch.y1) / 16.7);
      // 计算最后的位置
      // * 1 是因为 this.touch.anchorIndex 是字符串,用 * 1 偷懒的转化一下
      let index = this.touch.anchorIndex * 1 + delta;
      this.scrollToElement(index);
    },
    onShortcutEnd(e) {
      e.target.children[0].classList.remove('active')
    },
    scrollToElement(index) {
      // console.log(index)
      // 处理边界情况
      // 因为 index 通过滑动距离计算出来的
      // 所以向上滑超过索引框框的时候就会 < 0,向上就会超过最大值
      var windowHeight = document.documentElement.clientHeight || document.body.clientHeight
      var scrollHeight = this.$refs.ul.scrollHeight
      if(index < 0) {
        return
      }
      let curVal = this.listHeight[index]
      let curIndex = index

      if(this.listHeight[index] + windowHeight >= scrollHeight) {
        for(var i = 0; i < this.listHeight.length; i++) {
          if(this.listHeight[i] + windowHeight >= scrollHeight) {
            curIndex = i - 1
            break
          }
        }
      }
      // listHeight 是正的, 所以加个 -
      this.scrollY = -this.listHeight[curIndex]
      this.scroll.scrollToElement(this.$refs.listGroup[curIndex]);
    },
    _calculateHeight() {
      this.listHeight = [];
      let list = this.$refs.listGroup;
      let height = 0;
      this.listHeight.push(height);
      for (let i = 0; i < list.length - 1; i++) {
        let item = list[i];
        height += item.clientHeight;
        this.listHeight.push(height);
      }
    }
  },
  created() {
    this.commonApi.getParams({ typeId: "11" }).then(res => {
      if (res.returnCode == 0) {
        res.data.forEach(item => {
          this.bankList.push(item.currentNode);
          this.bankList = this.unique(this.bankList, "name");
        });
        this.bankList.forEach(item => {
          item.py = item.py.substr(0, 1).toUpperCase();
          this.quickguideList.push(item.py);
          this.quickguideList = this.singleUnique(this.quickguideList).sort();
        });
        this.quickguideList.forEach(item => {
          this.formatBankList.push(
            this.arrDataFormat(this.bankList, item, "py")
          );
        });

        // setTimeout(() => {
        //   this._initSrcoll()
        //   this._calculateHeight()
        // },20)
        this.$nextTick(() => {
          this._initSrcoll();
          this._calculateHeight();
        });
      }
    });
  },
  mounted() {
    this.touch = {};
  }
};
</script>

<style lang="scss">
@import "~@sass/_variables";
@import "~@sass/_func";
.p-repay_banklist {
  .p-repay--banklist {
    width: 100%;
    height: 100%;
  }
}
.p-banklist--item {
  display: flex;
  flex-direction: column;
  .o-repay_cell {
    display: flex;
    justify-content: flex-start;
  }
  .bank-item-header {
    height: 0.6rem;
    line-height: 0.7rem;
    margin: 0 0.4rem;
  }
  .bank-item-content {
    background: $bg-white;
    padding: 0 0.4rem;
    .bank-icon {
      display: inline-block;
      width: 0.586667rem;
      height: 0.586667rem;
      overflow: hidden;
      margin-right: 0.266667rem;
    }
  }
}

.p-banklist--guide {
  position: fixed;
  right: 0rem;
  top: 10%;
  padding: 0.2rem 0.35rem;
  background: transparent;
  li {
    list-style: none;
    margin-bottom: 0.1rem;
    position: relative;
    span {
      display: none;
      position: absolute;
      top:0;
      left: -0.45rem;
      &.active{
        display: block;
      }
    }
    &.current {
      color: red;
      position: relative;
    }
  }
}
.index-list-fixed {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  padding-left: 0.4rem;
  width: 100%;
  height: 0.64rem;
  line-height: 0.64rem;
  color:red;
  background: #f2f2f2;
}
</style>

把代码放出来,主要是在网上搜索的案例基本都是用本地数据模拟的,不太符合实战,还是得自己动手,丰衣足食。。。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值