Vue + better-scroll 实现移动端字母索引导航功能


Posted in Javascript onMay 07, 2018

vue+ better-scroll 实现移动端歌手列表字母索引导航。算是一个学习笔记吧,写个笔记让自己了解的更加深入一点。

Demo:list-view,使用 chrome 手机模式查看。换成手机模式之后,不能滑动的话,刷新一下就 OK 了。

Github: 移动端字母索引导航

效果图

Vue + better-scroll 实现移动端字母索引导航功能 

配置环境

因为用到的是 vue-cli 和 better-scroll,所以首先要安装 vue-cli,然后再 npm 安装better-scroll。

简单介绍一下 better-scroll:

better-scroll 是一款重点解决移动端(已支持 PC)各种滚动场景需求的插件。它的核心是借鉴的 iscroll 的实现,它的 API 设计基本兼容 iscroll,在 iscroll 的基础上又扩展了一些 feature 以及做了一些性能优化。
better-scroll 是基于原生 JS 实现的,不依赖任何框架。它编译后的代码大小是 63kb,压缩后是 35kb,gzip 后仅有 9kb,是一款非常轻量的 JS lib。

除了这两,还使用 scss、vue-lazyload。scss 预处理器,大家都懂,用别的也一样。lazyload 实现懒加载,不用也可以,主要是优化一下体验。

数据直接使用了网易云的歌手榜单,偷懒就直接放在 data 里面了。

CSS 样式我就不贴了,直接看源码就可以了。

实现基本样式

直接使用 v-for 和 双侧嵌套实现歌手列表、以及右侧索引栏。

HTML 结构:

<ul>
 <li v-for="group in singers" 
 class="list-group" 
 :key="group.id" 
 ref="listGroup">
  <h2 class="list-group-title">{{ group.title }}</h2>
  <ul>
   <li v-for="item in group.items" 
   class="list-group-item" :key="item.id">
    <img v-lazy="item.avatar" class="avatar">
    <span class="name">{{ item.name }}</span>
   </li>
  </ul>
 </li>
</ul>
<div class="list-shortcut">
 <ul>
  <li v-for="(item, index) in shortcutList"
  class="item"
  :data-index="index"
  :key="item.id"
  >
   {{ item }}
  </li>
 </ul>
</div>

shortcutList 是通过计算属性得到的,取 title 的第一个字符即可。

shortcutList () {
 return this.singers.map((group) => {
  return group.title.substr(0, 1)
 })
}

使用 better-scroll

使用 better-scroll 实现滚动。对了,使用的时候别忘了用 import 引入。

created () {
 // 初始化 better-scroll 必须要等 dom 加载完毕
 setTimeout(() => {
  this._initSrcoll()
 }, 20)
},
methods: {
 _initSrcoll () {
  console.log('didi')
  this.scroll = new BScroll(this.$refs.listView, {
   // 获取 scroll 事件,用来监听。
   probeType: 3
  })
 }
}

使用 created 方法进行 better-scroll 初始化,使用 setTimeout 是因为需要等到 DOM 加载完毕。不然 better-scroll 获取不到 dom 就会初始化失败。

这里把方法写在两 methods 里面,这样就不会看起来很乱,直接调用就可以了。

初始化的时候传入两 probeType: 3,解释一下:当 probeType 为 3 的时候,不仅在屏幕滑动的过程中,而且在 momentum 滚动动画运行过程中实时派发 scroll 事件。如果没有设置该值,其默认值为 0,即不派发 scroll 事件。

给索引添加点击事件和移动事件实现跳转

首先需要给索引绑定一个 touchstart 事件(当在屏幕上按下手指时触发),直接使用 v-on 就可以了。然后还需要给索引添加一个 data-index 这样就可以获取到索引的值,使用 :data-index="index" 。

<div class="list-shortcut">
 <ul>
  <li v-for="(item, index) in shortcutList"
  class="item"
  :data-index="index"
  :key="item.id"
  @touchstart="onShortcutStart"
  @touchmove.stop.prevent="onShortcutMove"
  >
   {{ item }}
  </li>
 </ul>
</div>

绑定一个 onShortcutStart 方法。实现点击索引跳转的功能。再绑定一个 onShortcutMove 方法,实现滑动跳转。

