通讯录功能

534 阅读6分钟

Hi~大家好,我是一名前端开发工程师,目前主要使用Angular、Vue来进行开发,来掘金社区挺久,第一次发帖子,请大家多多指教。

介绍 Ionic

小编目前正在使用Ionic3和Ionic5在做开发,下面简单介绍一下Ionic3。

  • Ionic 是基于Angular和Cordova结合开发出来的HTML5混合移动应用框架,可以快速创建一个跨平台的移动应用。
  • Ionic和Angular的关系:在Angular的基础上再做了一层封装,让我们可以使用Angular语法更快速和容易开发项目。
  • Ionic和Cordova关系:Ionic调用原生功能基于Cordova,Cordova提供JavaScript调用Native功能。
  • Ionic5已经额外支持Vue和React结合一起开发,而且开发起来也很舒服。Ionic5和Ionic3区别还是有点大,因为小编是最近才开始使用Ionic5,了解的还不够深,就不继续往下说了,有兴趣的可以去官网了解下。

安装项目、命令行相关

  • 可以通过官方提供的CLI脚手架来搭建项目
  • npm install -g @ionic/cli
  • 下载完成后搭建一个项目
  • ionic start myApp tabs
  • 因为npm下载的@ionic/cli 默认是最新版本的脚手架,所以在搭建项目时默认会搭建ionic5项目,如果需要搭建ionic3项目,更换以下这个命令行即可:
  • ionic start tabTest --type=ionic-angular
  • 搭建完成后我们使用 ionic serve 这个命令行来运行项目
  • ionic serve
  • Ionic3 启动成功显示界面
  • 成功启动之后我们需要通过命令行来创建文件夹
  • ionic g component|page|provider|pipe 文件名

封装通讯录

这里开始正式介绍一下怎么样在Ionic里面封装一个通讯录。

  • 首先我们先创建一个Page的文件
  • ionic g page addressBook
  • 创建成功我们首先在pages文件下的home.html写入一个button触发点击事件弹出addressBook
      <button (click)="goToAddressBookCom()">goToAddressBookCom</button>
  • 接着在home.ts文件引入ModalController包控制弹出addressBook
import { ModalController } from 'ionic-angular';
  • 在constructor中注册
 constructor(public modalCtrl: ModalController) { }
  • 接下来定义注册我们的click方法并且使用ModalController来弹出组件
  goToAddressBookCom() {
    let modal = this.modalCtrl.create('AddressBookPage');
    modal.present();
  }

这里是成功弹出来的addressBook:

  • 这里讲解一下写这个通讯录的思路:
  • 首先无非就是左右两边的实现联动,怎样联动?

侧边栏控制通讯栏:

  • 侧边栏:可以根据touchmove事件判断手指的滑动来做判断,还有点击当前的字母DOM元素来判断
  • 通讯栏:获取通列表DOM元素,根据侧边栏最终得到的Key值和通讯列表的innerHTML做判断,成功判断出来的获取它距离顶部的offsetTop,然后使用父元素.scrollTo(x,y)跳转到对应的起始位置。

通讯栏控制侧边栏:

  • 通讯栏:首先使用一个空对象,分别记录所有Key分别距离元素顶部的距离,然后使用addEventListener监听通讯栏的scroll事件,实时获取当前滚动距离,判断当前滚动距离是否大于等于当前记录Key的高度,判断满足就直接改变控制侧边栏的Key

下面把几个文件的代码写进来给大家参考:

  • addressBook.ts文件我们先定义好数据
    // 我们属需要的数据结构是这样的
    // 用 key作为通讯列表的标示:  # A - Z
    datas=[
     {
      "key": "#",
      "data": [
        {
          "code": "CN",
          "name": "中國"
        },
      ]
    },
      {
      "key": "A",
      "data": [
        {
          "code": "AU",
          "name": "澳大利亞"
        },
      ]
    },
    {
      "key": "B",
      "data": [
        {
          "code": "BE",
          "name": "比利時"
        },
      ]
    },
    // ........
    ]
  • 下面我们来书写html、css和ts代码
  • html
 <div id="areaContent">

    <div class="all-container" #container>
      <!-- 通讯栏 -->
      <div id="container" class="list-container">
        <div class="items" *ngFor="let val of address_book_datas;let i = index;">
          <div class="tag">{{val.key}}</div>
          <ul class="items-list-select">
            <li *ngFor="let keyValue of val.data;let keyIndex=index;" (click)="onItemClick(keyValue)">
              <span>{{keyValue.name}}</span>
              <span>{{keyValue.code}}</span>
            </li>
          </ul>
        </div>

      </div>
    </div>


    <!-- 侧边栏 -->
    <div id="letters" class="letters-scroll">
      <div class="items" *ngFor="let val of address_book_datas;let i = index;"
        [ngStyle]="{'color':lettersActiveType == val.key.trim() ? '#20AECE' : '#ccc'}">
        {{val.key}}
      </div>
    </div>

  </div>
  • css
