├── 123.gif ├── README.md ├── app.js ├── app.json ├── app.wxss ├── component └── listView │ ├── icon_down.png │ ├── icon_loading.png │ ├── listView.js │ ├── listView.json │ ├── listView.wxml │ ├── listView.wxss │ └── util.js └── index ├── index.js ├── index.json ├── index.wxml └── index.wxss /123.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyl169/wapp_refreshView/f4eb3f18424c3fb73d017f3fbceee1008330280c/123.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wapp_refreshView 2 | 一个在微信小程序中使用的上下拉刷新组件 3 | 4 | 小程序代码片段: wechatide://minicode/Iw0WABm37T2P 5 | 6 | 效果: 7 | ![Alt text](https://github.com/dyl169/wapp_refreshView/blob/master/123.gif "效果图") 8 | 9 | 参数: 10 | 11 | |参数名|类型|说明| 12 | |:----- |:-----|----- | 13 | | custom-class | 自定义样式class-name | 自定义样式 | 14 | | refresh |boolean |是否需要刷新| 15 | | loadMore |boolean |是否需要加载更多| 16 | | length |number |数据长度| 17 | | emptyText |string |数据为空时的提示| 18 | | noMore |boolean |是否没有更多了| 19 | | noMoreText |string |没有更多数据时的提示| 20 | | bind:onLoadMore |回调 |刷新时回调| 21 | | bind:onRefresh |回调 |加载更多时回调| 22 | 23 | ``` 24 | 例:刷新&加载更多 25 | onRefresh:function(e){ 26 | var callback = e.detail; 27 | setTimeout(function(){ 28 | callback.success(); 29 | },3000) 30 | }, 31 | onLoadMore: function (e) { 32 | var callback = e.detail; 33 | setTimeout(function () { 34 | callback.fail(); 35 | }, 3000) 36 | }, 37 | 38 | ``` 39 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | App({ 2 | onLaunch: function () { 3 | 4 | } 5 | }) 6 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages":[ 3 | "index/index" 4 | ], 5 | "window":{ 6 | "backgroundTextStyle":"light", 7 | "navigationBarBackgroundColor": "#fff", 8 | "navigationBarTitleText": "WeChat", 9 | "navigationBarTextStyle":"black" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyl169/wapp_refreshView/f4eb3f18424c3fb73d017f3fbceee1008330280c/app.wxss -------------------------------------------------------------------------------- /component/listView/icon_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyl169/wapp_refreshView/f4eb3f18424c3fb73d017f3fbceee1008330280c/component/listView/icon_down.png -------------------------------------------------------------------------------- /component/listView/icon_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyl169/wapp_refreshView/f4eb3f18424c3fb73d017f3fbceee1008330280c/component/listView/icon_loading.png -------------------------------------------------------------------------------- /component/listView/listView.js: -------------------------------------------------------------------------------- 1 | // components/listView/listView.js 2 | 3 | var util = require('./util.js'); 4 | 5 | Component({ 6 | /** 7 | * 外部样式 8 | */ 9 | externalClasses: ['custom-class'], 10 | 11 | /** 12 | * 组件的属性列表 13 | */ 14 | properties: { 15 | //是否需要刷新 16 | refresh: { 17 | type: Boolean, 18 | value: true, 19 | }, 20 | //是否需要加载更多 21 | loadMore: { 22 | type: Boolean, 23 | value: true, 24 | }, 25 | //数据长度 26 | length: { 27 | type: Number, 28 | value: 0, 29 | }, 30 | //空数据显示 31 | emptyText: { 32 | type: String, 33 | value: '没有数据~', 34 | }, 35 | noMore: { 36 | type: Boolean, 37 | value: false, 38 | }, 39 | noMoreText: { 40 | type: String, 41 | value: '没有更多了~', 42 | } 43 | }, 44 | 45 | /** 46 | * 组件的初始数据 47 | */ 48 | data: { 49 | EVENT_REFRESH: 'onRefresh', 50 | EVENT_LOADMORE: 'onLoadMore', 51 | rotateAngle: 0, 52 | scrollY: true,//默认true 53 | state: 0, //控件的状态 0:无状态 1:下拉中 2:下拉达到刷新条件 3:下拉刷新中 4:上拉中 5:上拉加载中 6:加载失败 54 | scrollToTop: true, //是否到顶部了 55 | scrollToBottom: false, //是否到底部了 56 | }, 57 | 58 | /** 59 | * 生命周期函数--监听页面初次渲染完成 60 | */ 61 | ready: function () { 62 | var that = this; 63 | util.queryView(this, '.listView_box_header', function (res) { 64 | var scale = 80 / res.height; 65 | that.data.headHeight = res.height * scale; 66 | that.data.headHeightDef = res.height * scale; 67 | }); 68 | 69 | util.queryView(this, '.listView_box', function (res) { 70 | that.setData({ 71 | bottom: res.bottom, 72 | }) 73 | }); 74 | }, 75 | 76 | /** 77 | * 组件的方法列表 78 | */ 79 | methods: { 80 | /** 81 | * 滚动事件 82 | */ 83 | scrollListener: function (e) { 84 | if (e.detail.scrollTop == 0) { 85 | //到顶部了 86 | this.data.scrollToTop = true; 87 | } else { 88 | //离开顶部了 89 | this.data.scrollToTop = false; 90 | } 91 | }, 92 | /** 93 | * 滚动到顶部 94 | */ 95 | scrollTopListener: function (e) { 96 | this.data.scrollToTop = true; 97 | this.data.scrollToBottom = false; 98 | }, 99 | /** 100 | * 滚动到底部 101 | */ 102 | scrollBottomListener: function (e) { 103 | //加载更多 104 | if (this.data.state != 3 105 | && this.data.state != 5 106 | && this.data.length != null && this.data.length > 0 107 | && this.data.loadMore 108 | && !this.data.noMore) { 109 | console.log('触发加载更多') 110 | this.data.scrollToTop = false; 111 | this.data.scrollToBottom = true; 112 | this.data.state = 5; 113 | this.data.headHeight = this.data.headHeightDef; 114 | this.setData(this.data); 115 | this.sendEvent(this.data.EVENT_LOADMORE); 116 | } 117 | }, 118 | /** 119 | * 手指点击 120 | */ 121 | touchStart: function (e) { 122 | this.data.touchStartY = e.touches[0].pageY; 123 | console.log('touchStart' + this.data.touchStartY); 124 | this.triggerEvent('bindScrollTouchStart'); 125 | }, 126 | /** 127 | * 手指滑动 128 | */ 129 | touchMove: function (e) { 130 | 131 | //1.计算出下拉的间距 132 | var dropDownInterval = (e.touches[0].pageY - this.data.touchStartY); 133 | this.data.rotateAngle = dropDownInterval / this.data.headHeightDef * 90; 134 | if (this.data.rotateAngle > 180) { 135 | this.data.rotateAngle = 180; 136 | } 137 | 138 | //2.下拉时 139 | if (dropDownInterval > 0 140 | && this.data.scrollToTop 141 | && this.data.state != 3 142 | && this.data.state != 5 143 | && this.data.refresh) { 144 | this.setData({ 145 | scrollY: false, 146 | }) 147 | //3.没有在加载的时候去计算head和margin-top 148 | if (this.data.state != 3 && this.data.state != 5) { 149 | var marginTop = -this.data.headHeightDef + (dropDownInterval); 150 | this.data.headMarginTop = marginTop > 0 ? 0 : marginTop; 151 | } 152 | 153 | //2.当下拉的高度大于等于head的高度 154 | if (dropDownInterval >= this.data.headHeightDef && this.data.state != 3 && this.data.state != 5) { 155 | //下拉的最大高度 156 | if (dropDownInterval > 200) { 157 | dropDownInterval = 200; 158 | } 159 | this.data.headHeight = dropDownInterval; 160 | 161 | //3.当下拉达到下拉刷新的条件时 162 | if (e.touches[0].pageY > this.data.touchStartY && 163 | this.data.headHeight + this.data.headMarginTop > this.data.headHeightDef && 164 | this.data.state == 1) { 165 | this.data.state = 2; 166 | } 167 | } else { 168 | //3.当是下拉 169 | this.data.state = 1;//下拉中 170 | } 171 | } else if (this.data.state != 3 && this.data.state != 5) { 172 | this.data.state = 4;//上拉中 173 | } 174 | console.log(this.data.state); 175 | this.setData(this.data); 176 | }, 177 | /** 178 | * 手指松开 179 | */ 180 | touchEnd: function (e) { 181 | this.setData({ 182 | scrollY: true, 183 | }) 184 | if (this.data.state == 2) { 185 | //触发刷新 186 | this.data.state = 3; 187 | this.data.headHeight = this.data.headHeightDef; 188 | this.data.scrollTop = 0; 189 | this.sendEvent(this.data.EVENT_REFRESH); 190 | this.setData(this.data); 191 | } else if (this.data.headMarginTop <= 0 && this.data.state == 1 || this.data.state == 4) { 192 | this.resetState(); 193 | } 194 | }, 195 | //重置状态 196 | resetState: function () { 197 | console.log('重置状态') 198 | //重置状态 199 | this.data.state = 0; 200 | this.data.headMarginTop = -this.data.headHeightDef; 201 | this.data.headHeight = this.data.headHeightDef; 202 | this.setData(this.data); 203 | }, 204 | //动态改变scroll-y的值 防止下拉时触发scroll-view的阻尼效果 205 | setScrollY: function (state) { 206 | this.data.scrollY = state; 207 | this.setData({ 208 | scrollY: this.data.scrollY, 209 | }) 210 | }, 211 | //发送事件 212 | sendEvent: function (eventType) { 213 | var that = this; 214 | this.triggerEvent(eventType, { 215 | success: function () { 216 | if (eventType == that.data.EVENT_REFRESH && that.data.state == 3) { 217 | that.data.scrollToTop = true; 218 | that.data.scrollToBottom = false; 219 | that.data.scrollTop = 0; 220 | } else if (eventType == that.data.EVENT_LOADMORE && that.data.state == 5) { 221 | that.data.scrollToTop = false; 222 | that.data.scrollToBottom = true; 223 | } 224 | that.resetState(); 225 | }, 226 | fail: function () { 227 | that.data.state = 6; 228 | that.setData(that.data); 229 | setTimeout(function () { 230 | if (eventType == that.data.EVENT_REFRESH && that.data.state == 3) { 231 | that.data.scrollToTop = true; 232 | that.data.scrollToBottom = false; 233 | that.data.scrollTop = 0; 234 | that.setData(that.data); 235 | } else if (eventType == that.data.EVENT_LOADMORE && that.data.state == 5) { 236 | that.data.scrollToTop = false; 237 | that.data.scrollToBottom = true; 238 | that.setData(that.data); 239 | } 240 | that.data.state = 0; 241 | that.resetState(); 242 | }, 2000) 243 | }, 244 | }) 245 | }, 246 | } 247 | }) -------------------------------------------------------------------------------- /component/listView/listView.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": {} 4 | } -------------------------------------------------------------------------------- /component/listView/listView.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 下拉刷新 9 | 松手立即刷新 10 | 11 | 12 | 正在刷新中... 13 | 14 | 加载失败 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | {{noMoreText}} 24 | 25 | {{emptyText}} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 加载更多中... 35 | 36 | 加载失败 37 | 38 | 39 | -------------------------------------------------------------------------------- /component/listView/listView.wxss: -------------------------------------------------------------------------------- 1 | /* components/listView/listView.wxss */ 2 | 3 | .listView_box { 4 | width: 100%; 5 | height: 100%; 6 | display: flex; 7 | flex-direction: column; 8 | } 9 | 10 | .listView_box_content { 11 | width: 100%; 12 | height: 100%; 13 | } 14 | 15 | .listView_box_header, 16 | .listView_box_footer { 17 | width: 100%; 18 | height: 80rpx; 19 | line-height: 80rpx; 20 | text-align: center; 21 | display: flex; 22 | flex-direction: row; 23 | align-items: center; 24 | justify-content: center; 25 | font-size: 26rpx; 26 | color: #999; 27 | background: #fff; 28 | } 29 | 30 | .listView_box_header { 31 | margin-top: -80rpx; 32 | } 33 | 34 | .refreshingView { 35 | width: 100%; 36 | height: 100%; 37 | display: flex; 38 | flex-direction: row; 39 | align-items: center; 40 | justify-content: center; 41 | } 42 | 43 | .dropDownImg, 44 | .dropEndImg { 45 | width: 32rpx; 46 | height: 32rpx; 47 | margin-right: 24rpx; 48 | } 49 | 50 | .refreshingView_img, 51 | .loadMoreRefresh_img { 52 | width: 32rpx; 53 | height: 32rpx; 54 | margin-right: 24rpx; 55 | transition: 0.5s; 56 | transform-origin: 50% 50%; 57 | animation: loading 1s linear infinite; 58 | } 59 | 60 | @keyframes loading { 61 | from { 62 | -webkit-transform: rotate(0deg); 63 | } 64 | 65 | to { 66 | -webkit-transform: rotate(360deg); 67 | } 68 | } 69 | 70 | .listView_box_footer { 71 | position: fixed; 72 | left: 0; 73 | bottom: 0; 74 | z-index: 10; 75 | } 76 | 77 | .textColor_red { 78 | color: rgba(255,0,0,0.6); 79 | } 80 | 81 | /*空列表*/ 82 | .emptyView { 83 | width: 100%; 84 | height: 100%; 85 | display: flex; 86 | flex-direction: row; 87 | align-items: center; 88 | justify-content: center; 89 | color: #999; 90 | font-size: 28rpx; 91 | } 92 | 93 | .noMoreView { 94 | width: 100%; 95 | height: 80rpx; 96 | line-height: 80rpx; 97 | text-align: center; 98 | color: #999; 99 | font-size: 28rpx; 100 | } -------------------------------------------------------------------------------- /component/listView/util.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | //获取节点信息 3 | queryView: function (context,name, success) { 4 | const query = wx.createSelectorQuery().in(context) 5 | query.select(name).boundingClientRect(function (res) { 6 | success(res); 7 | }).exec() 8 | } 9 | } -------------------------------------------------------------------------------- /index/index.js: -------------------------------------------------------------------------------- 1 | const app = getApp() 2 | 3 | Page({ 4 | data: { 5 | listData:[ 1,2,3,4,5,6,7,8,9,10] 6 | }, 7 | onLoad: function () { 8 | }, 9 | onRefresh:function(e){ 10 | var callback = e.detail; 11 | setTimeout(function(){ 12 | callback.success(); 13 | },3000) 14 | }, 15 | onLoadMore: function (e) { 16 | var callback = e.detail; 17 | setTimeout(function () { 18 | callback.fail(); 19 | }, 3000) 20 | }, 21 | 22 | }) 23 | -------------------------------------------------------------------------------- /index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "下拉刷新", 3 | "usingComponents": { 4 | "listView": "/component/listView/listView" 5 | } 6 | } -------------------------------------------------------------------------------- /index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 头部 4 | 5 | 6 | 7 | {{item}} 8 | 9 | -------------------------------------------------------------------------------- /index/index.wxss: -------------------------------------------------------------------------------- 1 | .pageView { 2 | width: 100vw; 3 | height: 100vh; 4 | display: flex; 5 | flex-direction: column; 6 | background: #e5e5e5; 7 | } 8 | 9 | .topView { 10 | width: 100vw; 11 | height: 120rpx; 12 | background: #fff; 13 | display: flex; 14 | flex-direction: row; 15 | align-items: center; 16 | justify-content: center; 17 | } 18 | 19 | .listView_box { 20 | width: 100vw; 21 | height: calc(100vh - 120rpx - 24rpx); 22 | margin-top: 24rpx; 23 | background: #fff; 24 | } 25 | 26 | .listView_itemView { 27 | width: 100vw; 28 | height: 120rpx; 29 | border: solid 1rpx #e5e5e5; 30 | } --------------------------------------------------------------------------------