<div> <mt-loadmore :top-method="loadTop" :bottom-method="loadBottom" :bottom-all-loaded="allLoaded" :max-distance="150" @top-status-change="handleTopChange" ref="loadmore"> <div slot="top"> <span v-show="topStatus === 'pull'" :class="{ 'rotate': topStatus === 'drop' }">↓</span> <span v-show="topStatus === 'loading'">Loading...</span> <span v-show="topStatus === 'drop'">释放更新</span> </div> <ul> <li v-for="item in list" @click="itemClick(item)">{{ item }}</li> </ul> </mt-loadmore> </div>
css
<link href="https://unpkg.com/mint-ui/lib/style.css" > *{ margin: 0; padding: 0; } html, body{ height: 100%; } #app{ height: 100%; overflow: scroll; } .scroll-wrapper{ margin: 0; padding: 0; list-style: none; } .scroll-wrapper li{ line-height: 120px; font-size: 60px; text-align: center; }
js
<!-- 先引入 Vue --> <script src="https://unpkg.com/vue/dist/vue.js"></script> <!-- 引入组件库 --> <script src="https://unpkg.com/mint-ui/lib/index.js"></script> <script> new Vue({ el: '#app', data: { list: [], allLoaded: false, topStatus: '' }, created: function () { var i =0, len=20; for (; i< len; i++){ this.list.push('demo' + i); } }, methods: { loadTop: function () { // 刷新数据的操作 var self = this; setTimeout(function () { self.list.splice(0, self.list.length); var i =0, len=20; for (; i< len; i++){ self.list.push('demo' + i); } self.$refs.loadmore.onTopLoaded(); }, 2000); }, loadBottom: function () { // 加载更多数据的操作 //load data //this.allLoaded = true;// 若数据已全部获取完毕 var self = this; setTimeout(function () { var i =0; len = 10; for (; i< len; i++){ self.list.push('dddd' + i); } self.$refs.loadmore.onBottomLoaded(); }, 2000); }, handleTopChange: function (status) { this.topStatus = status; }, itemClick: function (data) { console.log('item click, msg : ' + data); } } }); </script>
实现原理解析
布局原理
loadmore组件内部由三个slot组成,分别为name=top,name=bottom,default;
top用于展示下拉刷新不同状态展示的内容,初始设置margin-top为-top的高度来将自己隐藏
bottom同top,用于展示上拉加载更多不同状态展示的内容
default填充滚动详细内容
实现原理
主要是通过js的touch事件的监听来实现
在touchmove事件,如果是向下滑动并且滚动的dom的scrollTop为0,那么整个组件向下偏移(滑动的距离/比值)展示出top solt的内容
在touchmove时间,如果是向上滑动并且滑动到了底部,再继续滑动整个组件向上偏移(滑动的距离/比值)展示出bottom solt的内容
源码解析
组件的template html
<div> <div :class="{ 'is-dropped': topDropped || bottomDropped}" :style="{ 'transform': 'translate3d(0, ' + translate + 'px, 0)' }"> <slot> <div v-if="topMethod"> <spinner v-if="topStatus === 'loading'" :size="20" type="fading-circle"></spinner> <span>{{ topText }}</span> </div> </slot> <slot></slot> <slot> <div v-if="bottomMethod"> <spinner v-if="bottomStatus === 'loading'" :size="20" type="fading-circle"></spinner> <span>{{ bottomText }}</span> </div> </slot> </div> </div>
关于 上面的spinner标签,是一个组件,这里不做详细介绍。top solt和bottom slot中的内容是展示的内容,可以通过外部自定义的方式传入。
其实它的实现有一个很严重的弊端,就是写死了top solt和bottom slot的高度为50px,而且js中的处理也是使用50px进行的逻辑处理。所以并满足我们开发中自定义top slot 和bottom slot的需求。
js核心解析
props解析:关于props的解析,可以参考mint-ui的官方文档
data解析
data() { return { translate: 0, // 此变量决定当前组件上下移动, scrollEventTarget: null, // 滚动的dom节点 containerFilled: false, // 当前滚动的内容是否填充完整,不完成会调用 loadmore的回调函数 topText: '', // 下拉刷新,显示的文本 topDropped: false, // 记录当前drop状态,用给组件dom添加is-dropped class(添加回到原点的动画) bottomText: '', // 上拉加载更多 显示的文本 bottomDropped: false, // 同topDropped bottomReached: false, // 当前滚动是否滚动到了底部 direction: '', // touch-move过程中, 当前滑动的方向 startY: 0, // touch-start 起始的y的坐标值 startScrollTop: 0, // touch-start 起始的滚动dom的 scrollTop currentY: 0, // touch-move 过程中的 y的坐标 topStatus: '', // 下拉刷新的状态: pull(下拉) drop(释放) loading(正在加载数据) bottomStatus: '' // 上拉加载更多的状态: 状态同上 }; }
上面的关于每个data数据的具体作用通过注释做了详细说明。
watch解析