Vue+better-scroll 实现通讯录字母索引的示例代码

目录
  • 环境准备:
  • 实现步骤:
    • 数据结构
    • 基本HTML
    • 使用better-scroll实现内容区列表的滚动
    • 给索引添加点击事件和移动事件实现跳转
    • 给索引添加高亮
    • 全部代码
  • 总结
  • 参考文献

如图,实现一个滚动内容区域,右侧字母滚动索引定位;选择和拖动字母索引,对应内容滚动到视窗

环境准备:

  • 安装better-scroll npm包
  • 安装 mouseWheel 扩展 BetterScroll 鼠标滚轮的能力,开启鼠标滚动(移动端非必须)
npm install @better-scroll/core  @better-scroll/mouse-wheel --save

实现步骤:

数据结构

  • 内容区和索引按下图数据结构处理
export default {
  data() {
    return {
      entityList: [
        {
          key: 'A',
          list: ['氨基酸代谢病', '广泛性发育障碍']
        },
        {
          key: 'B',
          list: ['巴特综合征', '包涵体性结膜炎', '膀胱外翻', '鼻腔结外型NK/T细胞淋巴瘤']
        },
        {
          key: 'C',
          list: ['C5功能不全综合征', '肠道蛔虫症', '喘息样支气管炎']
        },
        {
          key: 'D',
          list: ['低氯性氮质血症综合征', '石棉状糠疹', 'Dravet综合征']
        }
      ]
    };
  }
};

基本HTML

<!-- 内容区域 -->
<!-- 最外层父容器wrapper,固定高度并且overflow:hidden-->
<div class="h-534px flex-1 wrapper overflow-hidden" ref="wrapper">
    <!-- content 注意滚动区域一定是父容器的第一个子元素,当高度超出父容器即可滚动 -->
    <ul class="content">
        <!-- v-for 循环出列表 -->
        <li
            v-for="(item, index) in entityList"
            :key="index"
            class="flex flex-col"
            ref="listGroup"
        >
            <div
                class="h-42px leading-42px text-sm font-bold pl-15px w-244px"
            >
                {{ item.key }}
            </div>
            <div class="flex flex-col">
                <span
                    class="h-42px leading-42px text-sm pl-15px g-clamp1 w-244px"
                    v-for="(it, i) in item.list"
                    :key="i"
                >
                    {{ it }}
                </span>
            </div>
        </li>
    </ul>
</div>
<!-- 索引 -->
<ul class="entityList w-15px bg-white">
     <!-- v-for 循环出索引 -->
    <li
        v-for="(item, index) in entityList"
        :key="index"
        :data-index="index"
        class="w-3 text-4 h-3 mb-1 leading-3 text-center text-gray6"
    >
        {{ item.key }}
    </li>
</ul>

使用better-scroll实现内容区列表的滚动

<script>
//import 引入BScroll
import BScroll from '@better-scroll/core';
import MouseWheel from '@better-scroll/mouse-wheel';
BScroll.use(MouseWheel);
export default {
    mounted() {
      //dom渲染完毕,初始化better-scroll
        this.$nextTick(() => {
            this.initBanner();
        });
    },
    methods: {
        initBanner() {
            if (this.scroll && this.scroll.destroy){
                this.scroll.refresh();//当 DOM 结构发生变化的时候需重新计算 BetterScroll
                this.scroll.destroy();//销毁 BetterScroll,解绑事件
            }
            this.scroll = new BScroll('.wrapper', {
                scrollY: true,//纵向滚动
                click: true,
                mouseWheel: true,
                disableMouse: false, //启用鼠标拖动
                disableTouch: false, //启用手指触摸
                probeType: 3 //设置为3,BetterScroll实时派发 scroll 事件
            });
        }
    }
};
</script>

注意:这里我们在mounted时期,在this.$nextTick 的回调函数中初始化 better-scroll 。这时wrapper 的 DOM 已经渲染了,我们可以正确计算它以及它内层 content 的高度,以确保滚动正常。

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

