关于iscroll以及根据iscroll实现下拉刷新、下拉加载更多效果

一、关于iscroll

1、安装iscroll :npm install iscroll@5.2.0

        !important:

        首先注意iscroll内每一个滚动元素都需要设定高度,不能设定滚动元素的宽度使高度自适应,否则iscroll会无法滚动到底部,底部总有一部分无法显示。

        其次注意iscroll要滚动,容器的高度必须要小于内容的高度,在引入iscroll滚动组件时,父组件必须在iscroll滚动组件外套一层容器,我一般是使用div内嵌套scrollView组件。div属性设置为:

div{ // 容器的高度固定。
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    overflow: hidden;
}

在div属性设置完毕,注意在iscroll滚动组件中,设置iscroll组件宽高同容器一致。 

2、封装滚动组件

<template>
  // 必要的三层结构的其中两层,另一层在父组件中
  <div id="wrapper" ref="wrapper">
    <slot></slot>
  </div>
</template>

<script>
import IScroll from 'iscroll/build/iscroll-probe'
export default {
  name: 'ScrollView', // iscroll滚动组件命名为ScrollView
  mounted() {
    this.iscroll = new IScroll(this.$refs.wrapper, {
      onmousewheel: true, // 禁止鼠标滚动
      scrollbars: false, // 是否需要显示滚动条
      probeType: 3, // 监听iscroll滚动的灵敏程度,3是最灵敏的。
      // 解决卡顿拖拽问题
      scrollX: false,
      ScrollY: true,
      disablePointer: true,
      disableTouch: false,
      disableMouse: true, 
      // 另外想进一步解决卡顿拖拽,在全局样式中的 html,body{} 中添加 touch-action:none;

      click: true // iscroll滚动之后可以对元素绑定click事件
    })

    // 1.创建一个观察者对象
    /*
    MutationObserver构造函数只要监听到了指定内容发生变化,就会执行传入的回调函数
    !注意: MutationObserver是微任务
    mutationList:发生变化的数组
    observer:观察者对象
    */
    const observer = new MutationObserver((mutationList, observer) => {
      // console.log(mutationList)
      // console.log(this.iscroll.maxScrollY)//maxScrollY即滚动范围
      this.iscroll.refresh()
      // console.log(this.iscroll.maxScrollY)
    })
    // 2.告诉观察者对象我们需要观察什么内容
    const config = {
      childList: true, // 观察目标子节点的变化,添加或者删除
      subtree: true, // 默认为false,设置为true可以观察后代节点
      attributeFilter: ['height', 'offsetHeight'] // 观察特定属性
    }
    // 3.告诉观察者对象,我们需要观察谁,需要观察什么内容
    /*
    第一个参数:告诉观察者对象我们需要观察谁
    第二个参数:告诉观察者对象我们需要观察什么内容
    */
    observer.observe(this.$refs.wrapper, config)
  },
  methods: {
    // 提供一个监听滚动距离的方法给外界使用
    scrolling(fn) {
      this.iscroll.on('scroll', function() {
        fn(this.y)
      })
    },
    refresh() {
      setTimeout(() => {
        this.iscroll.refresh()
      }, 100)
    },
    scrollTo(x, y, time) {
      this.iscroll.scrollTo(x, y, time)
    }
  }
}
</script>

<style lang="scss" scoped>
#wrapper { // 设置iscroll组件的宽高同容器一致。
  width: 100%;
  height: 100%;
}
</style>

二、根据iscroll实现下拉刷新、下拉加载更多效果