page-address-book {

  #areaContent {
    display: flex;
    max-height: 100vh;
    overflow: hidden;
  }

  .all-container {
    margin-top: 20px;
    width: 100%;
    min-height: 100vh;
    padding-left: 15px;
    padding-right: 3px;
    overflow-y: scroll;
    z-index: 10;
  }


  .list-container {
    >.items {
      >.tag {
        background-color: #f4f4f9;
        margin-right: 21px;
      }

      >.items-list-select {
        padding-left: 0;

        >li {
          margin-right: 25px;
          display: flex;
          justify-content: space-between;
          color: #342648;
          font-size: 17px;
          border-bottom: 1px solid #eae9ef;

          >span {
            margin: 13px 0;
          }
        }
      }
    }
  }

  #letters {
    margin-top: 20px;
    width: 15px;
    position: absolute;
    max-height: 100vh;
    right: 6px;
    top: 50%;
    overflow: hidden;
    transform: translateY(-50%);
    -webkit-transform: translateY(-50%);
    -moz-transform: translateY(-50%);
    -ms-transform: translateY(-50%);
    -o-transform: translateY(-50%);
    color: #ccc;
    background-color: #fff;
    border-radius: 5px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    z-index: 100;
    overflow-y: hidden;

    >.items {
      width: 100%;
      display: block;
      text-align: center;
      padding: 4px 0;
      font-size: 12px;
    }
  }
}

  • ts
import { Component, ViewChild, NgZone, ElementRef } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';

@IonicPage()
@Component({
  selector: 'page-address-book',
  templateUrl: 'address-book.html',
})
export class AddressBookPage {

  // 通讯录数据(这里我就不提供了,大家自己随便弄几个就好了)
  address_book_datas:any = [];
  // 获取元素通讯录dom元素
  Container: any;
  @ViewChild('container') container: ElementRef;

  // 获取侧边栏dom元素
  tagList: any;

  tagEle: any;

  // 默认起始位置从 #  开始
  lettersActiveType: string = '#';

  constructor(public navCtrl: NavController, public navParams: NavParams) {}

  ionViewDidLoad() {
    this.Container = this.container.nativeElement;
    this.tagList = document.querySelectorAll('.tag');
    
    //启动监听事件
    this.addEventLetters();
    this.addEventContainer();
  }

  // 监听侧边栏 A-Z 事件
  addEventLetters() {

    let letterDom = document.getElementById('letters');
    
    //监听侧边栏手指滑动事件
    letterDom.addEventListener('touchmove', (event: any) => {
      event.preventDefault();
      let y = event.touches[0].clientY
      let x = event.touches[0].clientX
      //根据当前纵向坐标控制内容的位置
      let MaxL = letterDom.getBoundingClientRect().left;
      let MaxR = letterDom.getBoundingClientRect().right;
      let MaxT = letterDom.getBoundingClientRect().top
      let MaxB = letterDom.getBoundingClientRect().top + letterDom.getBoundingClientRect().height;

      if ((x >= MaxL && x <= MaxR) && (y >= MaxT && y <= MaxB) && x && y) {
        this.tagEle = document.elementFromPoint(x, y);
        let eleContent = this.tagEle.innerHTML.trim();
        this.lettersActiveType = eleContent;
        this.goToLetter(eleContent);
      }
    });

    //滑动侧边栏点击事件
    letterDom.addEventListener('click', (event: any) => {
      event.preventDefault();
      let eleContent = event.target.innerHTML.trim();
      this.lettersActiveType = eleContent;
      this.goToLetter(eleContent);
    })
    
    // 移除侧边栏touchmove事件
    letterDom.addEventListener("touchend", function (e) {
      letterDom.removeEventListener("touchmove", function () {
        console.log('移除touchmove事件')
      }, false);
    });

  }

  // 监听通讯录 A-Z事件
  addEventContainer() {

    let containerListDom: any = document.querySelectorAll('#container >.items');
    let tagList: any = document.querySelectorAll('#container >.items >.tag');
    let tagsHeight: any = {};
    
    //获取通讯列表每个Key距离父元素顶部距离
    
    for (let index = 0; index < containerListDom.length; index++) {
      let key = tagList[index].innerHTML;
      tagsHeight[`${key}`] = containerListDom[index].offsetTop;
    }

    //监听取通讯列当前滚动位置是否大于等于当前Key距离顶部位置,然后改变侧边栏的key值,从而实现联动
    this.Container.addEventListener('scroll', (event: any) => {
      for (const key in tagsHeight) {
        if (event.target.scrollTop >= tagsHeight[key]) {
          this.lettersActiveType = key;
        }
      }
    })
  }

  // 点击侧边栏字母
  jumpTag(type) {
    this.lettersActiveType = type;
    this.goToLetter(type);
  }

  // 跳转到对应Key起始位置
  goToLetter(Letter) {
    for (let index = 0; index < this.tagList.length; index++) {
      if (Letter === '#') {
        this.Container.scrollTo(0, 0, 0);
        break;
      }
      else if (this.tagList[index].innerHTML == Letter) {
        const goToHeiht = this.tagList[index].offsetTop;
        this.Container.scrollTo(0, goToHeiht, 0);
        break;
      }
    }
  }

  // 获取点击所选国家值
  onItemClick(data) {
    console.log(data);
  }

}

最终效果图

其实这种写法在Vue还有原生开发也适用,只不过是换一下写法

这是我第一次写博客,可能有很多写得不好的地方或者描述的不清楚的地方,希望大家可以提供一些改善的建议,请多多指教。

文中如有不对的地方欢迎各位小伙伴多多指正~

谢谢大家💖~