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 结构:

    class="list-group"

    :key="group.id"

    ref="listGroup">

      class="list-group-item":key="item.id">

        class="item"

        :data-index="index"

        :key="item.id"

        >

        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 事件。

        顺便给大家推荐一个裙,它的前面是 537,中间是631,最后就是 707。想要学习前端的小伙伴可以加入我们一起学习,互相帮助。群里每天晚上都有大神免费直播上课,如果不是想学习的小伙伴就不要加啦。

        (537-631-707)

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

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

          class="item"

          :data-index="index"

          :key="item.id"

          @touchstart="onShortcutStart"

          @touchmove.stop.prevent="onShortcutMove"

          >

          绑定一个 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(leti= 0;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

          }elseif(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 插件也顺便说一下哈,增加一下用户体验。

          顺便给大家推荐一个裙,它的前面是 537,中间是631,最后就是 707。想要学习前端的小伙伴可以加入我们一起学习,互相帮助。群里每天晚上都有大神免费直播上课,如果不是想学习的小伙伴就不要加啦。(537-631-707)

          使用方法

          1.先 npm 安装

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

          importVueLazyloadfrom'vue-lazyload'

          Vue.use(VueLazyload, {

          loading:require('./common/image/default.jpg')

          })

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

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

          ©著作权归作者所有,转载或内容合作请联系作者
          • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
            沈念sama阅读 160,504评论 4 365
          • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
            沈念sama阅读 67,898评论 1 300
          • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
            开封第一讲书人阅读 110,218评论 0 248
          • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
            开封第一讲书人阅读 44,322评论 0 214
          • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
            茶点故事阅读 52,693评论 3 290
          • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
            开封第一讲书人阅读 40,812评论 1 223
          • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
            沈念sama阅读 32,010评论 2 315
          • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
            开封第一讲书人阅读 30,747评论 0 204
          • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
            沈念sama阅读 34,476评论 1 246
          • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
            茶点故事阅读 30,700评论 2 251
          • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
            茶点故事阅读 32,190评论 1 262
          • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
            沈念sama阅读 28,541评论 3 258
          • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
            茶点故事阅读 33,206评论 3 240
          • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
            开封第一讲书人阅读 26,129评论 0 8
          • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
            开封第一讲书人阅读 26,903评论 0 199
          • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
            沈念sama阅读 35,894评论 2 283
          • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
            茶点故事阅读 35,748评论 2 274

          推荐阅读更多精彩内容