created () {
 // 添加一个 touch 用于记录移动的属性
 this.touch = {}
 // 初始化 better-scroll 必须要等 dom 加载完毕
 setTimeout(() => {
  this._initSrcoll()
 }, 20)
},
methods: {
 _initSrcoll () {
  this.scroll = new BScroll(this.$refs.listView, {
   probeType: 3,
   click: true
  })
 },
 onShortcutStart (e) {
  // 获取到绑定的 index
  let index = e.target.getAttribute('data-index')
  // 使用 better-scroll 的 scrollToElement 方法实现跳转
  this.scroll.scrollToElement(this.$refs.listGroup[index])

  // 记录一下点击时候的 Y坐标 和 index
  let firstTouch = e.touches[0].pageY
  this.touch.y1 = firstTouch
  this.touch.anchorIndex = index
 },
 onShortcutMove (e) {
  // 再记录一下移动时候的 Y坐标,然后计算出移动了几个索引
  let touchMove = e.touches[0].pageY
  this.touch.y2 = touchMove
  
  // 这里的 16.7 是索引元素的高度
  let delta = Math.floor((this.touch.y2 - this.touch.y1) / 18)

  // 计算最后的位置
  // * 1 是因为 this.touch.anchorIndex 是字符串,用 * 1 偷懒的转化一下
  let index = this.touch.anchorIndex * 1 + delta
  this.scroll.scrollToElement(this.$refs.listGroup[index])
 }
}

这样就可以实现索引的功能了。

当然这样是不会满足我们的对不对,我们要加入炫酷的特效呀。比如索引高亮什么的~~

移动内容索引高亮

emmm,这个时候就有点复杂啦。但是有耐心就可以看懂滴。

我们需要 better-scroll 的 on 方法,返回内容滚动时候的 Y轴偏移值。所以在初始化 better-scroll 的时候需要添加一下代码。对了,别忘了在 data 中添加一个 scrollY,和 currentIndex (用来记录高亮索引的位置)因为我们需要监听,所以在 data 中添加。

_initSrcoll () {
 this.scroll = new BScroll(this.$refs.listView, {
  probeType: 3,
  click: true
 })
 // 监听Y轴偏移的值
 this.scroll.on('scroll', (pos) => {
  this.scrollY = pos.y
 })
}

然后需要计算一下内容的高度,添加一个 calculateHeight() 方法,用来计算索引内容的高度。

_calculateHeight () {
 this.listHeight = []
 const list = this.$refs.listGroup
 let height = 0
 this.listHeight.push(height)
 for (let i = 0; i < list.length; i++) {
  let item = list[i]
  height += item.clientHeight
  this.listHeight.push(height)
 }
}
// [0, 760, 1380, 1720, 2340, 2680, 2880, 3220, 3420, 3620, 3960, 4090, 4920, 5190, 5320, 5590, 5790, 5990, 6470, 7090, 7500, 7910, 8110, 8870]
// 得到这样的值

然后在 watch 中监听 scrollY,看代码:

watch: {
 scrollY (newVal) {
  // 向下滑动的时候 newVal 是一个负数,所以当 newVal > 0 时,currentIndex 直接为 0
  if (newVal > 0) {
   this.currentIndex = 0
   return
  }
  // 计算 currentIndex 的值
  for (let i = 0; i < this.listHeight.length - 1; i++) {
   let height1 = this.listHeight[i]
   let height2 = this.listHeight[i + 1]

   if (-newVal >= height1 && -newVal < height2) {
    this.currentIndex = i
    return
   }
  }
  // 当超 -newVal > 最后一个高度的时候
  // 因为 this.listHeight 有头尾,所以需要 - 2
  this.currentIndex = this.listHeight.length - 2
 }
}

得到 currentIndex 的之后,在 html 中使用。

给索引绑定 class -->  :class="{'current': currentIndex === index}"

最后再处理一下滑动索引的时候改变 currentIndex。

因为代码可以重复利用,且需要处理边界情况,所以就把

this.scroll.scrollToElement(this.$refs.listGroup[index])

重新写了个函数,来减少代码量。

// 在 scrollToElement 的时候,改变 scrollY,因为有 watch 所以就会计算出 currentIndex
scrollToElement (index) {
 // 处理边界情况
 // 因为 index 通过滑动距离计算出来的
 // 所以向上滑超过索引框框的时候就会 < 0,向上就会超过最大值
 if (index < 0) {
  return
 } else if (index > this.listHeight.length - 2) {
  index = this.listHeight.length - 2
 }
 // listHeight 是正的, 所以加个 -
 this.scrollY = -this.listHeight[index]
 this.scroll.scrollToElement(this.$refs.listGroup[index])
}

lazyload

lazyload 插件也顺便说一下哈,增加一下用户体验。

使用方法

先 npm 安装

在 main.js 中 import,然后 Vue.use

import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload, {
 loading: require('./common/image/default.jpg')
})

添加一张 loading 图片,使用 webpack 的 require 获取图片。

