Skip to content

vue组件--通讯录 #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
some-code opened this issue Jun 6, 2018 · 0 comments
Open

vue组件--通讯录 #8

some-code opened this issue Jun 6, 2018 · 0 comments
Labels

Comments

@some-code
Copy link
Owner

简介

在移动端开发中,通讯录是个很常见的需求。
通讯录通常要实现以下功能

  • 首字母导航
  • 滚动到一定位置首字母固定

在线通讯录demo

image

布局

通讯录是典型的上下两栏布局,上面是header,下面是内容区,我们这里采用flexbox来实现。

html,body,.page{height: 100%}
.page{display: flex}
.page-header{height: 44px}
.page-content{
  flex: 1;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
}
.navs {
  z-index: 2;
  position: fixed;
  right: 10px;
  top: 50px;
  bottom: 30px;
  text-align: center;
  color: #5d9ed3;
  font-size: 10px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  span {
    cursor: pointer;
  }
}
<div class='page'>
  <div class='page-header'>通讯录</div>
  <main class='page-content' ref='content'>
    <div class='page-navs' ref='navs'>
      <!--首字母导航区域-->
    </div>
    <div class='page-items' ref='items'>
      <!--通讯录内容-->
      <div class='item' v-for='i in 26' :key='i'>
        <div class='item-head'>A</div>
        <div class='item-list'>
          <div class='item-head'>a链家-张带看</div>
          <div class='item-head'>a天团tony</div>
        </div>
      </div>
    </div>
  </main>
</div>

OK。上面的代码已经足够定义一个页面的雏形,

  • page-header 高度44,
  • page-content 占据剩余的全局的高度,并做内部的滚动。-webkit-overflow-scrolling: touch 会在容器里面开启高性能滚动。

设置导航

页面布局完成之后,可以初始化导航条了,为了方便起见,我们默认通讯录里面包含了从A到Z的全部姓名。

  • 文章默认使用 vue单文件组 件开发,如果您使用其他框架,请自行转换代码
<template>
  <div class="page-navs">
    <span v-for='(item, index) in navs' :key='item'>{{item}}</span>
  </div>
</template>

<script>  
  data () {
    return {
      navs: "abcdefghijklmnopqrstuvwxyz".split("").map(i => i.toUpperCase());
    }
  }
</script>  

建立索引

建立索引实际上是用js操作dom,获取通讯录内容区域内每个首字母出现的位置并存储起来,方便做跳转和滚动监听。

<script>  
  moutend() {
    // 因为要获取dom属性,所以要在组件render后执行
    this.$nextTick(()=>{
      this.body = this.$refs.content;
      const navsEles = [...this.$refs.navs.querySelectorAll("span")]
      const itemsEles = [...this.$refs.items.querySelectorAll(".item")]
      
      // 获取导航栏字母的高度信息,方便做点击放大功能
      this.navsOffset = navsEles.map(item=>{
        return item.offsetTop || 0
      })
      
      // 获取通讯录内容区的首字母位置,方便做跳转和滚动监听
      this.itemsOffset = itemsEles.map(item=>{
        return item.offsetTop || 0
      })
    })
  },
  data () {
    return {
      body: null,
      itemsOffset: [],
      navsOffset: []
    }
  }
</script>  

监听跳转

监听跳转比较简单,在 .page-navs span 标签上绑定click事件即可处理

<template>
  <div class="page-navs">
    <span v-for='(item, index) in navs' :key='item' @click='jump(index)'>{{item}}</span>
  </div>
</template>

<script>  
  methods: {
    jump(index) {
      // 因为offsetTop属性是相对整个视口,而scrollTop是相对滚动容器,所以需要减去44px(header的高度)
      const offset = this.itemsOffset[index] - 44;
      this.body.scrollTop = offset;
    }
  },
  data () {
    return {
      navs: "abcdefghijklmnopqrstuvwxyz".split("").map(i => i.toUpperCase());
    }
  }
</script>  

监听滚动

因为是在page-content元素内部滚动,所以可以通过在该元素上绑定scroll方法监听页面的滚动。局部滚动的好处是组件销毁时事件监听也移除了,不像监听body的滚动还需要在销毁前手动removeEventListenr。

在执行滚动监听之前,我们还需要做两件事情

  1. itemsOffset 进行分组,划定监听的区间
  2. 在页面顶部创建一个展示 当前联系人首字母 的组件
// 当前联系人首字母组件
// 在这个组件里面也创建一个列表,用来做滚动的动画
<template>
  <div class="first-word">
    <div class="acr-list" :style="'transform: translate3d(0,'+(currentIndex * -40)+'px,0);'">
      <div class="item" v-for='(item, index) in navs' :key='index'>{{item}}</div>
    </div>
  </div>
</template>

// 对 `itemsOffset` 进行分组,划定监听的区间
mounted() {
  this.$nextTick(() => {
    let offsetCalc = this.offset.slice();
    offsetCalc.forEach((item, index) => {
      this.offsetList.push([item, offsetCalc[index + 1]]);
    });
  });
}
data() {
    return {
        currentIndex: -1,
        offsetList: []
    }
}

在准备工作做好之后,就开始监听容器的滚动行为,当滚动到通讯录之中的首字母部分时, 联系人首字母组件 也会自动滚动到里面相应的字母位置。

点击右侧导航,也会触发滚动事件。

// html模板部分
<main class='page-content' @scroll='scroll' ref='content'></main>

// js部分
methods: {
    scroll() {
      this.currentIndex = this.getArea(this.body.scrollTop);
    },
    getArea(scrollTop) {
      // 80是首字母标组件的高度+通讯录首字母的高度
      scrollTop += 80;
      let index = -1;
      for (let i = 0, size = this.offsetList.length; i < size; i++) {
        let [start, end] = this.offsetList[i];
        if (scrollTop >= start && scrollTop < (end || 999999)) {
          index = i;
          break;
        }
      }
      return index
    },
}

更多

点击右侧导航,有时候还要求在附近显示一个放大的字母,用于提醒点击了那个字母,通过前面获取的 navsOffset ,可以很方便的实现这个需求。至此,整个通讯录功能就基本完成了。

@some-code some-code added the vue label Jun 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant