├── 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 | 
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 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | {{noMoreText}}
24 |
25 | {{emptyText}}
26 |
27 |
28 |
29 |
30 |
31 |
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 | }
--------------------------------------------------------------------------------