EasonYou / my-blog

It's my blog recording front-end
2 stars 0 forks source link

Vue-index 一个vue的字母索引组件 #7

Open EasonYou opened 6 years ago

EasonYou commented 6 years ago

Vue-index 一个vue的字母索引组件

前言

公司需求要是实现一个首字母索引的功能,类似于微信通讯录那样的效果。在多次在github上查找都没有找到类似的实现,所以自己就简单地实现了一把。

dom结构

主要的dom解构分两大块,一大块是索引内容的列表,一大块是字母列表

<div
  class="index-lists-wapper"
  ref="scrollTarget">
  <div
    class="index-lists-content"
    ref="scrollWapper">
    <div
      v-for="(item, index) in finalDatas"
      :data-character="item.character"
      :key="index"
      class="list-item">
    </div>
  </div>
</div>
<div
  class="side-index-wapper"
  ref="sideIndexWapper"
  @mousedown="bindSideItemMouseDownEvent"
  @mousemove="bindSideItemMouseMoveEvent"
  @mouseup="bindSideItemMouseUpEvent">
  <div
    v-for="(item, index) in finalCharacters"
    :key="index"
    class="side-index-item">
  </div>
  </div>
</div>

在内容索引列表上,绑定上data-set,好在后面做查找对比。 因为要实现按住滑动还能查找的功能,在字母列表上做了事件代理,减少dom的事件绑定。

字母列表事件处理

mousedown的情况下,设置一个标志位为isMouseDown。在mousemove的时候切换查找的字母,最后mouseup的时候设标志位为false 在这里有一个问题就是在超出区域范围,会使得mouseup无效,所以在created的时候绑定了下windows,解决超出范围无效的情况。

bindSideItemMouseDownEvent (e) {
  this.isMouseDown = true
  this.lastTime = new Date()
  this.switchCharacter(e)
},
bindSideItemMouseMoveEvent (e) {
  const nowTime = new Date()
  if (nowTime - this.lastTime > this.throttleTime) {
    this.lastTime = nowTime
    this.switchCharacter(e)
  }
},
bindSideItemMouseUpEvent (e) {
  this.isMouseDown = false
  this.modalFlag = false
}

// created 
created () {
  const self = this
  document.addEventListener('mouseup', () => {
    self.isMouseDown = false
    self.modalFlag = false
  })
}

接下来是switchCharacter方法

switchCharacter (e) {
  if (this.isMouseDown) {
    // 判断是否可以触发判断,触发条件是是否代理到了side-index-item类的dom
    // 并返回这个字母
    let character = this.isCanTrigger(e)
    // 如果这个字母变了的话,就可以改变查看的索引字母了
    if (character && character !== this.character) {
      this.character = character
    }
  }
}
//
isCanTrigger (e) {
  let target = e.target
  let flag = false
  // while循环查找是否有side-index-item
  while (target) {
    if (target && target.className === 'side-index-item') {
      flag = true
      break
    }
    target = target.parentNode
  }
  if (!flag) {
    return false
  }
  // 这里 -2 是因为有个modal(字母弹出层)还有一个text dom
  // 因为是按循序排序,所以找出index,就可以找出对应的首字母
  let index = Array.from(this.$refs.sideIndexWapper.childNodes).indexOf(target) - 2
  return this.finalCharacters[index]
}

最后只要watch索引字母character就可以做滚动了

watch: {
  character (newVal, oldVal) {
    // 获取traget是为了滚动
    // 获取wapper是为了获取他的childNodes就是列表项
    const scrollTarget = this.$refs.scrollTarget
    const scrollWapper = this.$refs.scrollWapper
    const scrollItems = Array.from(scrollWapper.childNodes)
    let offsetTop
    // 遍历查找,找到就跳出便利,取得目标dom的offsetTop
    scrollItems.some((item) => {
      if (item.className === 'list-item' && item.dataset.character === newVal) {
        offsetTop = item.offsetTop
        return true
      }
    })
    // 进行滚动
    scrollTarget.scrollTop = offsetTop === undefined ? scrollTarget.scrollTop : offsetTop
  }
}

到此位置就是整个索引的主要流程,具体的代码可以看项目vue-index