zoukankan      html  css  js  c++  java
  • better-scroll在vue中的使用

    一、介绍

      关于better-scroll的原文详细介绍请参考,这里只做总结

        黄老师的文章当 better-scroll 遇见 Vue》的详细介绍

        better-scroll的api:点击

      better-scroll的滚动原理

    <div class="wrapper">
      <ul class="content">
        <li>...</li>
        <li>...</li>
        ...
      </ul>
    </div>    

          

         绿色部分为 wrapper,也就是父容器,它会有固定的高度。黄色部分为 content,它是父容器的第一个子元素,它的高度会随着内容的大小而撑高。那么,当 content 的高度不超过父容器的高度,是不能滚动的,而它一旦超过了父容器的高度,我们就可以滚动内容区了,这就是 better-scroll 的滚动原理。
         better-scroll 的初始化时机很重要,因为它在初始化的时候,会计算父元素和子元素的高度和宽度,来决定是否可以纵向和横向滚动。因此,我们在初始化它的时候,必须确保父元素和子元素的内容已经正确渲染了。如果子元素或者父元素 DOM 结构发生改变的时候,必须重新调用 scroll.refresh() 方法重新计算来确保滚动效果的正常。

    二、better-scroll在vue中的使用

      Vue.js 提供了我们一个获取 DOM 对象的接口—— vm.$refs。在这里,我们通过了 this.$refs.wrapper 访问到了这个 DOM 对象,并且我们在 mounted 这个钩子函数里,this.$nextTick 的回调函数中初始化 better-scroll 。因为这个时候,wrapper 的 DOM 已经渲染了,我们可以正确计算它以及它内层 content 的高度,以确保滚动正常。

      这里的 this.$nextTick 是一个异步函数,为了确保 DOM 已经渲染,感兴趣的同学可以了解一下它的内部实现细节,底层用到了 MutationObserver 或者是 setTimeout(fn, 0)。其实我们在这里把 this.$nextTick 替换成 setTimeout(fn, 20) 也是可以的(20 ms 是一个经验值,每一个 Tick 约为 17 ms),对用户体验而言都是无感知的。

      

      这里从今日头条截取获取后台数据链接,用vue-jsonp来异步获取数据。

      安装vue-jsonp

    npm install vue-jsonp --save-dev

      在组件单独使用

    import Vue from 'vue'
    import VueJsonp from 'vue-jsonp'
    Vue.use(VueJsonp)

      详细请参考 vue-jsonp的npm地址:点击

    <template>
      <div>
         <nav class="nav">
          <ul>
            <li>推荐</li>
          </ul>
        </nav>
        <div class="wrapper" ref="wrapper">
          <div class="content" >
            <section class="has_action" v-for="item in data">
              <div class="item-detail">
                <h3 class="dotdot">{{item.title}}</h3>
                <div class="item-info">
                  <div>
                    <span class="stick_label space">{{item.label}}</span>
                    <span class="src space">{{item.media_name}}</span>
                    <span class="cmt space">评论{{item.comment_count}}</span>
                    <span class="time space" title="2018-11-05 19:23">{{item.datetime}}</span>
                  </div>
                </div>
              </div>
            </section>
          </div>
        </div>
      </div>
    </template>
    

      

    <script>
      import Vue from 'vue'
      import VueJsonp from 'vue-jsonp'
      import BScroll from 'better-scroll'
      Vue.use(VueJsonp)
      export default {
        name: 'myscroll',
        data() {
          return {
            data: []
          }
        },
        created() {
          this._getData().then(json => {
            this.data = json.data
            this.$nextTick(() => {
              this.scroll = new BScroll(this.$refs.wrapper,{})
            })
            
          })
        },
        methods: {
          // 或者页面数据,调用今日头条数据接口
          _getData() {
            return this.$jsonp('https://m.toutiao.com/list',{
              tag: '__all__',
              ac: 'wap',
              count: 20,
              format: 'json_raw',
              as: 'A1B57B7E600F6A7',
              cp: '5BE0AFC5FDAEAE1',
              min_behot_time: 1541469897,
              _signature: 'A6d5hAAAWEyp4NHR.YRHRAOneZ',
              i: 1541469650
            })
          }
        }
      }
    </script>

    注意:nav和wrapper这两个class的css定位属性

    <style scoped lang="stylus">
      .nav
        z-index: 999
        position: fixed
        display: block
        box-sizing: border-box
        height: 74px
         100%
        text-align: center
        line-height: 74px
        font-size: 20px
        color: #f85959
        background: #f4f5f6
      .wrapper
        position: fixed
        top: 74px
        left: 0
        bottom: 0
        right: 0
        overflow: hidden
        .content
          list-style: none
          .has_action
            position: relative
            margin: 0 30px
            border-bottom: 1px solid rgba(221, 221, 221, 0.6)
            .item-detail
              padding: 32px 0px;
              .dotdot
                overflow: hidden
                text-overflow: ellipsis
                line-height: 42px
                font-size: 34px
                font-weight: normal
                color: #222
              .item-info
                margin-top: 12px
                overflow: hidden
                font-size: 0
                color: #999
                .space
                  display: inline-block
                  margin-right: 10px
                  vertical-align: middle
                  line-height: 30px
                  font-size: 28px
                .stick_label
                  border-radius: 4px
                  border: 1PX solid rgba(248,89,89,.5)
                   60px
                  text-align: center
                  color: #f85959
    </style>
    View Code

      

       结果如下:

       

     三、scroll 组件的抽象和封装

      在components下面新建一个scroll文件夹->scroll.vue

      1 <template>
      2   <div ref="wrapper">
      3     <slot></slot>
      4   </div>
      5 </template>
      6 <script type="text/ecmascript-6">
      7   import BScroll from 'better-scroll'
      8   export default {
      9     props: {
     10       /**
     11        * 1 滚动的时候会派发scroll事件,会截流。
     12        * 2 滚动的时候实时派发scroll事件,不会截流。
     13        * 3 除了实时派发scroll事件,在swipe的情况下仍然能实时派发scroll事件
     14        */
     15       probeType: {
     16         type: Number,
     17         default: 1
     18       },
     19       /**
     20        * 点击列表是否派发click事件
     21        */
     22       click: {
     23         type: Boolean,
     24         default: true
     25       },
     26       /**
     27        * 是否开启横向滚动
     28        */
     29       scrollX: {
     30         type: Boolean,
     31         default: false
     32       },
     33       /**
     34        * 是否派发滚动事件
     35        */
     36       listenScroll: {
     37         type: Boolean,
     38         default: false
     39       },
     40       /**
     41        * 列表的数据
     42        */
     43       data: {
     44         type: Array,
     45         default: null
     46       },
     47       /**
     48        * 是否派发滚动到底部的事件,用于上拉加载
     49        */
     50       pullup: {
     51         type: Boolean,
     52         default: false
     53       },
     54       /**
     55        * 是否派发顶部下拉的事件,用于下拉刷新
     56        */
     57       pulldown: {
     58         type: Boolean,
     59         default: false
     60       },
     61       /**
     62        * 是否派发列表滚动开始的事件
     63        */
     64       beforeScroll: {
     65         type: Boolean,
     66         default: false
     67       },
     68       /**
     69        * 当数据更新后,刷新scroll的延时。
     70        */
     71       refreshDelay: {
     72         type: Number,
     73         default: 20
     74       }
     75     },
     76     mounted() {
     77       // 保证在DOM渲染完毕后初始化better-scroll
     78       setTimeout(() => {
     79         this._initScroll()
     80       }, 20)
     81     },
     82     methods: {
     83       _initScroll() {
     84         if (!this.$refs.wrapper) {
     85           return
     86         }
     87         // better-scroll的初始化
     88         this.scroll = new BScroll(this.$refs.wrapper, {
     89           probeType: this.probeType,
     90           click: this.click,
     91           scrollX: this.scrollX
     92         })
     93 
     94         // 是否派发滚动事件
     95         if (this.listenScroll) {
     96           let me = this
     97           this.scroll.on('scroll', (pos) => {
     98             me.$emit('scroll', pos)
     99           })
    100         }
    101 
    102         // 是否派发滚动到底部事件,用于上拉加载
    103         if (this.pullup) {
    104           this.scroll.on('scrollEnd', () => {
    105             // 滚动到底部
    106             if (this.scroll.y <= (this.scroll.maxScrollY + 50)) {
    107               this.$emit('scrollToEnd')
    108             }
    109           })
    110         }
    111 
    112         // 是否派发顶部下拉事件,用于下拉刷新
    113         if (this.pulldown) {
    114           this.scroll.on('touchend', (pos) => {
    115             // 下拉动作
    116             if (pos.y > 50) {
    117               this.$emit('pulldown')
    118             }
    119           })
    120         }
    121 
    122         // 是否派发列表滚动开始的事件
    123         if (this.beforeScroll) {
    124           this.scroll.on('beforeScrollStart', () => {
    125             this.$emit('beforeScroll')
    126           })
    127         }
    128       },
    129       disable() {
    130         // 代理better-scroll的disable方法
    131         this.scroll && this.scroll.disable()
    132       },
    133       enable() {
    134         // 代理better-scroll的enable方法
    135         this.scroll && this.scroll.enable()
    136       },
    137       refresh() {
    138         // 代理better-scroll的refresh方法
    139         this.scroll && this.scroll.refresh()
    140       },
    141       scrollTo() {
    142         // 代理better-scroll的scrollTo方法
    143         this.scroll && this.scroll.scrollTo.apply(this.scroll, arguments)
    144       },
    145       scrollToElement() {
    146         // 代理better-scroll的scrollToElement方法
    147         this.scroll && this.scroll.scrollToElement.apply(this.scroll, arguments)
    148       }
    149     },
    150     watch: {
    151       // 监听数据的变化,延时refreshDelay时间后调用refresh方法重新计算,保证滚动效果正常
    152       data() {
    153         setTimeout(() => {
    154           this.refresh()
    155         }, this.refreshDelay)
    156       }
    157     }
    158   }
    159 </script>
    View Code

      在my-scroll中使用scroll组件

    <template>
      <div>
         <nav class="nav">
          <ul>
            <li>推荐</li>
          </ul>
        </nav>
        <scroll class="wrapper" :data="data">
          <div class="content" >
            <section class="has_action" v-for="item in data">
              <div class="item-detail">
                <h3 class="dotdot">{{item.title}}</h3>
                <div class="item-info">
                  <div>
                    <span class="stick_label space">{{item.label}}</span>
                    <span class="src space">{{item.media_name}}</span>
                    <span class="cmt space">评论{{item.comment_count}}</span>
                    <span class="time space" title="2018-11-05 19:23">{{item.datetime}}</span>
                  </div>
                </div>
              </div>
            </section>
          </div>
        </scroll>
      </div>
    </template>
    View Code
    <script>
      import Vue from 'vue'
      import VueJsonp from 'vue-jsonp'
      import BScroll from 'better-scroll'
      import scroll from 'components/scroll/scroll'
      Vue.use(VueJsonp)
      export default {
        name: 'myscroll',
        components: {
          scroll
        },
        data() {
          return {
            data: []
          }
        },
        created() {
          this._getData().then(json => {
            this.data = json.data
            this.data = this.data.concat(this.data)
          })
        },
        methods: {
          // 或者页面数据,跳用今日头条数据接口
          _getData() {
            return this.$jsonp('https://m.toutiao.com/list',{
              tag: '__all__',
              ac: 'wap',
              count: 20,
              format: 'json_raw',
              as: 'A1D5EB0E82E44A1',
              cp: '5BE224C46AC11E1',
              min_behot_time: 1541555213,
              _signature: '.P8dTQAApydWuLUYyYBGu.z.HV',
              i: 1541555213
            })
          }
        }
      }
    </script>
    View Code

    github地址

  • 相关阅读:
    解决PHP处理图片时内存占用过高问题
    destoon下动态链接301到伪静态(ngnix)
    微信小程序转百度小程序代码
    解决:本图片来自微信公众号,未经许可,不能引用 问题
    MP4文件批量转码成MP3
    dt框架自定义url规则
    织梦后台基本参数无法保存解决办法
    关于tomcat启动没有进行编译或者编译报错的问题
    zf-安徽桐城关于(资源中心-数据录入)上传文件后没有进行处理Excel文件的原因
    zf-关于平台的用户名密码的设置
  • 原文地址:https://www.cnblogs.com/Jiangchuanwei/p/9919825.html
Copyright © 2011-2022 走看看