美文网首页Vue.js专区Vue.js开发技巧vue+js+react
Vue div节点滚动事件-加载更多

Vue div节点滚动事件-加载更多

作者: 6fa986559971 | 来源:发表于2018-06-25 10:53 被阅读41次

    使用Vue.directive注册全局指令

    Vue.directive('scroll', {})
    

    绑定事件

    Vue.directive('scroll', {
      bind: (el, binding, vnode) => {
        
      }
    })
    

    对于Vue自定义指令不明白的同学请移步: Vue自定义指令

    编写逻辑

    Vue.directive('scroll', {
      bind: (el, binding, vnode) => {
        // 距离底部剩余距离开始触发回调
        let distance = 100 // (unit: px)
        // 监听滚动事件
        el.onscroll = (e) => {
            // 获取当前节点可滚动的距离   节点滚动条总高度 - 节点本身高度
           let scrollHeight = e.target.scrollHeight - e.target.offsetHeight
           // 获取节点剩余可滚动的高度   可滚动距离  -  已经滚动的距离
           let residualHeight = scrollHeight - e.target.offsetTop
          //  滚动到指定区域执行回调事件
           if ((typeof binding.value === 'function') && residualHeight < distance) {
            // 执行事件回调函数   如果不明白此处的binding.value的同学请点击上面的链接,自行去官方查看
            binding.value()
          } 
        }
      }
    })
    

    阻止回调多次执行

    以上代码以及完全实现了加载更多的功能, 但是有个小问题,就是当滚动到 distance 范围之内后就会不断的执行回调, 而有的时候我们只需要执行一次, 其实很简单,请看下面代码, 注意在代码中出行的 eventAction 变量

    Vue.directive('scroll', {
      bind: (el, binding, vnode) => {
        // 是否执行回调事件
        let eventAction = true
        // 距离底部剩余距离开始触发回调
        let distance = 100 // (unit: px)
        // 监听滚动事件
        el.onscroll = (e) => {
            // 获取当前节点可滚动的距离   节点滚动条总高度 - 节点本身高度
           let scrollHeight = e.target.scrollHeight - e.target.offsetHeight
           // 获取节点剩余可滚动的高度   可滚动距离  -  已经滚动的距离
           let residualHeight = scrollHeight - e.target.offsetTop
          //  滚动到指定区域执行回调事件
           if ((typeof binding.value === 'function') && residualHeight < distance && eventAction) {
            // 执行事件回调函数   如果不明白此处的binding.value的同学请点击上面的链接,自行去官方查看
            binding.value()
            eventAction = false
          }  else if (residualHeight > distance) {
            eventAction = true
          }
        } 
      }
    })
    

    以上代码就可完全正常的工作了, 只需要将其放入Vue脚手架的main.js里面就可以全局使用
    使用方法:

    <template>
      <div class="result">
        <div class="search" @click="jump({name: 'search', params: {kw: kw}}, false, true)">
          <div class="search-box">
            <div class="input">
              <input type="text" :disabled="true" :value="kw">
            </div>
            <div class="icon font">&#xe61d;</div>
          </div>
        </div>
        <div class="goods" ref="goods" v-scroll="scroll">
          <div
            v-for="(item,idx) in items"
            @click="jump({name: 'product.detail', params: {id: item.id}})"
            :style="{width: itemWidth + 'px', height: (itemWidth + 60) + 'px'}"
            :key="'goods-item-' + idx" class="item">
            <img
              v-lazy="imageUrl + item.thumb"
              :onerror="'this.src=\'' + require('@assets/banner/1.jpg') + '\''"
              :style="{width: itemWidth + 'px', height: itemWidth + 'px'}">
            <div class="item-info">
              <div class="item-title">{{item.title}}</div>
              <div class="price-views">
                <div class="price">¥{{(Number(item.price) / 100).toFixed(2)}}</div>
                <div class="views">
                  &nbsp;{{item.views}}<div class="font">&#xe610;</div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </template>
    <script>
    export default {
      data () {
        return {
          imageUrl: this.config('imageUrl'),
          itemWidth: 0,
          items: [],
          page: 1,
          size: 20,
          kw: ''
        }
      },
      methods: {
        scroll () {
          this.getData()
        },
        /**
         * @purpose 获取数据
         */
        getData () {
          this.showLoading()
          this
            .$api
            .goods
            .search(this.kw, this.page, this.size)
            .then(r => {
              if (!(r.list instanceof Array) || r.list.length < 0) {
                return false
              }
              this.items = this.items.concat(r.list)
              this.page++
            })
        }
      },
      mounted () {
        this.itemWidth = (this.$refs.goods.clientWidth - 2) / 2
      },
      created () {
        let kw = this.$route.params.kw
        if (kw) {
          this.kw = kw
          this.getData()
        } else {
          this.toast('缺少搜索关键词!', 2, () => {
            this.jump(-1)
          })
        }
      }
    }
    </script>
    <style scoped lang="sass">
      .result
        width: 100%
        height: 100%
        .search
          width: 100%
          height: 50px
          background-color: #ffffff
          display: flex
          align-items: center
          justify-content: center
          z-index: 100
          .search-box
            width: calc(100% - 20px)
            height: 30px
            display: flex
            align-items: center
            justify-content: center
            background-color: #ebebeb
            border-radius: 20px
            .input
              flex: 1
              height: 100%
              background-color: transparent
              input
                width: calc(100% - 1rem)
                height: 100%
                font-size: 1rem
                background-color: transparent
                margin-left: 1rem
                color: #393a3f
            .icon
              width: 40px
              height: 40px
              font-size: 1rem
              font-weight: bold
              color: #393a3f
              display: flex
              align-items: center
              justify-content: center
              margin-right: .2rem
        .goods
          width: 100%
          height: calc(100% - 51px)
          overflow-x: hidden
          overflow-y: auto
          overflow-scrolling: touch
          .item
            background-color: #ffffff
            margin-bottom: 2px
            float: left
            &:nth-child(2n)
              margin-left: 2px
            .item-info
              width: calc(100% - 10px)
              height: 60px
              padding: 0 5px
              .item-title
                height: 30px
                font-size: .8rem
                line-height: 15px
                overflow: hidden
                text-overflow: ellipsis
                display: -webkit-box
                -webkit-line-clamp: 2
                -webkit-box-orient: vertical
                word-break: break-all
              .price-views
                width: 100%
                height: 30px
                display: flex
                align-items: center
                .price
                  flex: 1
                  height: 100%
                  font-size: .8rem
                  color: #ff4700
                  display: flex
                  align-items: center
                .views
                  flex: 1
                  height: 100%
                  font-size: .6rem
                  color: #666666
                  display: flex
                  align-items: center
                  flex-direction: row-reverse
    </style>
    

    相关文章

      网友评论

        本文标题:Vue div节点滚动事件-加载更多

        本文链接:https://www.haomeiwen.com/subject/yxweyftx.html