<ul class="entityList w-15px bg-white">
  <li
      v-for="(item, index) in entityList"
      :key="index"
      :data-index="index"
      class="w-3 text-4 h-3 mb-1 leading-3 text-center text-gray6"
      @touchstart="onShortcutStart" //点击事件
      @touchmove.stop.prevent="onShortcutMove" //移动事件
      >
    {{ item.key }}
  </li>
</ul>
created() {
  // 添加一个 touch 用于记录移动的属性
  this.touch = {};

  this.$nextTick(() => {
      this.initBanner();
  });
},
methods: {
  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);

      // 计算最后的位置
      let index = this.touch.anchorIndex * 1 + delta;
      this.scroll.scrollToElement(this.$refs.listGroup[index]);
  }
}

给索引添加高亮

  • 在data中定义currentIndex用于索引高亮的判断,并在html中绑定class
data() {
  return {
    currentIndex: 0,
    entityList: [
        {
          key: 'A',
          list: ['氨基酸代谢病', '广泛性发育障碍']
        }]
    }
}
<ul class="entityList w-15px bg-white">
    <li
        v-for="(item, index) in entityList"
        :key="index"
        :data-index="index"
        class="w-3 text-4 h-3 mb-1 leading-3 text-center text-gray6"
        @touchstart="onShortcutStart"
        @touchmove.stop.prevent="onShortcutMove"
        :class="{ current: currentIndex === index }"
    >
        {{ item.key }}
    </li>
</ul>

接下来求currentIndex:

  • 先通过better-scroll 的on(type, fn, context)方法,监听当前实例上的scroll,得到内容区y轴的偏移量
initBanner() {
    if (this.scroll && this.scroll.destroy) {
        this.scroll.refresh();
        this.scroll.destroy();
    }
    this.scroll = new BScroll('.wrapper', {
        scrollY: true,
        click: true,
        mouseWheel: true,
        disableMouse: false, //启用鼠标拖动
        disableTouch: false, //启用手指触摸
        probeType: 3
    });
  // 监听Y轴偏移的值
    this.scroll.on('scroll', pos => {
        this.scrollY = pos.y;
    });
},
  • data中初始化 listHeight ,添加calculateHeight() 方法计算内容区高度
data() {
    return {
        listHeight: [],
        currentIndex: 0,
        entityList: [
            {
              key: 'A',
              list: ['氨基酸代谢病', '广泛性发育障碍']
            }
        ]
    }
}
//计算内容区高度
_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);
    }
}
  • data中初始化scrollY为-1,在 watch 中监听 scrollY
data() {
  return {
    scrollY: -1
    currentIndex: 0,
    listHeight: [],
      entityList: [
        {
          key: 'A',
          list: ['氨基酸代谢病', '广泛性发育障碍']
        }
      ]
  }
}
 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 实现索引高亮的特效

全部代码

<template>
    <div>
        <!-- 内容区域 -->
        <div class="h-534px flex-1 wrapper overflow-hidden" ref="listview">
            <ul class="content">
                <li
                    v-for="(item, index) in entityList"
                    :key="index"
                    class="flex flex-col"
                    ref="listGroup"
                >
                    <div class="h-42px leading-42px text-sm font-bold pl-15px w-244px">
                        {{ item.key }}
                    </div>
                    <div class="flex flex-col">
                        <Link
                            class="h-42px leading-42px text-sm pl-15px g-clamp1 w-244px"
                            v-for="(it, i) in item.list"
                            :key="i"
                            :to="{
                                name: 'Yidian',
                                query: {
                                    title: it
                                }
                            }"
                        >
                            {{ it }}
                        </Link>
                    </div>
                </li>
            </ul>
        </div>
        <!-- 索引 -->
        <ul class="entityList w-15px bg-white">
            <li
                v-for="(item, index) in entityList"
                :key="index"
                :data-index="index"
                class="w-3 text-4 h-3 mb-1 leading-3 text-center text-gray6"
                @touchstart="onShortcutStart"
                @touchmove.stop.prevent="onShortcutMove"
                :class="{ current: currentIndex === index }"
            >
                {{ item.key }}
            </li>
        </ul>
    </div>
