vue/js 实现仿通讯录-列表滑动字母索引

最近写东西刚好遇到如下设计:

vue/js 实现仿通讯录-列表滑动字母索引_第1张图片

要求

  • 实现地区按字母排序
  • 加上侧边索引
  • 点击侧边索引滑动到指定字母列表
  • 页面滑动的时候顶栏序列号随之变化
  • PC端鼠标悬浮,移动端触屏侧边索引实现以上两个效果

步骤

  • 从country.json获取countryList数据
  • 提取侧边栏字母并排序
  • 将countryList按照字母分类排序
  • 对侧边栏进行操作

不废话直接上代码

代码有一丢丢长,如果看不下去可以直接 点击下载源码查看调试


<article class="country" ref="listview" v-show="countryList.length>0"  v-cloak>
    <ul class="country-list">
        <li v-for="group in countryList" ref="listGroup">
            <h2 class="list-group-title">{
    {group.title}}h2>
            <ul>
              <li v-for="item in group.items" :class="[{active:phoneCode==item.code},'list-group-item']" @click="phoneCode=item.code">
                <span class="name">{
    {item.name_cn}}span>
                <span>(+{
    {item.code}})span>
              li>
            ul>
        li>
    ul>
    <ul class="list-shortcut" @click.stop.prevent="onShortcutTouchStart"  @touchstart="onShortcutTouchStart" @touchmove.stop.prevent="onShortcutTouchMove" @touchend.stop.prevent="onShortcutTouchEnd">
        <li @mouseover.stop.prevent="onMouseover(index)" @mouseout.stop.prevent="onShortcutTouchEnd" :class="[{selected:fixedTitle==item},'item']" v-for="(item,index) in shortcut">{
    {item}}li>
    ul>
  <div class="selected-shortcut" v-show="isTouch">{
    {fixedTitle}}div>
    <div class="list-fixed" v-show="fixedTitle" ref="fixed">{
    {fixedTitle}}div>
article>
<script src="./js/jquery-3.2.1.min.js">script>
<script src="./js/vue.min.js">script>
<script type="text/javascript">
$(function() {
      
  var country = new Vue({
      
    el: ".country",
    data: {
      
      isTouch:false,
      countryList: [],
      shortcut: [],
      phoneCode: '93',
      touch: {
      },
      listHeight: [],
      scrollY: -1,
      currentIndex: 0,
      diff: -1,
      fixedTop: -1
    },
    created() {
      
      this.getCountryList();
    },
    computed: {
      
      fixedTitle() {
      
        return this.shortcut[this.currentIndex] ? this.shortcut[this.currentIndex] : 'A'
      }
    },
    mounted() {
      
      var _this = this;

      // 地区选择模块
      $('.country').scroll(function() {
      
        _this.scrollY = $('.country').scrollTop()
      })

      setTimeout(function() {
      
        _this.calculateTotalHeight()
      }, 20)
    },
    methods: {
      
      getCountryList() {
      
        var _this = this;
        $.getJSON('http://mdsApistatic/js/login/country.json', function(res) {
      
          // 格式化数据
          var map = {
      };
          res.forEach(function(item, index) {
      
            var key = item.name_en.slice(0, 1);
            if (!map[key]) {
      
              _this.shortcut.push(key)
              map[key] = {
      
                title: key,
                items: []
              }
            }
            map[key].items.push(item);
          })
          // 转为数组
          var ret = [];
          for (var k in map) {
      
            var val = map[k];
            ret.push(val);
          }
          // 排序
          ret.sort(function(a, b) {
      
            return a.title.charCodeAt(0) - b.title.charCodeAt(0);
          });
          _this.shortcut.sort(function(a, b) {
      
            return a.charCodeAt(0) - b.charCodeAt(0);
          });
          _this.countryList = ret;
        })
      },
      onShortcutTouchStart(e) {
      
        let anchorIndex = this.shortcut.indexOf(e.target.innerText);
        this.touch.y1 = e.pageY ? e.pageY : e.touches[0].pageY
        this.touch.anchorIndex = anchorIndex;
        this.scrollToIndex(anchorIndex);
        this.isTouch = true;
      },
      onShortcutTouchMove(e) {
      
        this.touch.y2 = e.touches[0].pageY
        var delta = (this.touch.y2 - this.touch.y1) / 16 | 0
        var anchorIndex = parseInt(this.touch.anchorIndex) + parseInt(delta)
        this.scrollToIndex(anchorIndex);
        this.isTouch = true;
      },
      onShortcutTouchEnd(){
      
        this.isTouch = false;
      },
      onMouseover(index){
      
        this.touch.anchorIndex = index;
        this.scrollToIndex(index);
        this.isTouch = true;
      },
      scrollToIndex(index) {
      
        this.$refs.listview.scrollTo(0, this.listHeight[index])
      },
      calculateTotalHeight() {
      
        var list = this.$refs.listGroup
        var height = 0
        this.listHeight.push(height)
        for (var i = 0; i < list.length; i++) {
      
          var item = list[i]
          height += item.clientHeight
          this.listHeight.push(height)
        }
      }
    },
    watch: {
      
      scrollY(newY) {
      
        var listHeight = this.listHeight
        // 当滚动到顶部时, newY<=0
        if (newY <= 0) {
      
          this.currentIndex = 0
          return
        }
        // 中间部分滚动
        for (var i = 0; i < listHeight.length - 1; i++) {
      
          var height1 = listHeight[i]
          var height2 = listHeight[i + 1]
          if (!height2 || (newY >= height1 && newY < height2)) {
      
            this.currentIndex = i
            this.diff = height2 - newY
            return
          }
        }
        // 滚动到底部且newY大于最后一个元素的上限
        this.currentIndex = listHeight.length - 1
      },
      diff(newVal) {
      
        var fixedTop = (newVal - 24) < 0 ? newVal - 24 : 0
        if (this.fixedTop === fixedTop) {
      
          return false;
        }
        this.fixedTop = fixedTop
        this.$refs.fixed.style.transform = `translate3d(-50%,${
        fixedTop}px,0)`
      }
    }
  })
})

script>

我的个人博客,有空来坐坐

https://www.wangyanan.online

你可能感兴趣的:(VUE,前端,插件,仿通讯录,字母索引)