Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”

TIP ๐Ÿ‘‰ carefully planted flowers do not hair, leisurely inserted liuliu chengyin. Yuan ยท Guan Hanqing, “Lu Zhailang”

preface

In our daily project development, we will be involved in the function of the scroll list, so encapsulate the scroll list component.

Scroll list component

attribute

PullDownRefresh – pullDownRefresh
  • Value types: Boolean | Object
  • Default value:
{
threshold: flexible.rem2px(100 / 75), // The pull-down distance to trigger the refresh
stop: flexible.rem2px(90 / 75) // The distance of the bounce hover
}
Copy the code
PullUpLoad – Pull-up load
  • Value types: Boolean | Object
  • Default value: true
  • Example values of the Object type:
{
    threshold: 0 // Indicates the threshold for triggering the pull-up event
}
Copy the code
ScrollConfig – Scrollbar component configuration
  • Value type: Object
  • Default value:
{
wrapperBgColor: '#F5F5F5'.// Background color of scroll bar wrapper
bounce: true.// Displays a rebound animation
bounceTime: 800.// The animation length of the rebound animation (in milliseconds)
useTransition: false.// Use requestAnimationFrame to animate
observeDOM: true // Enable detection of DOM changes
}
Copy the code

For details, see the Scroll component

The event

1. Refresh – Refresh data (this event is triggered after the component is created successfully or after the top of the list is pulled down)

Parameters:

  • Callback: callback method after successfully refreshing data
    • Method parameters: status Data status, SUCCESS (success), no-data(no data), no-more(no more data), fail(fail)

ใ€ Attention ใ€‘ :

  • If there is pagination, the listener for this event should query the data on the first page, and the queried data should replace the current data array rather than append to it
  • After the component is successfully created, a Refresh event is automatically triggered
2. Load – Load data (this event is triggered when scrolling to the bottom of the list and pulling up)

Parameters:

  • Callback: callback method after successfully refreshing data
    • Method parameters: status Data status, SUCCESS (no more data), fail(failed)

ใ€ Attention ใ€‘ :

  • If there is pagination, the listener should query the next page of data and append the data to the data array

The sample

<template>
  <ScrollList @refresh="handleRefresh" @load="handleLoad">
    <ul class="data-list" v-if="dataList.length > 0">
      <li class="item" v-for="item in dataList" :key="item.id">
        <div class="title">{{item.title}}</div>
        <div class="date">{{item.articleDate}}</div>
      </li>
    </ul>
  </ScrollList>
</template>
<script>
import ScrollList from '@/components/m/scrollList'