然后在需要使用的时候,把 :src="" 换成 v-lazy="" 就实现了图片懒加载的功能。

总结

移动端字母索引导航就这么实现啦,感觉还是很有难度的哈(对我来说)。

主要就是使用了 better-scroll 的 on 获取移动偏移值(实现高亮)、scrollToElement 跳转到相应的位置(实现跳转)。以及使用 touch 事件监听触摸,来获取开始的位置,以及滑动距离(计算最后的位置)。

总结

以上所述是小编给大家介绍的Vue + better-scroll 实现移动端字母索引导航功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
JavaScript实现Sleep函数的代码
Mar 04 Javascript
juery框架写的弹窗效果适合新手
Nov 27 Javascript
Javascript加载速度慢的解决方案
Mar 11 Javascript
分享纯手写漂亮的表单验证
Nov 19 Javascript
使用vue-cli创建项目的图文教程(新手入门篇)
May 02 Javascript
解决淘宝cnpm 安装后cnpm不是内部或外部命令的问题
May 17 Javascript
webstrom Debug 调试vue项目的方法步骤
Jul 17 Javascript
JS匿名函数内部this指向问题详析
May 10 Javascript
vue-cli配置全局sass、less变量的方法
Jun 06 Javascript
深入理解 ES6中的 Reflect用法
Jul 18 Javascript
Vue-Ant Design Vue-普通及自定义校验实例
Oct 24 Javascript
微信小程序picker组件两列关联使用方式
Oct 27 Javascript
node使用promise替代回调函数
May 07 #Javascript
node 使用 async 控制并发的方法
May 07 #Javascript
Angular 数据请求的实现方法
May 07 #Javascript
JavaScript数组去重算法实例小结
May 07 #Javascript
JavaScript求一组数的最小公倍数和最大公约数常用算法详解【面向对象,回归迭代和循环】
May 07 #Javascript
详解VUE-地区选择器(V-Distpicker)组件使用心得
May 07 #Javascript
JavaScript实现的DOM树遍历方法详解【二叉DOM树、多叉DOM树】
May 07 #Javascript
You might like
PHPnow安装服务[apache_pn]失败的问题的解决方法
2010/09/10 PHP
使用php显示搜索引擎来的关键词
2014/02/13 PHP
PHP中模拟链表和链表的基本操作示例
2016/02/27 PHP
Yii 框架入口脚本示例分析
2020/05/19 PHP
jQuery 表格工具集
2010/04/25 Javascript
提高javascript效率 一次判断,而不要次次判断
2012/03/30 Javascript
JS验证身份证有效性示例
2013/10/11 Javascript
JS Replace 全部替换字符的用法小结
2013/12/24 Javascript
node.js中的fs.closeSync方法使用说明
2014/12/17 Javascript
使用node+vue.js实现SPA应用
2016/01/28 Javascript
JavaScript代码里的判断小结
2016/08/22 Javascript
js当前页面登录注册框,固定div,底层阴影的实例代码
2016/10/04 Javascript
jQuery事件绑定方法学习总结(推荐)
2016/11/21 Javascript
原生js实现鼠标跟随效果
2017/02/28 Javascript
webpack-dev-server远程访问配置方法
2018/02/22 Javascript
nodejs前端模板引擎swig入门详解
2018/05/15 NodeJs
VUE预渲染及遇到的坑
2018/09/03 Javascript
简单说说如何使用vue-router插件的方法
2019/04/08 Javascript
vue自定义js图片碎片轮播图切换效果的实现代码
2019/04/28 Javascript
[09:34]2018DOTA2国际邀请赛寻真——永不放弃的iG
2018/08/14 DOTA
Python Web框架Tornado运行和部署
2020/10/19 Python
Python3安装Scrapy的方法步骤
2017/11/23 Python
Python实现直播推流效果
2019/11/26 Python
Python统计文本词汇出现次数的实例代码
2020/02/27 Python
Python局部变量与全局变量区别原理解析
2020/07/14 Python
英国银首饰公司:e&e Jewellery
2021/02/11 全球购物
说一下Linux下有关用户和组管理的命令
2016/01/04 面试题
后勤人员自我评价怎么写
2013/09/19 职场文书
党校个人总结
2015/03/04 职场文书
清洁工个人工作总结
2015/03/05 职场文书
退休欢送会致辞
2015/07/31 职场文书
2019升学宴主持词范本5篇
2019/10/09 职场文书
js实现模拟购物商城案例
2021/05/18 Javascript
python元组打包和解包过程详解
2021/08/02 Python
Java移除无效括号的方法实现
2021/08/07 Java/Android
JDK8中String的intern()方法实例详细解读
2022/09/23 Java/Android