【Vue】使用Vue+Element+BetterScroll实现横向无限滚动

5,565 阅读1分钟

实现效果

组件化BetterScroll

通过element-loading-background定义“加载中”的背景颜色

<template>
  <div
    class="my-better-scroll"
    ref="scroll"
    element-loading-background="rgba(0, 0, 0, 0.8)"
  >
    <div class="my-better-scroll__content">
      <slot></slot>
    </div>
    <button class="my-scroll-arrow" @click="arrowClick">
      <i class="el-icon-arrow-right"></i>
    </button>
  </div>
</template>

<script>
import BScroll from '@better-scroll/core'
import MouseWheel from '@better-scroll/mouse-wheel'
import ObserveDOM from '@better-scroll/observe-dom'

/*
开启对 content 以及 content 子元素 DOM 改变的探测。
当插件被使用后,当这些 DOM 元素发生变化时,将会触发 scroll 的 refresh 方法。
observe-dom 插件具有以下几个特性:

针对改变频繁的 CSS 属性,增加 debounce
如果改变发生在 scroll 动画过程中,则不会触发 refresh
*/
BScroll.use(ObserveDOM)
// mouseWheel 扩展 BetterScroll 鼠标滚轮的能力。
BScroll.use(MouseWheel)

/**
 * TODO 本组件仅定义为向右无限滚动
 * [当 better-scroll 遇见 Vue](https://zhuanlan.zhihu.com/p/27407024)
 * [API](https://better-scroll.github.io/docs/zh-CN/)
 */
export default {
  name: 'BetterScroll',
  methods: {
    /**
     * 当箭头被点击时,滚动一个组件宽度,以显示用户未看到的内容
     */
    arrowClick() {
      // TODO 仅适合向右滚动
      const scrollLength = this.scroll.x - this.$refs.scroll.clientWidth
      this.scroll.scrollTo(scrollLength)
      this.$emit('arrow')
    },
    init() {
      this.scroll = new BScroll(this.$refs.scroll, {
        click: true,
        scrollX: true,
        scrollY: false,
        observeDOM: true,
        mouseWheel: {
          // 滚动动画的缓动时长
          easeTime: 100,
          // 只要在 discreteTime 时间内没有探测到滚动,那么一次的滚轮动作就结束了。
          discreteTime: 100,
        },
      })
      this.scroll.scroller.hooks.on('scrollEnd', () => {
        // 滚动到底部
        if (this.scroll.x <= this.scroll.maxScrollX + 50) {
          this.$emit('end')
        }
      })
    },
  },
  mounted() {
    this.init()
  },
}
</script>

<style lang="scss" scoped>
.my-better-scroll {
  overflow: hidden;
  position: relative;
  $arrow-size: 12px;
  & > .my-better-scroll__content {
    display: inline-block;
    white-space: nowrap;
    padding-right: $arrow-size + 2;
    & > * {
      display: inline-block;
    }
  }
  & > .my-scroll-arrow {
    cursor: pointer;
    display: flex;
    position: absolute;
    flex-direction: column;
    justify-content: center;
    font-size: $arrow-size;
    border-radius: 20px;
    height: 100%;
    top: 0;
    right: 0;
    padding: 0;
    margin: 0;
    border-width: 0;
    & > i {
      font-weight: 800;
    }
    &:focus {
      outline: none;
    }
  }
}
</style>

使用

通过v-loading来实现延迟加载功能

<template>
  <BetterScroll @end="loadData" v-loading="loading">
    <div
      class="my-scroll-item"
      v-for="(item, index) in items"
      :key="index"
      @click="itemClick(item, index)"
    >
      <el-card>
        {{ item.text }}
      </el-card>
    </div>
  </BetterScroll>
</template>

<script>
import BetterScroll from '@/components/BetterScroll'

export default {
  name: 'MyScroll',
  components: {
    BetterScroll,
  },
  data() {
    return {
      items: [
        { text: '元素' },
        { text: '元素' },
        { text: '元素' },
        { text: '元素' },
        { text: '元素' },
        { text: '元素' },
        { text: '元素' },
        { text: '元素' },
        { text: '元素' },
      ],
      loading: false,
    }
  },
  methods: {
    loadData() {
      this.loading = true
      if (this.items.length < 15) {
        console.log('load start')
        setTimeout(() => {
          console.log('load end')
          for (let i = 0; i < 3; i++) {
            this.items.push({
              text: `元素${this.items.length}`,
            })
          }
          this.loading = false
        }, 1000)
      } else {
        this.loading = false
        this.loaded()
      }
    },
    loaded() {
      this.$message({
        message: '没有更多数据了',
        duration: 1500,
      })
    },
    itemClick(item, index) {
      console.log(item, index)
    },
  },
}
</script>



<style lang="scss" scoped>
.my-scroll-item {
  width: 80px;
  height: 80px;
}
</style>