</template>
<script>
import BScroll from '@better-scroll/core';
import MouseWheel from '@better-scroll/mouse-wheel';
BScroll.use(MouseWheel);
export default {
    data() {
        return {
            currentIndex: 0,
            listHeight: [],
            entityList: [
                {
                    key: 'A',
                    list: ['氨基酸代谢病', '广泛性发育障碍']
                },
                {
                    key: 'B',
                    list: ['巴特综合征', '包涵体性结膜炎', '膀胱外翻', '鼻腔结外型NK/T细胞淋巴瘤']
                },
                {
                    key: 'C',
                    list: ['C5功能不全综合征', '肠道蛔虫症', '喘息样支气管炎']
                },
                {
                    key: 'D',
                    list: ['低氯性氮质血症综合征', '石棉状糠疹', 'Dravet综合征']
                },
                {
                    key: 'E',
                    list: ['耳聋', '儿童癫痫', '儿童头痛', '儿童急性中耳炎']
                },
                {
                    key: 'F',
                    list: ['腹肌缺如综合征', '肥大性神经病', '肺缺如', '樊尚咽峡炎', '腹壁疝']
                }
            ],
            scrollY: -1
        };
    },
    mounted() {
        this.touch = {};
        this.$nextTick(() => {
            this.initBanner();
        });
    },
    methods: {
        //初始化scroll
        initBanner() {
            if (this.scroll && this.scroll.destroy) {
                this.scroll.refresh();
                this.scroll.destroy();
            }
            this.scroll = new BScroll('.wrapper', {
                scrollY: true,
                click: true,
                mouseWheel: true,
                disableMouse: false, //启用鼠标拖动
                disableTouch: false, //启用手指触摸
                probeType: 3
            });
            this._calculateHeight();
            this.scroll.on('scroll', pos => {
                console.log(pos.y);
                this.scrollY = pos.y;
            });
        },
        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);

            // 计算最后的位置
            let index = this.touch.anchorIndex * 1 + delta;
            //注意这里需要判断边界,不然拖动到顶部和底部会报错
            if (index >= 0 && index <= this.entityList.length - 2) {
            this.scroll.scrollToElement(this.$refs.listGroup[index]);
            }
        },
        //计算索引内容高度
        _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);
            }
        }
    },
    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;
        }
    }
};
</script>

<style scoped lang="postcss">
.tabActive {
    @apply font-bold;
}
.tabActive::after {
    content: '';
    display: block;
    width: 18px;
    height: 3px;
    background: #00c2b0;
    border-radius: 2px;
    position: absolute;
    bottom: 0;
}
.sortActive {
    color: #00c2b0;
}
.select-left {
    @apply w-110px;
}
.select-left-item {
    @apply pl-15px h-42px text-sm;
}
.entityList {
    position: fixed;
    right: 8px;
    top: 156px;
}
.current {
    border-radius: 50%;
    background: #00beb0;
    color: #fff;
}
.typeAct {
    @apply bg-white text-primary;
}
</style>

总结

  • 参考了很多网上的资料,相对于原生实现,better-scroll带来了更大的便利,但是同时也需要我们对better-scroll有一定的了解。

参考文献

better-scroll官方文档

参考博客