export default {
  components: {
    ScrollList
  },
  data () {
    return {
      pageSize: 10.// Size per page
      pageNo: 0./ / page
      count: 0.// Total number of data
      dataList: [].// List data
      isLoading: false // Whether the file is being loaded}},methods: {
    // Get list data
    getList (pageNo) {
      if (this.isLoading) {
        return
      }
      this.isLoading = true
      pageNo = pageNo || this.pageNo + 1
      return this.$api.post({
        url: '/api/article/list'.data: { pageSize: this.pageSize, pageNo }
      }, this).then(data= > {
        this.pageNo = data.page.pageNo
        this.count = data.page.count
        if (this.pageNo > 1) {
          this.dataList = [...this.dataList, ...data.page.list]
        } else {
          this.dataList = data.page.list
        }
        this.isLoading = false

        let status = 'success' // The query succeeded
        if (data.page.count === 0) {
          status = 'no-data' // There is no data yet
        } else if (data.page.count <= data.page.pageNo * this.pageSize) {
          status = 'no-more' // No more data
        }
        return status
      }, e= > {
        this.isLoading = false
        return 'fail' // Query failed})},// Refresh operation (a refresh event is automatically triggered when the page is initialized)
    handleRefresh (callback) {
      this.getList(1).then((status) = > {
        callback(status)
      })
    },
    // Load operation
    handleLoad (callback) {
      this.getList().then((status) = > {
        callback(status)
      })
    }
  }
}
</script>
Copy the code

Scroll.vue

<template>
  <Scroll v-if="config" ref="scroll" v-bind="config" @created="init">
    <div v-if="inited" class="pulldown-wrapper">
      <div v-show="beforePullDown" class="pulldown-tips">
        <span>Release to refresh</span>
      </div>
      <div v-show=! "" beforePullDown">
        <div v-show="isPullingDown" class="loading">
          <BaseSpinner spinner="bubbles" size="s"></BaseSpinner>
          <span class="loading-txt">Loading in...</span>
        </div>
        <div v-show=! "" isPullingDown" class="refresh-result">
          <span v-if="isFail">Refresh the failure</span>
          <span v-else>Refresh the success</span>
        </div>
      </div>
    </div>
    <div v-else-if="isIniting" class="init-tips">
      <div class="loading">
        <BaseSpinner spinner="bubbles" size="s"></BaseSpinner>
        <span class="loading-txt">Loading in...</span>
      </div>
    </div>
    <div v-else-if="isFail" class="error-tips">
      <div v-if=! "" isPullingDown" class="error-content" @click="emitRefresh">
        <Icon name="cry" class="error-icon"></Icon>
        <span>Query failed. Click Retry</span>
      </div>
    </div>

    <slot ref="list"></slot>

    <template v-if="inited">
      <div v-if="status === 'no-data'" class="no-data-tips">
        <div v-if=! "" isPullingDown" class="no-data-content">
          <Icon name="no-data" class="no-data-icon"></Icon>
          <span>Temporarily no data</span>
        </div>
      </div>
      <div v-else class="pullup-tips">
        <div v-if="status === 'no-more'" class="no-more-tips">
          <span>There's no more data</span>
        </div>
        <div v-else-if=! "" isPullUpLoad">
          <span v-if="isFail">Loading failed. Please pull up and try again</span>
          <span v-else>Pull up to load more</span>
        </div>
        <div v-else class="loading">
          <BaseSpinner spinner="bubbles" size="s"></BaseSpinner>
          <span class="loading-txt">Loading in...</span>
        </div>
      </div>
    </template>
  </Scroll>
</template>
<script>
import Scroll from '@/components/base/scroll'
import BaseSpinner from '@/components/base/spinner'

const defaultConfig = {
  wrapperBgColor: '#F5F5F5'.// Background color of scroll bar wrapper
  bounce: true.// Displays a rebound animation
  bounceTime: 800.// The animation length of the rebound animation (in milliseconds)
  useTransition: false.// Use requestAnimationFrame to animate
  observeDOM: true // Enable detection of DOM changes
}

export default {
  name: 'ScrollList'.components: {
    Scroll,
    BaseSpinner
  },
  props: {
    // Drop refresh
    pullDownRefresh: {
      type: [Boolean.Object].default: () = > {
        let threshold = 100
        let stop = 90
        if (window.lib && window.lib.flexible) {
          let flexible = window.lib.flexible
          threshold = flexible.rem2px(threshold / 75)
          stop = flexible.rem2px(stop / 75)}return {
          threshold: threshold, // The pull-down distance to trigger the refresh
          stop: stop // The distance of the bounce hover}}},// Pull up load
    /* Example object {threshold: 0 // The threshold for triggering pull-up events} */
    pullUpLoad: {
      type: [Boolean.Object].default: true
    },
    // Scroll bar component configuration
    scrollConfig: {
      type: Object.default: () = > { return {} }
    }
  },
  data () {
    return {
      config: null.inited: false.// Whether it has been initialized
      isIniting: false.// Whether initialization is in progress
      isFail: false.// Whether to fail
      status: ' '.// Current status: SUCCESS, no-data, no-more
      beforePullDown: true.// Whether to pull down before releasing the hand
      isPullingDown: false.// Whether the refresh is being pulled down
      isPullUpLoad: false // Whether the load is being pulled up}},watch: {
    scrollConfig (val) {
      this.config = this.getConfig()
    },
    pullDownRefresh (val) {
      this.config = this.getConfig()
    },
    pullUpLoad (val) {
      this.config = this.getConfig()
    }
  },
  created () {
    this.config = this.getConfig()
    // Trigger the refresh
    this.emitRefresh()
  },
  mounted () {
    // Set the minimum height of the content area to 1 pixel larger than the wrapper to ensure scrolling
    this.setContentMinHeight()
  },
  methods: {
    / / initialization
    init (bs) {
      bs.on('pullingDown'.this.pullingDownHandler)
      bs.on('pullingUp'.this.pullingUpHandler)
      // bs.on('scroll', e => { console.log('scroll') })
      // bs.on('scrollEnd', e => { console.log('scrollEnd') })
    },
    getConfig () {
      constconfig = { ... defaultConfig, ... this.scrollConfig }if (this.pullDownRefresh) {
        config.pullDownRefresh = this.pullDownRefresh
      }
      if (this.pullUpLoad) {
        config.pullUpLoad = this.pullUpLoad
      }
      return config
    },
    // Trigger the refresh
    emitRefresh () {
      if (this.isIniting) {
        return
      }
      this.isIniting = true
      this.$emit('refresh'.(status) = > {
        this.status = status
        this.isIniting = false
        if(status ! = ='fail') {
          this.inited = true
          this.isFail = false
        } else {
          this.isFail = true}})},// Drop down operation
    pullingDownHandler () {
      if (!this.inited || this.isPullingDown || this.isPullUpLoad) {
        if (!this.isPullingDown) {
          const bs = this.$refs.scroll && this.$refs.scroll.bs / / BetterScroll instance
          bs.finishPullDown()
        }
        return
      }

      this.beforePullDown = false
      this.isPullingDown = true
      this.$emit('refresh'.(status) = > {
        if(status ! = ='fail') {
          this.status = status
          this.isFail = false
        } else {
          this.isFail = true
        }
        this.isPullingDown = false
        const bs = this.$refs.scroll && this.$refs.scroll.bs / / BetterScroll instance
        if (bs) {
          bs.finishPullDown()
          setTimeout(() = > {
            this.beforePullDown = true
            bs.refresh()
            if (this.pullUpLoad) {
              bs.openPullUp(this.pullUpLoad)
            }
          }, this.config.bounceTime + 100)}})},// Pull up operation
    pullingUpHandler () {
      if (!this.inited || this.isPullingDown || this.isPullUpLoad ||
          this.status === 'no-data' || this.status === 'no-more') {
        const bs = this.$refs.scroll && this.$refs.scroll.bs / / BetterScroll instance
        if (bs) {
          bs.finishPullUp()
        }
        return
      }
      this.isPullUpLoad = true

      this.$emit('load'.(status) = > {
        if(status ! = ='fail') {
          this.status = status
          this.isFail = false
        } else {
          this.isFail = true
        }
        this.isPullUpLoad = false
        const bs = this.$refs.scroll && this.$refs.scroll.bs / / BetterScroll instance
        if (bs) {
          bs.finishPullUp()
          bs.refresh()
        }
      })
    },
    // Set the minimum height of the content area to 1 pixel larger than the wrapper to ensure scrolling
    setContentMinHeight () {
      const scroll = this.$refs.scroll
      const wrapperHeight = scroll.$refs.wrapper.getBoundingClientRect().height
      scroll.$refs.content.style.minHeight = (wrapperHeight + 1) + 'px'}}}</script>
<style lang="scss" scoped>
.pulldown-wrapper{
  position: absolute;
  width: 100%;
  padding: 20px;
  box-sizing: border-box;
  transform: translateY(-100%) translateZ(0);
  text-align: center;
  color: # 999;
}
.pulldown-tips {
  line-height: 110px;
}
.init-tips {
  padding-top: 20px;
}
.loading {
  display: flex;
  align-items: center;
  justify-content: center;
  color: # 999;
  .loading-txt {
    margin-left: 12px; }}.refresh-result {}

.no-data-tips {
  padding-top: 50px;
  text-align: center;
  color: # 999;
  .no-data-content {
    display: flex;
    flex-direction: column;
    .no-data-icon {
      font-size: 120px;
      margin-bottom: 20px; }}}.error-tips {
  padding-top: 40px;
  text-align: center;
  color: # 999;
  .error-content {
    display: flex;
    flex-direction: column;
    .error-icon {
      font-size: 120px;
      margin-bottom: 10px; }}}.pullup-tips {
  padding: 20px;
  text-align: center;
  color: # 999;
  .no-more-tips {
    padding-bottom: 30px; }}</style>
Copy the code

index.js

/ * * * * @ scrolling list component's https://github.com/ustbhuangyi/better-scroll * @ see https://better-scroll.github.io/docs/zh-CN/ * / import ScrollList from './ScrollList.vue' export default ScrollListCopy the code

“Feel free to discuss in the comments section.”

Hope to finish watching friends can give a thumbs-up, encourage once