<template>
  <div id="wrapper" ref="wrapper">
    <div
      class="scroll-wrapper"
      ref="scrollWrapper"
      :class="{ transition: isTransition }"
    >
      // 下拉刷新
      <div class="pull-down-box" v-if="this.value && this.isPullDownShow">
        <div class="pull-down">
          <img v-lazy="isShrinked ? refreshImg : loadImg" alt="" />
        </div>
      </div>

      <div class="content">
        <slot name="content"></slot>
      </div>
      // 上拉加载更多效果
      <div class="pull-up-box" v-if="this.value && this.isPullUpShow">
        <div class="pull-up">
          <img v-lazy="loadImg" />
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import IScroll from 'iscroll/build/iscroll-probe'
export default {
  name: 'ScrollView',
  props: {
    value: {
      type: Boolean,
      default: false,
      required: false
    }
  },
  methods: {
    refresh () {
      setTimeout(() => {
        this.iscroll.refresh()
      }, 100)
    },
    scrolling (fn) {
      this.iscroll.on('scroll', function () {
        fn(this.y)
      })
    },
    scrollTo (x, y, time) {
      this.iscroll.scrollTo(x, y, time)
    }, // 最好不要刚给scrollTo传递时间,可能是因为scrollTo内部是setInternal?传递时间后重新滚动有bug
    scrollEnd (fn) {
      this.iscroll.on('scrollEnd', function () {
        fn()
      })
    }
  },
  data () {
    return {
      isPullDownShow: false,
      isPullUpShow: false,
      pageHeight: 0, // 可视高度
      contentHeight: 0, // 页面元素高度
      isShrinked: true, // 选择下移标识图片
      loadImg:
        'https://img.lanrentuku.com/img/allimg/1212/5-121204193R5-50.gif',
      refreshImg:
        'https://s.cn.bing.net/th?id=OIP.fDBrKAm7YOFweVMG2A0VqgAAAA&w=183&h=183&c=7&qlt=90&bgcl=ececec&o=6&pid=PersonalBing',
      isTransition: false,
      isPullDownData: false, // 下拉是否达到进行刷新数据的状态
      isPullUpData: false, // 上拉是否达到加载数据的状态
      isBackTop: true // 当上拉加载数据之后是否需要返回顶部
    }
  },
  mounted () {
    this.iscroll = new IScroll(this.$refs.wrapper, {
      onmousewheel: true,
      scrollbars: false,
      probeType: 3,
      scrollX: false,
      ScrollY: true,
      disablePointer: true,
      disableTouch: false,
      disableMouse: true,
      click: true
    })

    const scrollWrapper = document.querySelector('.scroll-wrapper')
    const contentWrapper = document.querySelector('.content')
    // 1.创建一个观察者对象
    const observer = new MutationObserver((mutationList, observer) => {
      this.iscroll.refresh()
      scrollWrapper.style.marginTop = '0px'


      // console.log(
      //   'MutationObserver中',
      //   this.value,
      //   '父组件v-model给子组件的值为true,同时在异步async方法和同步方法中设置v-model的数据的值为false,在MutationObserver中为false,但是且无论是同步方法还是异步async方法设置的false,而MutationObserver外都是最开始传递的v-model值true?'
        // 解答:关于在MutationObserver中为false,原因是MutationObsernver 是微任务。但是疑问还是??...继续学习...猜想和v-model有关。
      // )

      // 获取实际高度(包括滚动距离)
      // 必须在此处获取:因为MutationObserver中时时监听节点变化,在MutationObserver外获取是固定值,则会无限执行下列if条件内的事件
      this.contentHeight = scrollWrapper.offsetHeight
      if (this.isPullUpShow) {
        if (this.contentHeight < this.pageHeight) {
          // if (scrollWrapper.offsetHeight < this.pageHeight) {
          // 页面元素不铺满一页
          this.$emit('load')
          // console.log(this.contentHeight, this.pageHeight)
        }
      }
      if (scrollWrapper.offsetHeight < 1) {
        console.log('页面无任何元素')
      }
    })
    // 2.告诉观察者对象我们需要观察什么内容
    const config = {
      childList: true,
      subtree: true,
      attributeFilter: ['height', 'offsetHeight']
    }
    // 3.告诉观察者对象,我们需要观察谁,观察什么对象
    observer.observe(this.$refs.wrapper, config)

    // console.log('MutationObserver外', this.value) // true
    // this.value为true表示是加载模式,即排除之前设置的三联选择器模式
    if (this.value) {
      // 获得页面的可视高度
      const pageDom = document.querySelector('#wrapper')
      this.pageHeight = Math.ceil(pageDom.getBoundingClientRect().height)

      const that = this
      this.iscroll.on('scroll', function () {
        // 下拉刷新和上拉加载操作
        if (this.y > 200) {
          // 关于下拉刷新
          if (that.isPullDownShow) {
            scrollWrapper.style.marginTop = this.y + 'px'
            that.isShrinked = false
            // 获取下拉更新数据
            that.isPullDownData = true
          }
        } else if (this.y < 0) {
          // 上拉加载模式
          if (that.isPullUpShow) {
            scrollWrapper.style.marginTop = 0 + 'px'
            that.isShrinked = true
            that.isTransition = true
            if (Math.abs(this.y) + that.pageHeight > that.contentHeight) {
              // 到达滚动条底部,获取上拉加载数据
              that.isPullUpData = true
              // console.log(that.isPullUpShow)
            }
          }
        }
      })
      this.iscroll.on('scrollEnd', function () {
        if (that.isPullDownData) {
          // console.log('scrollEnd下拉刷新', that.isPullDownData)
          // 执行下拉刷新操作
          that.$emit('refresh')
          // 执行完毕下拉刷新操作之后,恢复原先状态
          that.isPullDownData = false
        } else if (that.isPullUpData) {
          // console.log('scrollEnd上拉加载', that.isPullUpData)
          // 执行上拉加载操作
          that.$emit('load')

          // 加载完成之后附加的说明段落语句
          const contentOver = document.createElement('p')
          contentOver.innerHTML = '加载完成'
          contentOver.style.fontSize = 'larger'
          contentOver.style.textAlign = 'center'
          contentOver.style.color = 'black'

          contentWrapper.appendChild(contentOver)
          setTimeout(function () {
            contentWrapper.removeChild(contentOver)
          }, 1000)

          // 执行完毕上拉加载操作之后,恢复原先状态
          that.isPullUpData = false
        }
      })
    }
  },
  created () {
    if (this.$listeners.load) {
      this.isPullUpShow = true
      this.$emit('load')
    }
    if (this.$listeners.refresh) {
      this.isPullDownShow = true
    }
  }
}
</script>