到此这篇关于Vue+better-scroll 实现通讯录字母索引的文章就介绍到这了,更多相关Vue+better-scroll 实现通讯录字母索引内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

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

    vue+ better-scroll 实现移动端歌手列表字母索引导航.算是一个学习笔记吧,写个笔记让自己了解的更加深入一点. Demo:list-view,使用 chrome 手机模式查看.换成手机模式之后,不能滑动的话,刷新一下就 OK 了. Github: 移动端字母索引导航 效果图 配置环境 因为用到的是 vue-cli 和 better-scroll,所以首先要安装 vue-cli,然后再 npm 安装better-scroll. 简单介绍一下 better-scroll: better

  • Vue+better-scroll 实现通讯录字母索引的示例代码

    目录 环境准备: 实现步骤: 数据结构 基本HTML 使用better-scroll实现内容区列表的滚动 给索引添加点击事件和移动事件实现跳转 给索引添加高亮 全部代码 总结 参考文献 如图,实现一个滚动内容区域,右侧字母滚动索引定位:选择和拖动字母索引,对应内容滚动到视窗 环境准备: 安装better-scroll npm包 安装 mouseWheel 扩展 BetterScroll 鼠标滚轮的能力,开启鼠标滚动(移动端非必须) npm install @better-scroll/core

  • vue实现公告栏文字上下滚动效果的示例代码

    本文详细的介绍了vue实现公告栏文字上下滚动效果的示例代码,分享给大家,具体入如下: 代码实现: 在项目结构的components中新建text-scroll.vue文件 <template> <div class="text-container"> <transition class="" name="slide" mode="out-in"> <p class="text

  • vue中el-upload上传图片到七牛的示例代码

    一.思路,从后台获取七牛token,上传图片到七牛,获取返回图片路径放入el-upload. 二.代码. <el-input v-model="listVideoQuery.orgLogo" @change="orgLogoChange"></el-input> <el-col :span="10" class="mt10"> <el-upload class="upload

  • vue ssr+koa2构建服务端渲染的示例代码

    之前做了活动投放页面在百度.360等渠道投放,采用 koa2 + 模版引擎的方式.发现几个问题 相较于框架开发页面效率较低,维护性差 兼容性问题,在页面中添加埋点后发现有些用户的数据拿不到,排查后发现通过各个渠道过来的用户的设备中仍然包含大量低版本的浏览器. 服务端渲染 服务端渲染和单页面渲染区别 查看下面两张图,可以看到如果是服务端渲染,那么在浏览器中拿到的直接是完整的 html 结构.而单页面是一些 script 标签引入的js文件,最终将虚拟dom去挂在到 #app 容器上. @vue/c

  • vue项目中使用bpmn-自定义platter的示例代码

    内容概述 本系列"vue项目中使用bpmn-xxxx"分为七篇,均为自己使用过程中用到的实例,手工原创,目前陆续更新中.主要包括vue项目中bpmn使用实例.应用技巧.基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下.如果转载或通过爬虫直接爬的,格式特别丑,请来原创看:我是作者原文 前情提要 经过前四篇的学习,我们能够实现bpmn基本绘图.预览.为节点加事件加颜色等效果,这一篇我们来说,如何自定义左侧工具栏(platter),首先看一下自定义前后效果图对比: 我

  • vue实现导航菜单和编辑文本的示例代码

    导航菜单实例 <div id="main"> <!-- 激活的菜单样式为 active 类 --> <!-- 为了阻止链接在点击时跳转,我们使用了 "prevent" 修饰符 (preventDefault 的简称). --> <nav v-bind:class="active" v-on:click.prevent> <!-- 当菜单上的链接被点击时,我们调用了 makeActive 方法,

  • vue移动端写的拖拽功能示例代码

    相关知识点 touchstart 当在屏幕上按下手指时触发 touchmove 当在屏幕上移动手指时触发 touchend 当在屏幕上抬起手指时触发 mousedown mousemove mouseup对应的是PC端的事件 touchcancel 当一些更高级别的事件发生的时候(如电话接入或者弹出信息)会取消当前的touch操作,即触发 touchcancel.一般会在touchcancel时暂停游戏.存档等操作. 效果图 实现步骤html 总结了一下评论,好像发现大家都碰到了滑动的问题.就在

  • vue 使用class创建和清除水印的示例代码

    页面添加水印的方法有很多,以下举例使用class定义的方法进行水印内容渲染: 1.新建文件:WatermarkClass.js export default class WatermarkClass { constructor({id="watermarkID", str = "", fontSize = 18, width = 400, height = 400, fillStyle="#333333", opacity = 1 }) { th

  • vue实现禁止浏览器记住密码功能的示例代码

    查找资料 网上查到的一些方法: 使用 autocomplete="off"(现代浏览器许多都不支持) 使用 autocomplete="new-password" 在真正的账号密码框之前增加相同 name 的 input 框 使用 readonly 属性,在聚焦时移除该属性 初始化 input 框的 type 属性为 text,聚焦时修改为 password 使用 type="text",手动替换文本框内容为星号 "*" 或者

随机推荐