refresher.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. // [z-paging]下拉刷新view模块
  2. import u from '.././z-paging-utils'
  3. import c from '.././z-paging-constant'
  4. import Enum from '.././z-paging-enum'
  5. export default {
  6. props: {
  7. //下拉刷新的主题样式,支持black,white,默认black
  8. refresherThemeStyle: {
  9. type: String,
  10. default: u.gc('refresherThemeStyle', '')
  11. },
  12. //自定义下拉刷新中左侧图标的样式
  13. refresherImgStyle: {
  14. type: Object,
  15. default: function() {
  16. return u.gc('refresherImgStyle', {});
  17. }
  18. },
  19. //自定义下拉刷新中右侧状态描述文字的样式
  20. refresherTitleStyle: {
  21. type: Object,
  22. default: function() {
  23. return u.gc('refresherTitleStyle', {});
  24. }
  25. },
  26. //自定义下拉刷新中右侧最后更新时间文字的样式(show-refresher-update-time为true时有效)
  27. refresherUpdateTimeStyle: {
  28. type: Object,
  29. default: function() {
  30. return u.gc('refresherUpdateTimeStyle', {});
  31. }
  32. },
  33. //在微信小程序和QQ小程序中,是否实时监听下拉刷新中进度,默认为否
  34. watchRefresherTouchmove: {
  35. type: Boolean,
  36. default: u.gc('watchRefresherTouchmove', false)
  37. },
  38. //底部加载更多的主题样式,支持black,white,默认black
  39. loadingMoreThemeStyle: {
  40. type: String,
  41. default: u.gc('loadingMoreThemeStyle', '')
  42. },
  43. //是否只使用下拉刷新,设置为true后将关闭mounted自动请求数据、关闭滚动到底部加载更多,强制隐藏空数据图。默认为否
  44. refresherOnly: {
  45. type: Boolean,
  46. default: u.gc('refresherOnly', false)
  47. },
  48. //自定义下拉刷新默认状态下回弹动画时间,单位为毫秒,默认为100毫秒,nvue无效
  49. refresherDefaultDuration: {
  50. type: [Number, String],
  51. default: u.gc('refresherDefaultDuration', 100)
  52. },
  53. //自定义下拉刷新结束以后延迟回弹的时间,单位为毫秒,默认为0
  54. refresherCompleteDelay: {
  55. type: [Number, String],
  56. default: u.gc('refresherCompleteDelay', 0)
  57. },
  58. //自定义下拉刷新结束回弹动画时间,单位为毫秒,默认为300毫秒(refresherEndBounceEnabled为false时,refresherCompleteDuration为设定值的1/3),nvue无效
  59. refresherCompleteDuration: {
  60. type: [Number, String],
  61. default: u.gc('refresherCompleteDuration', 300)
  62. },
  63. //自定义下拉刷新结束状态下是否允许列表滚动,默认为否
  64. refresherCompleteScrollable: {
  65. type: Boolean,
  66. default: u.gc('refresherCompleteScrollable', false)
  67. },
  68. //是否使用自定义的下拉刷新,默认为是,即使用z-paging的下拉刷新。设置为false即代表使用uni scroll-view自带的下拉刷新,h5、App、微信小程序以外的平台不支持uni scroll-view自带的下拉刷新
  69. useCustomRefresher: {
  70. type: Boolean,
  71. default: u.gc('useCustomRefresher', true)
  72. },
  73. //自定义下拉刷新下拉帧率,默认为40,过高可能会出现抖动问题
  74. refresherFps: {
  75. type: [Number, String],
  76. default: u.gc('refresherFps', 40)
  77. },
  78. //自定义下拉刷新允许触发的最大下拉角度,默认为40度,当下拉角度小于设定值时,自定义下拉刷新动画不会被触发
  79. refresherMaxAngle: {
  80. type: [Number, String],
  81. default: u.gc('refresherMaxAngle', 40)
  82. },
  83. //自定义下拉刷新的角度由未达到最大角度变到达到最大角度时,是否继续下拉刷新手势,默认为否
  84. refresherAngleEnableChangeContinued: {
  85. type: Boolean,
  86. default: u.gc('refresherAngleEnableChangeContinued', false)
  87. },
  88. //自定义下拉刷新默认状态下的文字
  89. refresherDefaultText: {
  90. type: [String, Object],
  91. default: u.gc('refresherDefaultText', null)
  92. },
  93. //自定义下拉刷新松手立即刷新状态下的文字
  94. refresherPullingText: {
  95. type: [String, Object],
  96. default: u.gc('refresherPullingText', null)
  97. },
  98. //自定义下拉刷新刷新中状态下的文字
  99. refresherRefreshingText: {
  100. type: [String, Object],
  101. default: u.gc('refresherRefreshingText', null)
  102. },
  103. //自定义下拉刷新刷新结束状态下的文字
  104. refresherCompleteText: {
  105. type: [String, Object],
  106. default: u.gc('refresherCompleteText', null)
  107. },
  108. //自定义下拉刷新默认状态下的图片
  109. refresherDefaultImg: {
  110. type: String,
  111. default: u.gc('refresherDefaultImg', null)
  112. },
  113. //自定义下拉刷新松手立即刷新状态下的图片,默认与refresherDefaultImg一致
  114. refresherPullingImg: {
  115. type: String,
  116. default: u.gc('refresherPullingImg', null)
  117. },
  118. //自定义下拉刷新刷新中状态下的图片
  119. refresherRefreshingImg: {
  120. type: String,
  121. default: u.gc('refresherRefreshingImg', null)
  122. },
  123. //自定义下拉刷新刷新结束状态下的图片
  124. refresherCompleteImg: {
  125. type: String,
  126. default: u.gc('refresherCompleteImg', null)
  127. },
  128. //是否开启自定义下拉刷新刷新结束回弹效果,默认为是
  129. refresherEndBounceEnabled: {
  130. type: Boolean,
  131. default: u.gc('refresherEndBounceEnabled', true)
  132. },
  133. //是否开启自定义下拉刷新,默认为是
  134. refresherEnabled: {
  135. type: Boolean,
  136. default: u.gc('refresherEnabled', true)
  137. },
  138. //设置自定义下拉刷新阈值,默认为80rpx
  139. refresherThreshold: {
  140. type: [Number, String],
  141. default: u.gc('refresherThreshold', '80rpx')
  142. },
  143. //设置系统下拉刷新默认样式,支持设置 black,white,none,none 表示不使用默认样式,默认为black
  144. refresherDefaultStyle: {
  145. type: String,
  146. default: u.gc('refresherDefaultStyle', 'black')
  147. },
  148. //设置自定义下拉刷新区域背景
  149. refresherBackground: {
  150. type: String,
  151. default: u.gc('refresherBackground', 'transparent')
  152. },
  153. //设置固定的自定义下拉刷新区域背景
  154. refresherFixedBackground: {
  155. type: String,
  156. default: u.gc('refresherFixedBackground', 'transparent')
  157. },
  158. //设置固定的自定义下拉刷新区域高度,默认为0
  159. refresherFixedBacHeight: {
  160. type: [Number, String],
  161. default: u.gc('refresherFixedBacHeight', 0)
  162. },
  163. //设置自定义下拉刷新下拉超出阈值后继续下拉位移衰减的比例,范围0-1,值越大代表衰减越多。默认为0.65(nvue无效)
  164. refresherOutRate: {
  165. type: Number,
  166. default: u.gc('refresherOutRate', 0.65)
  167. },
  168. //设置自定义下拉刷新下拉时实际下拉位移与用户下拉距离的比值,默认为0.75,即代表若用户下拉10px,则实际位移为7.5px(nvue无效)
  169. refresherPullRate: {
  170. type: Number,
  171. default: u.gc('refresherPullRate', 0.75)
  172. },
  173. //是否显示最后更新时间,默认为否
  174. showRefresherUpdateTime: {
  175. type: Boolean,
  176. default: u.gc('showRefresherUpdateTime', false)
  177. },
  178. //如果需要区别不同页面的最后更新时间,请为不同页面的z-paging的`refresher-update-time-key`设置不同的字符串
  179. refresherUpdateTimeKey: {
  180. type: String,
  181. default: u.gc('refresherUpdateTimeKey', 'default')
  182. },
  183. //下拉刷新时下拉到“松手立即刷新”状态时是否使手机短振动,默认为否(h5无效)
  184. refresherVibrate: {
  185. type: Boolean,
  186. default: u.gc('refresherVibrate', false)
  187. },
  188. },
  189. data() {
  190. return {
  191. R: Enum.Refresher,
  192. //下拉刷新状态
  193. refresherStatus: Enum.Refresher.Default,
  194. refresherTouchstartY: 0,
  195. lastRefresherTouchmove: null,
  196. refresherReachMaxAngle: true,
  197. refresherTransform: 'translateY(0px)',
  198. refresherTransition: '',
  199. finalRefresherDefaultStyle: 'black',
  200. refresherRevealStackCount: 0,
  201. refresherCompleteTimeout: null,
  202. refresherCompleteSubTimeout: null,
  203. refresherEndTimeout: null,
  204. isTouchmovingTimeout: null,
  205. refresherTriggered: false,
  206. isTouchmoving: false,
  207. isTouchEnded: false,
  208. isUserPullDown: false,
  209. privateRefresherEnabled: -1,
  210. privateShowRefresherWhenReload: false,
  211. customRefresherHeight: -1,
  212. showCustomRefresher: false,
  213. doRefreshAnimateAfter: false,
  214. isRefresherInComplete: false,
  215. pullDownTimeStamp: 0,
  216. moveDis: 0,
  217. oldMoveDis: 0,
  218. currentDis: 0,
  219. oldCurrentMoveDis: 0,
  220. oldRefresherTouchmoveY: 0,
  221. oldTouchDirection: ''
  222. }
  223. },
  224. watch: {
  225. refresherDefaultStyle: {
  226. handler(newVal) {
  227. if (newVal.length) {
  228. this.finalRefresherDefaultStyle = newVal;
  229. }
  230. },
  231. immediate: true
  232. },
  233. refresherStatus(newVal) {
  234. newVal === Enum.Refresher.Loading && this._cleanRefresherEndTimeout();
  235. this.refresherVibrate && newVal === Enum.Refresher.ReleaseToRefresh && this._doVibrateShort();
  236. this.$emit('refresherStatusChange', newVal);
  237. this.$emit('update:refresherStatus', newVal);
  238. }
  239. },
  240. computed: {
  241. pullDownDisTimeStamp() {
  242. return 1000 / this.refresherFps;
  243. },
  244. finalRefresherEnabled() {
  245. if (this.useChatRecordMode) return false;
  246. if (this.privateRefresherEnabled === -1) return this.refresherEnabled;
  247. return this.privateRefresherEnabled === 1;
  248. },
  249. finalRefresherThreshold() {
  250. let refresherThreshold = this.refresherThreshold;
  251. let idDefault = false;
  252. if (refresherThreshold === '80rpx') {
  253. idDefault = true;
  254. if (this.showRefresherUpdateTime) {
  255. refresherThreshold = '120rpx';
  256. }
  257. }
  258. if (idDefault && this.customRefresherHeight > 0) return this.customRefresherHeight;
  259. return u.convertTextToPx(refresherThreshold);
  260. },
  261. finalRefresherFixedBacHeight() {
  262. return u.convertTextToPx(this.refresherFixedBacHeight);
  263. },
  264. finalRefresherThemeStyle() {
  265. return this.refresherThemeStyle.length ? this.refresherThemeStyle : this.defaultThemeStyle;
  266. },
  267. finalRefresherOutRate() {
  268. let rate = this.refresherOutRate;
  269. rate = Math.max(0,rate);
  270. rate = Math.min(1,rate);
  271. return rate;
  272. },
  273. finalRefresherPullRate() {
  274. let rate = this.refresherPullRate;
  275. rate = Math.max(0,rate);
  276. return rate;
  277. },
  278. finalRefresherTransform() {
  279. if (this.refresherTransform === 'translateY(0px)') return 'none';
  280. return this.refresherTransform;
  281. },
  282. finalShowRefresherWhenReload() {
  283. return this.showRefresherWhenReload || this.privateShowRefresherWhenReload;
  284. },
  285. finalRefresherTriggered() {
  286. if (!(this.finalRefresherEnabled && !this.useCustomRefresher)) return false;
  287. return this.refresherTriggered;
  288. },
  289. showRefresher() {
  290. const showRefresher = this.finalRefresherEnabled && this.useCustomRefresher;
  291. // #ifndef APP-NVUE
  292. if (this.customRefresherHeight === -1 && showRefresher) {
  293. setTimeout(() => {
  294. this.$nextTick(()=>{
  295. this._updateCustomRefresherHeight();
  296. })
  297. }, 100)
  298. }
  299. // #endif
  300. return showRefresher;
  301. },
  302. hasTouchmove(){
  303. // #ifdef VUE2
  304. // #ifdef APP-VUE || H5
  305. if (this.$listeners && !this.$listeners.refresherTouchmove) return false;
  306. // #endif
  307. // #ifdef MP-WEIXIN || MP-QQ
  308. return this.watchRefresherTouchmove;
  309. // #endif
  310. return true;
  311. // #endif
  312. return this.watchRefresherTouchmove;
  313. },
  314. },
  315. methods: {
  316. //终止下拉刷新状态
  317. endRefresh(){
  318. this.totalData = this.realTotalData;
  319. this._refresherEnd();
  320. this._endSystemLoadingAndRefresh();
  321. },
  322. handleRefresherStatusChanged(func) {
  323. this.refresherStatusChangedFunc = func;
  324. },
  325. //自定义下拉刷新被触发
  326. _onRefresh(fromScrollView = false,isUserPullDown = true) {
  327. if (fromScrollView && !(this.finalRefresherEnabled && !this.useCustomRefresher)) return;
  328. this.$emit('onRefresh');
  329. this.$emit('Refresh');
  330. // #ifdef APP-NVUE
  331. if (this.loading) {
  332. setTimeout(()=>{
  333. this._nRefresherEnd();
  334. },500)
  335. return;
  336. }
  337. // #endif
  338. if (this.loading || this.isRefresherInComplete) return;
  339. this.loadingType = Enum.LoadingType.Refresher;
  340. if (this.nShowRefresherReveal) return;
  341. this.isUserPullDown = isUserPullDown;
  342. this.isUserReload = !isUserPullDown;
  343. this._startLoading(true);
  344. this.refresherTriggered = true;
  345. if(this.reloadWhenRefresh && isUserPullDown){
  346. this.useChatRecordMode ? this._onLoadingMore('click') : this._reload(false, false, isUserPullDown);
  347. }
  348. },
  349. //自定义下拉刷新被复位
  350. _onRestore() {
  351. this.refresherTriggered = 'restore';
  352. this.$emit('onRestore');
  353. this.$emit('Restore');
  354. },
  355. // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
  356. //拖拽开始
  357. _refresherTouchstart(e) {
  358. this._handleListTouchstart();
  359. if (this._touchDisabled()) return;
  360. const touch = u.getTouch(e);
  361. this._handleRefresherTouchstart(touch);
  362. },
  363. // #endif
  364. //进一步处理拖拽开始结果
  365. _handleRefresherTouchstart(touch) {
  366. if (!this.loading && this.isTouchEnded) {
  367. this.isTouchmoving = false;
  368. }
  369. this.loadingType = Enum.LoadingType.Refresher;
  370. this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
  371. this.isTouchEnded = false;
  372. this.refresherTransition = '';
  373. this.refresherTouchstartY = touch.touchY;
  374. this.$emit('refresherTouchstart', this.refresherTouchstartY);
  375. this.lastRefresherTouchmove = touch;
  376. this._cleanRefresherCompleteTimeout();
  377. this._cleanRefresherEndTimeout();
  378. },
  379. // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
  380. //拖拽中
  381. _refresherTouchmove(e) {
  382. const currentTimeStamp = u.getTime();
  383. let touch = null;
  384. let refresherTouchmoveY = 0;
  385. if (this.watchTouchDirectionChange) {
  386. touch = u.getTouch(e);
  387. refresherTouchmoveY = touch.touchY;
  388. const direction = refresherTouchmoveY > this.oldRefresherTouchmoveY ? 'top' : 'bottom';
  389. direction === this.oldTouchDirection && this._handleTouchDirectionChange({direction});
  390. this.oldTouchDirection = direction;
  391. this.oldRefresherTouchmoveY = refresherTouchmoveY;
  392. }
  393. if (this.pullDownTimeStamp && currentTimeStamp - this.pullDownTimeStamp <= this.pullDownDisTimeStamp) return;
  394. if (this._touchDisabled()) return;
  395. this.pullDownTimeStamp = Number(currentTimeStamp);
  396. touch = u.getTouch(e);
  397. refresherTouchmoveY = touch.touchY;
  398. let moveDis = refresherTouchmoveY - this.refresherTouchstartY;
  399. if (moveDis < 0) return;
  400. if (this.refresherMaxAngle >= 0 && this.refresherMaxAngle <= 90 && this.lastRefresherTouchmove && this.lastRefresherTouchmove.touchY <= refresherTouchmoveY) {
  401. if (!moveDis && !this.refresherAngleEnableChangeContinued && this.moveDis < 1 && !this.refresherReachMaxAngle) return;
  402. const x = Math.abs(touch.touchX - this.lastRefresherTouchmove.touchX);
  403. const y = Math.abs(refresherTouchmoveY - this.lastRefresherTouchmove.touchY);
  404. const z = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
  405. if ((x || y) && x > 1) {
  406. const angle = Math.asin(y / z) / Math.PI * 180;
  407. if (angle < this.refresherMaxAngle) {
  408. this.lastRefresherTouchmove = touch;
  409. this.refresherReachMaxAngle = false;
  410. return;
  411. }
  412. }
  413. }
  414. moveDis = this._getFinalRefresherMoveDis(moveDis);
  415. this._handleRefresherTouchmove(moveDis, touch);
  416. if (!this.disabledBounce) {
  417. if(this.isIos){
  418. // #ifndef MP-LARK
  419. this._handleScrollViewDisableBounce({bounce: false});
  420. // #endif
  421. }
  422. this.disabledBounce = true;
  423. }
  424. this._emitTouchmove({pullingDistance:moveDis,dy:this.moveDis - this.oldMoveDis});
  425. },
  426. // #endif
  427. //进一步处理拖拽中结果
  428. _handleRefresherTouchmove(moveDis, touch) {
  429. this.refresherReachMaxAngle = true;
  430. this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
  431. this.isTouchmoving = true;
  432. this.isTouchEnded = false;
  433. this.refresherStatus = moveDis >= this.finalRefresherThreshold ? Enum.Refresher.ReleaseToRefresh : this.refresherStatus = Enum.Refresher.Default;
  434. // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
  435. // this.scrollEnable = false;
  436. this.refresherTransform = `translateY(${moveDis}px)`;
  437. this.lastRefresherTouchmove = touch;
  438. // #endif
  439. this.moveDis = moveDis;
  440. },
  441. // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
  442. //拖拽结束
  443. _refresherTouchend(e) {
  444. if (this._touchDisabled() || !this.isTouchmoving) return;
  445. const touch = u.getTouch(e);
  446. let refresherTouchendY = touch.touchY;
  447. let moveDis = refresherTouchendY - this.refresherTouchstartY;
  448. moveDis = this._getFinalRefresherMoveDis(moveDis);
  449. this._handleRefresherTouchend(moveDis);
  450. this._handleScrollViewDisableBounce({bounce: true});
  451. this.disabledBounce = false;
  452. },
  453. // #endif
  454. //进一步处理拖拽结束结果
  455. _handleRefresherTouchend(moveDis) {
  456. // #ifndef APP-PLUS || H5 || MP-WEIXIN
  457. if (!this.isTouchmoving) return;
  458. // #endif
  459. this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
  460. this.refresherReachMaxAngle = true;
  461. this.isTouchEnded = true;
  462. if (moveDis >= this.finalRefresherThreshold && this.refresherStatus === Enum.Refresher.ReleaseToRefresh) {
  463. // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
  464. this.refresherTransform = `translateY(${this.finalRefresherThreshold}px)`;
  465. this.refresherTransition = 'transform .1s linear';
  466. // #endif
  467. this.moveDis = this.finalRefresherThreshold;
  468. this.refresherStatus = Enum.Refresher.Loading;
  469. this._doRefresherLoad();
  470. } else {
  471. this._refresherEnd();
  472. this.isTouchmovingTimeout = setTimeout(() => {
  473. this.isTouchmoving = false;
  474. }, this.refresherDefaultDuration);
  475. }
  476. this.scrollEnable = true;
  477. this.$emit('refresherTouchend', moveDis);
  478. },
  479. //处理列表触摸开始事件
  480. _handleListTouchstart() {
  481. if (this.useChatRecordMode && this.autoHideKeyboardWhenChat) {
  482. uni.hideKeyboard();
  483. this.$emit('hidedKeyboard');
  484. }
  485. },
  486. //处理scroll-view bounce是否生效
  487. _handleScrollViewDisableBounce({ bounce }) {
  488. if (!this.usePageScroll && !this.scrollToTopBounceEnabled) {
  489. // #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
  490. this.refresherTransition = '';
  491. // #endif
  492. this.scrollEnable = bounce;
  493. }
  494. },
  495. //wxs正在下拉状态改变处理
  496. _handleWxsPullingDownStatusChange(onPullingDown) {
  497. this.wxsOnPullingDown = onPullingDown;
  498. if (onPullingDown && !this.useChatRecordMode) {
  499. this.renderPropScrollTop = 0;
  500. }
  501. },
  502. //wxs正在下拉处理
  503. _handleWxsPullingDown({ moveDis, diffDis }){
  504. this._emitTouchmove({pullingDistance:moveDis,dy:diffDis});
  505. },
  506. //wxs触摸方向改变
  507. _handleTouchDirectionChange({ direction }) {
  508. this.$emit('touchDirectionChange',direction);
  509. },
  510. //wxs通知更新其props
  511. _handlePropUpdate(){
  512. this.wxsPropType = u.getTime().toString();
  513. },
  514. //下拉刷新结束
  515. _refresherEnd(shouldEndLoadingDelay = true, fromAddData = false, isUserPullDown = false, setLoading = true) {
  516. if (this.loadingType === Enum.LoadingType.Refresher) {
  517. let refresherCompleteDelay = 0;
  518. if(fromAddData && (isUserPullDown || this.showRefresherWhenReload)){
  519. refresherCompleteDelay = this.refresherCompleteDuration > 700 ? 1 : this.refresherCompleteDelay;
  520. }
  521. const refresherStatus = refresherCompleteDelay > 0 ? Enum.Refresher.Complete : Enum.Refresher.Default;
  522. if (this.finalShowRefresherWhenReload) {
  523. const stackCount = this.refresherRevealStackCount;
  524. this.refresherRevealStackCount --;
  525. if (stackCount > 1) return;
  526. }
  527. this._cleanRefresherEndTimeout();
  528. this.refresherEndTimeout = setTimeout(() => {
  529. this.refresherStatus = refresherStatus;
  530. }, this.refresherStatus !== Enum.Refresher.Default && refresherStatus === Enum.Refresher.Default ? this.refresherCompleteDuration : 0);
  531. // #ifndef APP-NVUE
  532. if (refresherCompleteDelay > 0) {
  533. this.isRefresherInComplete = true;
  534. }
  535. // #endif
  536. this._cleanRefresherCompleteTimeout();
  537. this.refresherCompleteTimeout = setTimeout(() => {
  538. let animateDuration = 1;
  539. const animateType = this.refresherEndBounceEnabled && fromAddData ? 'cubic-bezier(0.19,1.64,0.42,0.72)' : 'linear';
  540. if (fromAddData) {
  541. animateDuration = this.refresherEndBounceEnabled ? this.refresherCompleteDuration / 1000 : this.refresherCompleteDuration / 3000;
  542. }
  543. this.refresherTransition = `transform ${fromAddData ? animateDuration : this.refresherDefaultDuration / 1000}s ${animateType}`;
  544. // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
  545. this.refresherTransform = 'translateY(0px)';
  546. this.currentDis = 0;
  547. // #endif
  548. // #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
  549. this.wxsPropType = this.refresherTransition + 'end' + u.getTime();
  550. // #endif
  551. // #ifdef APP-NVUE
  552. this._nRefresherEnd();
  553. // #endif
  554. this.moveDis = 0;
  555. // #ifndef APP-NVUE
  556. if (refresherStatus === Enum.Refresher.Complete) {
  557. if (this.refresherCompleteSubTimeout) {
  558. clearTimeout(this.refresherCompleteSubTimeout);
  559. this.refresherCompleteSubTimeout = null;
  560. }
  561. this.refresherCompleteSubTimeout = setTimeout(() => {
  562. this.$nextTick(() => {
  563. this.refresherStatus = Enum.Refresher.Default;
  564. this.isRefresherInComplete = false;
  565. })
  566. }, animateDuration * 800);
  567. }
  568. // #endif
  569. }, refresherCompleteDelay);
  570. }
  571. if (setLoading) {
  572. setTimeout(() => {
  573. this.loading = false;
  574. }, shouldEndLoadingDelay ? c.delayTime : 0);
  575. isUserPullDown && this._onRestore();
  576. }
  577. },
  578. //模拟用户手动触发下拉刷新
  579. _doRefresherRefreshAnimate() {
  580. this._cleanRefresherCompleteTimeout();
  581. // #ifndef APP-NVUE
  582. const doRefreshAnimateAfter = !this.doRefreshAnimateAfter && (this.finalShowRefresherWhenReload) && this
  583. .customRefresherHeight === -1 && this.refresherThreshold === '80rpx';
  584. if (doRefreshAnimateAfter) {
  585. this.doRefreshAnimateAfter = true;
  586. return;
  587. }
  588. // #endif
  589. this.refresherRevealStackCount ++;
  590. // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
  591. this.refresherTransform = `translateY(${this.finalRefresherThreshold}px)`;
  592. // #endif
  593. // #ifdef APP-VUE || MP-WEIXIN || MP-QQ || H5
  594. this.wxsPropType = 'begin' + u.getTime();
  595. // #endif
  596. this.moveDis = this.finalRefresherThreshold;
  597. this.refresherStatus = Enum.Refresher.Loading;
  598. this.isTouchmoving = true;
  599. this.isTouchmovingTimeout && clearTimeout(this.isTouchmovingTimeout);
  600. this._doRefresherLoad(false);
  601. },
  602. //触发下拉刷新
  603. _doRefresherLoad(isUserPullDown = true) {
  604. this._onRefresh(false,isUserPullDown);
  605. this.loading = true;
  606. },
  607. // #ifndef APP-VUE || MP-WEIXIN || MP-QQ || H5
  608. //获取处理后的moveDis
  609. _getFinalRefresherMoveDis(moveDis) {
  610. let diffDis = moveDis - this.oldCurrentMoveDis;
  611. this.oldCurrentMoveDis = moveDis;
  612. if (diffDis > 0) {
  613. diffDis = diffDis * this.finalRefresherPullRate;
  614. if (this.currentDis > this.finalRefresherThreshold) {
  615. diffDis = diffDis * (1 - this.finalRefresherOutRate);
  616. }
  617. }
  618. diffDis = diffDis > 100 ? diffDis / 100 : diffDis;
  619. this.currentDis += diffDis;
  620. this.currentDis = Math.max(0, this.currentDis);
  621. return this.currentDis;
  622. },
  623. //判断touch手势是否要触发
  624. _touchDisabled() {
  625. const checkOldScrollTop = this.oldScrollTop > 5;
  626. return this.loading || this.isRefresherInComplete || this.useChatRecordMode || !this.refresherEnabled || !this.useCustomRefresher ||(this.usePageScroll && this.useCustomRefresher && this.pageScrollTop > 10) || (!(this.usePageScroll && this.useCustomRefresher) && checkOldScrollTop);
  627. },
  628. // #endif
  629. //更新自定义下拉刷新view高度
  630. _updateCustomRefresherHeight() {
  631. this._getNodeClientRect('.zp-custom-refresher-slot-view').then((res) => {
  632. this.customRefresherHeight = res ? res[0].height : 0;
  633. this.showCustomRefresher = this.customRefresherHeight > 0;
  634. if (this.doRefreshAnimateAfter) {
  635. this.doRefreshAnimateAfter = false;
  636. this._doRefresherRefreshAnimate();
  637. }
  638. });
  639. },
  640. //发射pullingDown事件
  641. _emitTouchmove(e){
  642. // #ifndef APP-NVUE
  643. e.viewHeight = this.finalRefresherThreshold;
  644. // #endif
  645. e.rate = e.pullingDistance / e.viewHeight;
  646. this.hasTouchmove && this.$emit('refresherTouchmove',e);
  647. },
  648. //清除refresherCompleteTimeout
  649. _cleanRefresherCompleteTimeout() {
  650. this.refresherCompleteTimeout = this._cleanTimeout(this.refresherCompleteTimeout);
  651. // #ifdef APP-NVUE
  652. this._nRefresherEnd(false);
  653. // #endif
  654. },
  655. //清除refresherEndTimeout
  656. _cleanRefresherEndTimeout() {
  657. this.refresherEndTimeout = this._cleanTimeout(this.refresherEndTimeout);
  658. },
  659. }
  660. }