<style lang="less" scoped>
#wrapper {
  width: 100%;
  height: 100%;
  .scroll-wrapper {
    width: 100%;
    position: relative;
    &.transition {
      transition: top 0.5s ease-in 0s;
    }
    .content {
      &::after {
        content: '-';
        display: inline-block;
        color: #fff;
        width: 100%;
        height: 30px;
      }
    }
    .pull-down,
    .pull-up {
      position: absolute;
      margin-left: 50%;
      transform: translateX(-50%);
      top: -35px;
      img {
        width: 20px;
        height: 20px;
      }
    }
    .pull-up {
      width: 100px;
      top: unset;
      bottom: 3px;
      text-align: center;
    }
    .textOver {
      background: red;
      position: absolute;
      left: 0;
      bottom: 0;
    }
  }
}
</style>

// 要点一:使用该组件的下拉刷新上拉加载功能,必须传入一个model,即该组件会询问想要使用该功能的用户:是否是上拉加载下拉刷新模式,这时应该传入是的答案。
即在父组件中给子组件传入 v-model='参数',而参数在父组件中答案为“是”,子组件中的props用value接收 
// 要点二:使用该组件的父组件给该组件传递上拉加载办法=> load,以及下拉刷新方法=> refresh 。 
// 未解决的问题,若前文this.iscroll.scrollTo(0,0)执行,当上拉数据加载之后iscroll会滚回顶部,而该功能不需要滚回顶部
// 注意iscroll中的是具名插槽。
// 关于MutationObserver是微任务,需要进一步了解js的执行机制。

因为本人菜,本文有很多不足,如果有大佬指出,非常感谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值