• 使用BetterScroll封装页面滚动及轮播图组件(一文入门移动端页面滚动神器BetterScroll)


    目录

    一、轮播图组件的封装

    1. 安装 BeterScroll 及 Slide 插件

    2. 新建 slide.vue 文件

    3. 前往官网复制一个示例,做一个小 demo

    4. 钩子函数的应用

    二、滚动组件的封装

    1. 新建 scroll 组件

    2. 初始化BetterScroll

    3. 添加属性

    3. 安装并引入observe-dom


    一、轮播图组件的封装

    1. 安装 BeterScroll 及 Slide 插件

    1. npm install @better-scroll/core --save
    2. // or
    3. yarn add @better-scroll/core
    1. npm install @better-scroll/slide --save
    2. // or
    3. yarn add @better-scroll/slide

     这样就表示我们安装完成:

    2. 新建 slide.vue 文件

    在 components 目录下新建 slide/slide.vue 来编写轮播图相关代码

    3. 前往官网复制一个示例,做一个小 demo

    下面是 slide.vue 的代码:

    1. <template>
    2. <div class="slider" ref="rootRef">
    3. <div class="slider-group">
    4. <div
    5. class="slider-page"
    6. v-for="item in sliders"
    7. :key="item.id"
    8. >
    9. <img :src="item.pic"/>
    10. div>
    11. div>
    12. <div class="dots-wrapper">
    13. <span
    14. class="dot"
    15. v-for="(item, index) in sliders"
    16. :key="item.id"
    17. :class="{'active': currentPageIndex === index}">
    18. span>
    19. div>
    20. div>
    21. template>
    22. <script>
    23. import BScroll from '@better-scroll/core'
    24. import Slide from '@better-scroll/slide'
    25. import { onMounted, onUnmounted, onActivated, onDeactivated, ref } from 'vue'
    26. BScroll.use(Slide)
    27. export default {
    28. name: 'slider',
    29. props: {
    30. sliders: {
    31. type: Array,
    32. default() {
    33. return []
    34. }
    35. }
    36. },
    37. setup(props) {
    38. const rootRef = ref(null)
    39. const slider = ref(null)
    40. const currentPageIndex = ref(0)
    41. onMounted(() => {
    42. const sliderVal = slider.value = new BScroll(rootRef.value, {
    43. click: true,
    44. scrollX: true,
    45. scrollY: false,
    46. momentum: false,
    47. bounce: false,
    48. probeType: 2,
    49. slide: true
    50. })
    51. sliderVal.on('slideWillChange', (page) => {
    52. currentPageIndex.value = page.pageX
    53. })
    54. })
    55. onUnmounted(() => {
    56. slider.value.destroy()
    57. })
    58. onActivated(() => {
    59. slider.value.enable()
    60. slider.value.refresh()
    61. })
    62. onDeactivated(() => {
    63. slider.value.disable()
    64. })
    65. return {
    66. slider,
    67. currentPageIndex,
    68. rootRef
    69. }
    70. }
    71. }
    72. script>
    73. <style lang="less" scoped>
    74. .slider {
    75. position :relative;
    76. .slider-group {
    77. position: relative;
    78. overflow: hidden;
    79. white-space: nowrap;
    80. .slider-page {
    81. display: inline-block;
    82. transform: translate3d(0, 0, 0);
    83. backface-visibility: hidden;
    84. a {
    85. display: block;
    86. width: 100%;
    87. }
    88. img {
    89. display: block;
    90. width: 100%;
    91. }
    92. }
    93. }
    94. .dots-wrapper {
    95. position: absolute;
    96. left: 50%;
    97. bottom: 12px;
    98. line-height: 12px;
    99. transform: translateX(-50%);
    100. .dot {
    101. display: inline-block;
    102. margin: 0 4px;
    103. width: 8px;
    104. height: 8px;
    105. border-radius: 50%;
    106. background: rgba(255, 255, 255, 0.5);
    107. &.active {
    108. width: 20px;
    109. border-radius: 5px;
    110. background:rgba(255, 255, 255, 0.8);
    111. }
    112. }
    113. }
    114. }
    115. style>

    轮播的信息通过 props 从父组件传递过来,下面是父组件的代码:

    1. <template>
    2. <header>头部header>
    3. <div>
    4. <slide :sliders="list">slide>
    5. div>
    6. template>
    7. <script>
    8. import slide from '../components/slider/slider.vue'
    9. import { ref } from 'vue'
    10. export default {
    11. name: 'HomeView',
    12. components: {
    13. slide
    14. },
    15. setup () {
    16. const list = ref([
    17. {
    18. id: 1,
    19. pic: "https://7463-tcb-tjpv7cz5f6ef80-7dw7b0630091b-1312294860.tcb.qcloud.la/swiper/havetea.png"
    20. },
    21. {
    22. id: 2,
    23. pic: "https://7463-tcb-tjpv7cz5f6ef80-7dw7b0630091b-1312294860.tcb.qcloud.la/swiper/谨防诈骗.png"
    24. },
    25. {
    26. id: 3,
    27. pic: "https://7463-tcb-tjpv7cz5f6ef80-7dw7b0630091b-1312294860.tcb.qcloud.la/swiper/首页轮播1.png"
    28. }
    29. ])
    30. return {
    31. list
    32. }
    33. },
    34. }
    35. script>
    36. <style lang="less" scoped>
    37. header {
    38. height: 30px;
    39. width: 100%;
    40. background-color: pink;
    41. }
    42. style>

    启动项目,看一下我们的轮播图:

    4. 钩子函数的应用

    下面我们再使用 composition api 的方式创建一个钩子函数,把 new slide 的逻辑抽离出去:

    我们定义了一个 useSlider 函数,他传入的参数是轮播图的容器,这个是 betterScroll 必须的,返回的是当前页的索引值,下面我们看一下 use-slider.js 的代码:

    1. import { onMounted, onUnmounted, onActivated, onDeactivated, ref } from 'vue'
    2. import BScroll from '@better-scroll/core'
    3. import Slide from '@better-scroll/slide'
    4. BScroll.use(Slide)
    5. export default function useSlider(wrapperRef) {
    6. const slider = ref(null)
    7. const currentPageIndex = ref(0)
    8. onMounted(() => {
    9. const sliderVal = slider.value = new BScroll(wrapperRef.value, {
    10. click: true,
    11. scrollX: true,
    12. scrollY: false,
    13. momentum: false,
    14. bounce: false,
    15. probeType: 2,
    16. slide: true
    17. })
    18. sliderVal.on('slideWillChange', (page) => {
    19. currentPageIndex.value = page.pageX
    20. })
    21. })
    22. onUnmounted(() => {
    23. slider.value.destroy()
    24. })
    25. onActivated(() => {
    26. slider.value.enable()
    27. slider.value.refresh()
    28. })
    29. onDeactivated(() => {
    30. slider.value.disable()
    31. })
    32. return {
    33. slider,
    34. currentPageIndex
    35. }
    36. }

    然后再看一下现在 slider.vue 的代码,就会感受到 composition api 的强大之处了:

    1. import { ref } from 'vue'
    2. import useSlider from './use-slider'
    3. export default {
    4. name: 'slider',
    5. props: {
    6. sliders: {
    7. type: Array,
    8. default() {
    9. return []
    10. }
    11. }
    12. },
    13. setup() {
    14. const rootRef = ref(null)
    15. const { currentPageIndex } = useSlider(rootRef)
    16. return {
    17. rootRef,
    18. currentPageIndex
    19. }
    20. }
    21. }

    这样处理之后,useSlider 函数只跟 slide 轮播图的创建有关,主逻辑部分很清晰。

    二、滚动组件的封装

    在我们移动端的开发中经常会遇到滚动列表的场景,此时我们用原生的滚动效果是没有回弹及一些其他对交互很友好的动画效果的,类似于这样:

    同样借助于 BetterScroll 我们可以轻松的封装一个自己的滚动组件来实现移动端下顺滑的滚动体验

    1. 新建 scroll 组件

    在 components 目录下新建 scroll / scroll.vue 文件,它的实现非常简单:

    1. <script>
    2. export default {
    3. name: 'scroll'
    4. }
    5. script>

    滚动的内容部分我们就放在 slot 插槽中,然后在外层的 div 盒子中通过 BetterScroll 做一些初始化的联动,让内容部分实现滚动。

    2. 初始化BetterScroll

    和上一讲的 slider 组件一样,我们使用 composition api 和 钩子函数的方式来和 BetterScroll 做初始化,所以我们新建 use-scroll.js 文件,代码如下所示

    1. import BScroll from '@better-scroll/core'
    2. import { onMounted, onUnmounted, ref } from 'vue'
    3. BScroll.use(ObserveDOM)
    4. export default function useScroll(wrapperRef) {
    5. const scroll = ref(null)
    6. onMounted(() => {
    7. scroll.value = new BScroll(wrapperRef.value)
    8. onUnmounted(() => {
    9. scroll.value.destroy()
    10. })
    11. }

    这里的 wrapperRef 就是最外层的 dom ,我们通过 ref 获取他然后作为参数传进来,下一步我们在 scroll 中引入这个钩子函数:

    1. <script>
    2. import useScroll from './use-scroll'
    3. import { ref } from 'vue'
    4. export default {
    5. name: 'scroll',
    6. setup() {
    7. const rootRef = ref(null)
    8. const scroll = useScroll(rootRef)
    9. return {
    10. rootRef
    11. }
    12. }
    13. }
    14. script>

    这样我们简单的 scroll 组件就实现了

    3. 添加属性

    因为 BetterScroll 是支持我们添加很多自定义属性和事件的,比如在官网中可以定义 click 点击事件,它的默认值是 false:

    现在我们就给我们的滚动组件添加这个 click 事件,这里的 click 我们想实现自定义配置就应该从使用 scroll 组件的父级传进来,那我们通过 props 接收他就好,然后把 props 作为参数传给需要用到他的钩子函数,这样不论 props 中添加了什么新的属性,钩子函数中都能获取到,因为都包含在 props 中,修改 scroll.vue 代码如下:

    1. <script>
    2. import useScroll from './use-scroll'
    3. import { ref } from 'vue'
    4. export default {
    5. name: 'scroll',
    6. props: {
    7. click: {
    8. type: Boolean,
    9. default: true
    10. }
    11. setup(props) {
    12. const rootRef = ref(null)
    13. useScroll(rootRef, props, emit)
    14. return {
    15. rootRef
    16. }
    17. }
    18. }
    19. script>

    值得注意的是, scroll 组件只对第一个子节点生效,这也是 BeterScroll 的特性。

    如果 BetterScroll 的时候,并没有实现滚动,可以看一下文档中的滚动原理

    BetterScroll 判断滚动是在初始化的时候就进行判断,也就是在 new BScroll 的时候进行计算,如果我们在 created 的时候获取列表数据,那显然很有可能我们在数据还没有获取的时候,BetterScroll 已经计算完了,这样页面就滚动不了。

    解决这个问题的方法就是 BetterScroll 的一个插件:observe-dom:

    这样当 我们的 dom 发生变化时,BetterScroll就能自动感知然后执行刷新操作。

    3. 安装并引入observe-dom

    1. npm install @better-scroll/observe-dom --save
    2. // or
    3. yarn add @better-scroll/observe-dom

     use-scroll.vue:

    1. import BScroll from '@better-scroll/core'
    2. import ObserveDOM from '@better-scroll/observe-dom'
    3. import { onMounted, onUnmounted, ref } from 'vue'
    4. BScroll.use(ObserveDOM)
    5. export default function useScroll(wrapperRef, options) {
    6. const scroll = ref(null)
    7. onMounted(() => {
    8. scroll.value = new BScroll(wrapperRef.value, {
    9. observeDOM: true,
    10. ...options
    11. })
    12. onUnmounted(() => {
    13. scroll.value.destroy()
    14. })
    15. }

    我们试一下使用 scroll.vue 组件后的滚动列表的效果,下面是 Home.vue 的代码:

    1. <template>
    2. <div style="position:relative;">
    3. <div class="head">固定的头部div>
    4. <div class="main">
    5. <scroll class="theScroll">
    6. <div>
    7. <div v-for="(i,index) in 10" :key="index" style="margin:10px 0">
    8. <van-card
    9. num="2"
    10. price="2.00"
    11. desc="描述信息"
    12. title="商品标题"
    13. thumb="https://fastly.jsdelivr.net/npm/@vant/assets/ipad.jpeg"
    14. />
    15. div>
    16. div>
    17. scroll>
    18. div>
    19. div>
    20. template>
    21. <script>
    22. import slide from '../components/slider/slider.vue'
    23. import scroll from '../components/scroll/scroll.vue'
    24. import { ref } from 'vue'
    25. export default {
    26. name: 'HomeView',
    27. components: {
    28. slide,
    29. scroll
    30. },
    31. setup () {
    32. const list = ref([
    33. {
    34. id: 1,
    35. pic: "https://7463-tcb-tjpv7cz5f6ef80-7dw7b0630091b-1312294860.tcb.qcloud.la/swiper/havetea.png"
    36. },
    37. {
    38. id: 2,
    39. pic: "https://7463-tcb-tjpv7cz5f6ef80-7dw7b0630091b-1312294860.tcb.qcloud.la/swiper/谨防诈骗.png"
    40. },
    41. {
    42. id: 3,
    43. pic: "https://7463-tcb-tjpv7cz5f6ef80-7dw7b0630091b-1312294860.tcb.qcloud.la/swiper/首页轮播1.png"
    44. }
    45. ])
    46. return {
    47. list
    48. }
    49. },
    50. }
    51. script>
    52. <style lang="less" scoped>
    53. .head {
    54. position: fixed;
    55. top: 0;
    56. left: 0;
    57. height: 50px;
    58. width: 100%;
    59. background-color: pink;
    60. z-index: 999;
    61. }
    62. .main {
    63. position: fixed;
    64. top: 50px;
    65. bottom: 0;
    66. left: 0;
    67. right: 0;
    68. background-color: rgb(232, 234, 235);
    69. .theScroll {
    70. height: 100%;
    71. overflow: hidden;
    72. }
    73. }
    74. style>

    启动项目,查看效果:

    现在我们就实现了带回弹效果的这样顺滑的滚动了。 

  • 相关阅读:
    分布式系统中的乐观和错误假设
    微信小程序渲染层与逻辑层交互原理
    dockers --cap-add 哪些值可以设置
    Redis的瓶颈在哪里?
    vue 预览zip
    电脑主机如何选择内存条
    Applied Time Series Analysis with R
    C++设计模式|结构型 代理模式
    Docker更新Springboot的部署
    读书笔记:《精益数据分析》
  • 原文地址:https://blog.csdn.net/qq_49900295/article/details/126291744