recycle-view.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925
  1. module.exports =
  2. /******/ (function(modules) { // webpackBootstrap
  3. /******/ // The module cache
  4. /******/ var installedModules = {};
  5. /******/
  6. /******/ // The require function
  7. /******/ function __webpack_require__(moduleId) {
  8. /******/
  9. /******/ // Check if module is in cache
  10. /******/ if(installedModules[moduleId]) {
  11. /******/ return installedModules[moduleId].exports;
  12. /******/ }
  13. /******/ // Create a new module (and put it into the cache)
  14. /******/ var module = installedModules[moduleId] = {
  15. /******/ i: moduleId,
  16. /******/ l: false,
  17. /******/ exports: {}
  18. /******/ };
  19. /******/
  20. /******/ // Execute the module function
  21. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  22. /******/
  23. /******/ // Flag the module as loaded
  24. /******/ module.l = true;
  25. /******/
  26. /******/ // Return the exports of the module
  27. /******/ return module.exports;
  28. /******/ }
  29. /******/
  30. /******/
  31. /******/ // expose the modules object (__webpack_modules__)
  32. /******/ __webpack_require__.m = modules;
  33. /******/
  34. /******/ // expose the module cache
  35. /******/ __webpack_require__.c = installedModules;
  36. /******/
  37. /******/ // define getter function for harmony exports
  38. /******/ __webpack_require__.d = function(exports, name, getter) {
  39. /******/ if(!__webpack_require__.o(exports, name)) {
  40. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  41. /******/ }
  42. /******/ };
  43. /******/
  44. /******/ // define __esModule on exports
  45. /******/ __webpack_require__.r = function(exports) {
  46. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  47. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  48. /******/ }
  49. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  50. /******/ };
  51. /******/
  52. /******/ // create a fake namespace object
  53. /******/ // mode & 1: value is a module id, require it
  54. /******/ // mode & 2: merge all properties of value into the ns
  55. /******/ // mode & 4: return value when already ns object
  56. /******/ // mode & 8|1: behave like require
  57. /******/ __webpack_require__.t = function(value, mode) {
  58. /******/ if(mode & 1) value = __webpack_require__(value);
  59. /******/ if(mode & 8) return value;
  60. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  61. /******/ var ns = Object.create(null);
  62. /******/ __webpack_require__.r(ns);
  63. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  64. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  65. /******/ return ns;
  66. /******/ };
  67. /******/
  68. /******/ // getDefaultExport function for compatibility with non-harmony modules
  69. /******/ __webpack_require__.n = function(module) {
  70. /******/ var getter = module && module.__esModule ?
  71. /******/ function getDefault() { return module['default']; } :
  72. /******/ function getModuleExports() { return module; };
  73. /******/ __webpack_require__.d(getter, 'a', getter);
  74. /******/ return getter;
  75. /******/ };
  76. /******/
  77. /******/ // Object.prototype.hasOwnProperty.call
  78. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  79. /******/
  80. /******/ // __webpack_public_path__
  81. /******/ __webpack_require__.p = "";
  82. /******/
  83. /******/
  84. /******/ // Load entry module and return exports
  85. /******/ return __webpack_require__(__webpack_require__.s = 6);
  86. /******/ })
  87. /************************************************************************/
  88. /******/ ({
  89. /***/ 0:
  90. /***/ (function(module, exports, __webpack_require__) {
  91. "use strict";
  92. var isIPhone = false;
  93. var deviceWidth = void 0;
  94. var deviceDPR = void 0;
  95. var BASE_DEVICE_WIDTH = 750;
  96. var checkDeviceWidth = function checkDeviceWidth() {
  97. var info = wx.getSystemInfoSync();
  98. // console.log('info', info)
  99. isIPhone = info.platform === 'ios';
  100. var newDeviceWidth = info.screenWidth || 375;
  101. var newDeviceDPR = info.pixelRatio || 2;
  102. if (!isIPhone) {
  103. // HACK switch width and height when landscape
  104. // const newDeviceHeight = info.screenHeight || 375
  105. // 暂时不处理转屏的情况
  106. }
  107. if (newDeviceWidth !== deviceWidth || newDeviceDPR !== deviceDPR) {
  108. deviceWidth = newDeviceWidth;
  109. deviceDPR = newDeviceDPR;
  110. // console.info('Updated device width: ' + newDeviceWidth + 'px DPR ' + newDeviceDPR)
  111. }
  112. };
  113. checkDeviceWidth();
  114. var eps = 1e-4;
  115. var transformByDPR = function transformByDPR(number) {
  116. if (number === 0) {
  117. return 0;
  118. }
  119. number = number / BASE_DEVICE_WIDTH * deviceWidth;
  120. number = Math.floor(number + eps);
  121. if (number === 0) {
  122. if (deviceDPR === 1 || !isIPhone) {
  123. return 1;
  124. }
  125. return 0.5;
  126. }
  127. return number;
  128. };
  129. var rpxRE = /([+-]?\d+(?:\.\d+)?)rpx/gi;
  130. // const inlineRpxRE = /(?::|\s|\(|\/)([+-]?\d+(?:\.\d+)?)rpx/g
  131. var transformRpx = function transformRpx(style, inline) {
  132. if (typeof style !== 'string') {
  133. return style;
  134. }
  135. var re = rpxRE;
  136. return style.replace(re, function (match, num) {
  137. return transformByDPR(Number(num)) + (inline ? 'px' : '');
  138. });
  139. };
  140. module.exports = {
  141. transformRpx: transformRpx
  142. };
  143. /***/ }),
  144. /***/ 6:
  145. /***/ (function(module, exports, __webpack_require__) {
  146. "use strict";
  147. /* eslint complexity: ["error", {"max": 50}] */
  148. /* eslint-disable indent */
  149. var DEFAULT_SHOW_SCREENS = 4;
  150. var RECT_SIZE = 200;
  151. var systemInfo = wx.getSystemInfoSync();
  152. var DEBUG = false;
  153. var transformRpx = __webpack_require__(0).transformRpx;
  154. Component({
  155. options: {
  156. multipleSlots: true // 在组件定义时的选项中启用多slot支持
  157. },
  158. relations: {
  159. '../recycle-item/recycle-item': {
  160. type: 'child', // 关联的目标节点应为子节点
  161. linked: function linked(target) {
  162. // 检查第一个的尺寸就好了吧
  163. if (!this._hasCheckSize) {
  164. this._hasCheckSize = true;
  165. var size = this.boundingClientRect(this._pos.beginIndex);
  166. if (!size) {
  167. return;
  168. }
  169. setTimeout(function () {
  170. try {
  171. target.createSelectorQuery().select('.wx-recycle-item').boundingClientRect(function (rect) {
  172. if (rect && (rect.width !== size.width || rect.height !== size.height)) {
  173. // eslint-disable-next-line no-console
  174. console.warn('[recycle-view] the size in <recycle-item> is not the same with param ' + ('itemSize, expect {width: ' + rect.width + ', height: ' + rect.height + '} but got ') + ('{width: ' + size.width + ', height: ' + size.height + '}'));
  175. }
  176. }).exec();
  177. } catch (e) {
  178. // do nothing
  179. }
  180. }, 10);
  181. }
  182. }
  183. }
  184. },
  185. /**
  186. * 组件的属性列表
  187. */
  188. properties: {
  189. debug: {
  190. type: Boolean,
  191. value: false
  192. },
  193. scrollY: {
  194. type: Boolean,
  195. value: true
  196. },
  197. batch: {
  198. type: Boolean,
  199. value: false,
  200. public: true,
  201. observer: '_recycleInnerBatchDataChanged'
  202. },
  203. batchKey: {
  204. type: String,
  205. value: 'batchSetRecycleData',
  206. public: true
  207. },
  208. scrollTop: {
  209. type: Number,
  210. value: 0,
  211. public: true,
  212. observer: '_scrollTopChanged',
  213. observeAssignments: true
  214. },
  215. height: {
  216. type: Number,
  217. value: systemInfo.windowHeight,
  218. public: true,
  219. observer: '_heightChanged'
  220. },
  221. width: {
  222. type: Number,
  223. value: systemInfo.windowWidth,
  224. public: true,
  225. observer: '_widthChanged'
  226. },
  227. // 距顶部/左边多远时,触发bindscrolltoupper
  228. upperThreshold: {
  229. type: Number,
  230. value: 50,
  231. public: true
  232. },
  233. // 距底部/右边多远时,触发bindscrolltolower
  234. lowerThreshold: {
  235. type: Number,
  236. value: 50,
  237. public: true
  238. },
  239. scrollToIndex: {
  240. type: Number,
  241. public: true,
  242. value: 0,
  243. observer: '_scrollToIndexChanged',
  244. observeAssignments: true
  245. },
  246. scrollWithAnimation: {
  247. type: Boolean,
  248. public: true,
  249. value: false
  250. },
  251. enableBackToTop: {
  252. type: Boolean,
  253. public: true,
  254. value: false
  255. },
  256. // 是否节流,默认是
  257. throttle: {
  258. type: Boolean,
  259. public: true,
  260. value: true
  261. },
  262. placeholderImage: {
  263. type: String,
  264. public: true,
  265. value: ''
  266. },
  267. screen: { // 默认渲染多少屏的数据
  268. type: Number,
  269. public: true,
  270. value: DEFAULT_SHOW_SCREENS
  271. }
  272. },
  273. /**
  274. * 组件的初始数据
  275. */
  276. data: {
  277. innerBeforeHeight: 0,
  278. innerAfterHeight: 0,
  279. innerScrollTop: 0,
  280. innerScrollIntoView: '',
  281. placeholderImageStr: '',
  282. totalHeight: 0,
  283. useInPage: false
  284. },
  285. attached: function attached() {
  286. if (this.data.placeholderImage) {
  287. this.setData({
  288. placeholderImageStr: transformRpx(this.data.placeholderImage, true)
  289. });
  290. }
  291. this.setItemSize({
  292. array: [],
  293. map: {},
  294. totalHeight: 0
  295. });
  296. },
  297. ready: function ready() {
  298. var _this = this;
  299. this._initPosition(function () {
  300. _this._isReady = true; // DOM结构ready了
  301. // 有一个更新的timer在了
  302. if (_this._updateTimerId) return;
  303. _this._scrollViewDidScroll({
  304. detail: {
  305. scrollLeft: _this._pos.left,
  306. scrollTop: _this._pos.top,
  307. ignoreScroll: true
  308. }
  309. }, true);
  310. });
  311. },
  312. detached: function detached() {
  313. this.page = null;
  314. // 销毁对应的RecycleContext
  315. if (this.context) {
  316. this.context.destroy();
  317. this.context = null;
  318. }
  319. },
  320. /**
  321. * 组件的方法列表
  322. */
  323. methods: {
  324. _log: function _log() {
  325. var _console;
  326. if (!DEBUG && !this.data.debug) return;
  327. var h = new Date();
  328. var str = h.getHours() + ':' + h.getMinutes() + ':' + h.getSeconds() + '.' + h.getMilliseconds();
  329. for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
  330. args[_key] = arguments[_key];
  331. }
  332. Array.prototype.splice.call(args, 0, 0, str);
  333. // eslint-disable-next-line no-console
  334. (_console = console).log.apply(_console, args);
  335. },
  336. _scrollToUpper: function _scrollToUpper(e) {
  337. this.triggerEvent('scrolltoupper', e.detail);
  338. },
  339. _scrollToLower: function _scrollToLower(e) {
  340. this.triggerEvent('scrolltolower', e.detail);
  341. },
  342. _beginToScroll: function _beginToScroll() {
  343. if (!this._lastScrollTop) {
  344. this._lastScrollTop = this._pos && (this._pos.top || 0);
  345. }
  346. },
  347. _clearList: function _clearList(cb) {
  348. this.currentScrollTop = 0;
  349. this._lastScrollTop = 0;
  350. var pos = this._pos;
  351. pos.beginIndex = this._pos.endIndex = -1;
  352. pos.afterHeight = pos.minTop = pos.maxTop = 0;
  353. this.page._recycleViewportChange({
  354. detail: {
  355. data: pos,
  356. id: this.id
  357. }
  358. }, cb);
  359. },
  360. // 判断RecycleContext是否Ready
  361. _isValid: function _isValid() {
  362. return this.page && this.context && this.context.isDataReady;
  363. },
  364. // eslint-disable-next-line no-complexity
  365. _scrollViewDidScroll: function _scrollViewDidScroll(e, force) {
  366. // 如果RecycleContext还没有初始化, 不做任何事情
  367. if (!this._isValid()) {
  368. return;
  369. }
  370. // 监测白屏时间
  371. if (!e.detail.ignoreScroll) {
  372. this.triggerEvent('scroll', e.detail);
  373. }
  374. this.currentScrollTop = e.detail.scrollTop;
  375. // 高度为0的情况, 不做任何渲染逻辑
  376. if (!this._pos.height || !this.sizeArray.length) {
  377. // 没有任何数据的情况下, 直接清理所有的状态
  378. this._clearList(e.detail.cb);
  379. return;
  380. }
  381. // 在scrollWithAnimation动画最后会触发一次scroll事件, 这次scroll事件必须要被忽略
  382. if (this._isScrollingWithAnimation) {
  383. this._isScrollingWithAnimation = false;
  384. return;
  385. }
  386. var pos = this._pos;
  387. var that = this;
  388. var scrollLeft = e.detail.scrollLeft;
  389. var scrollTop = e.detail.scrollTop;
  390. var scrollDistance = Math.abs(scrollTop - this._lastScrollTop);
  391. if (!force && Math.abs(scrollTop - pos.top) < pos.height * 1.5) {
  392. this._log('【not exceed height');
  393. return;
  394. }
  395. this._lastScrollTop = scrollTop;
  396. var SHOW_SCREENS = this.data.screen; // 固定4屏幕
  397. this._log('SHOW_SCREENS', SHOW_SCREENS, scrollTop);
  398. this._calcViewportIndexes(scrollLeft, scrollTop, function (beginIndex, endIndex, minTop, afterHeight, maxTop) {
  399. that._log('scrollDistance', scrollDistance, 'indexes', beginIndex, endIndex);
  400. // 渲染的数据不变
  401. if (!force && pos.beginIndex === beginIndex && pos.endIndex === endIndex && pos.minTop === minTop && pos.afterHeight === afterHeight) {
  402. that._log('------------is the same beginIndex and endIndex');
  403. return;
  404. }
  405. // 如果这次渲染的范围比上一次的范围小,则忽略
  406. that._log('【check】before setData, old pos is', pos.minTop, pos.maxTop, minTop, maxTop);
  407. that._throttle = false;
  408. pos.left = scrollLeft;
  409. pos.top = scrollTop;
  410. pos.beginIndex = beginIndex;
  411. pos.endIndex = endIndex;
  412. // console.log('render indexes', endIndex - beginIndex + 1, endIndex, beginIndex)
  413. pos.minTop = minTop;
  414. pos.maxTop = maxTop;
  415. pos.afterHeight = afterHeight;
  416. pos.ignoreBeginIndex = pos.ignoreEndIndex = -1;
  417. that.page._recycleViewportChange({
  418. detail: {
  419. data: that._pos,
  420. id: that.id
  421. }
  422. }, function () {
  423. if (e.detail.cb) {
  424. e.detail.cb();
  425. }
  426. });
  427. });
  428. },
  429. // 计算在视窗内渲染的数据
  430. _calcViewportIndexes: function _calcViewportIndexes(left, top, cb) {
  431. var that = this;
  432. // const st = +new Date
  433. this._getBeforeSlotHeight(function () {
  434. var _that$__calcViewportI = that.__calcViewportIndexes(left, top),
  435. beginIndex = _that$__calcViewportI.beginIndex,
  436. endIndex = _that$__calcViewportI.endIndex,
  437. minTop = _that$__calcViewportI.minTop,
  438. afterHeight = _that$__calcViewportI.afterHeight,
  439. maxTop = _that$__calcViewportI.maxTop;
  440. if (cb) {
  441. cb(beginIndex, endIndex, minTop, afterHeight, maxTop);
  442. }
  443. });
  444. },
  445. _getBeforeSlotHeight: function _getBeforeSlotHeight(cb) {
  446. if (typeof this.data.beforeSlotHeight !== 'undefined') {
  447. if (cb) {
  448. cb(this.data.beforeSlotHeight);
  449. }
  450. } else {
  451. this.reRender(cb);
  452. }
  453. },
  454. _getAfterSlotHeight: function _getAfterSlotHeight(cb) {
  455. if (typeof this.data.afterSlotHeight !== 'undefined') {
  456. if (cb) {
  457. cb(this.data.afterSlotHeight);
  458. }
  459. // cb && cb(this.data.afterSlotHeight)
  460. } else {
  461. this.reRender(cb);
  462. }
  463. },
  464. _getIndexes: function _getIndexes(minTop, maxTop) {
  465. if (minTop === maxTop && maxTop === 0) {
  466. return {
  467. beginIndex: -1,
  468. endIndex: -1
  469. };
  470. }
  471. var startLine = Math.floor(minTop / RECT_SIZE);
  472. var endLine = Math.ceil(maxTop / RECT_SIZE);
  473. var rectEachLine = Math.floor(this.data.width / RECT_SIZE);
  474. var beginIndex = void 0;
  475. var endIndex = void 0;
  476. var sizeMap = this.sizeMap;
  477. for (var i = startLine; i <= endLine; i++) {
  478. for (var col = 0; col < rectEachLine; col++) {
  479. var key = i + '.' + col;
  480. // 找到sizeMap里面的最小值和最大值即可
  481. if (!sizeMap[key]) continue;
  482. for (var j = 0; j < sizeMap[key].length; j++) {
  483. if (typeof beginIndex === 'undefined') {
  484. beginIndex = endIndex = sizeMap[key][j];
  485. continue;
  486. }
  487. if (beginIndex > sizeMap[key][j]) {
  488. beginIndex = sizeMap[key][j];
  489. } else if (endIndex < sizeMap[key][j]) {
  490. endIndex = sizeMap[key][j];
  491. }
  492. }
  493. }
  494. }
  495. return {
  496. beginIndex: beginIndex,
  497. endIndex: endIndex
  498. };
  499. },
  500. _isIndexValid: function _isIndexValid(beginIndex, endIndex) {
  501. if (typeof beginIndex === 'undefined' || beginIndex === -1 || typeof endIndex === 'undefined' || endIndex === -1 || endIndex >= this.sizeArray.length) {
  502. return false;
  503. }
  504. return true;
  505. },
  506. __calcViewportIndexes: function __calcViewportIndexes(left, top) {
  507. if (!this.sizeArray.length) return {};
  508. var pos = this._pos;
  509. if (typeof left === 'undefined') {
  510. left = pos.left;
  511. }
  512. if (typeof top === 'undefined') {
  513. top = pos.top;
  514. }
  515. // top = Math.max(top, this.data.beforeSlotHeight)
  516. var beforeSlotHeight = this.data.beforeSlotHeight || 0;
  517. // 和direction无关了
  518. var SHOW_SCREENS = this.data.screen;
  519. var minTop = top - pos.height * SHOW_SCREENS - beforeSlotHeight;
  520. var maxTop = top + pos.height * SHOW_SCREENS - beforeSlotHeight;
  521. // maxTop或者是minTop超出了范围
  522. if (maxTop > this.totalHeight) {
  523. minTop -= maxTop - this.totalHeight;
  524. maxTop = this.totalHeight;
  525. }
  526. if (minTop < beforeSlotHeight) {
  527. maxTop += Math.min(beforeSlotHeight - minTop, this.totalHeight);
  528. minTop = 0;
  529. }
  530. // 计算落在minTop和maxTop之间的方格有哪些
  531. var indexObj = this._getIndexes(minTop, maxTop);
  532. var beginIndex = indexObj.beginIndex;
  533. var endIndex = indexObj.endIndex;
  534. if (endIndex >= this.sizeArray.length) {
  535. endIndex = this.sizeArray.length - 1;
  536. }
  537. // 校验一下beginIndex和endIndex的有效性,
  538. if (!this._isIndexValid(beginIndex, endIndex)) {
  539. return {
  540. beginIndex: -1,
  541. endIndex: -1,
  542. minTop: 0,
  543. afterHeight: 0,
  544. maxTop: 0
  545. };
  546. }
  547. // 计算白屏的默认占位的区域
  548. var maxTopFull = this.sizeArray[endIndex].beforeHeight + this.sizeArray[endIndex].height;
  549. var minTopFull = this.sizeArray[beginIndex].beforeHeight;
  550. // console.log('render indexes', beginIndex, endIndex)
  551. var afterHeight = this.totalHeight - maxTopFull;
  552. return {
  553. beginIndex: beginIndex,
  554. endIndex: endIndex,
  555. minTop: minTopFull, // 取整, beforeHeight的距离
  556. afterHeight: afterHeight,
  557. maxTop: maxTop
  558. };
  559. },
  560. setItemSize: function setItemSize(size) {
  561. this.sizeArray = size.array;
  562. this.sizeMap = size.map;
  563. if (size.totalHeight !== this.totalHeight) {
  564. // console.log('---totalHeight is', size.totalHeight);
  565. this.setData({
  566. totalHeight: size.totalHeight,
  567. useInPage: this.useInPage || false
  568. });
  569. }
  570. this.totalHeight = size.totalHeight;
  571. },
  572. setList: function setList(key, newList) {
  573. this._currentSetDataKey = key;
  574. this._currentSetDataList = newList;
  575. },
  576. setPage: function setPage(page) {
  577. this.page = page;
  578. },
  579. forceUpdate: function forceUpdate(cb, reInit) {
  580. var _this2 = this;
  581. if (!this._isReady) {
  582. if (this._updateTimerId) {
  583. // 合并多次的forceUpdate
  584. clearTimeout(this._updateTimerId);
  585. }
  586. this._updateTimerId = setTimeout(function () {
  587. _this2.forceUpdate(cb, reInit);
  588. }, 10);
  589. return;
  590. }
  591. this._updateTimerId = null;
  592. var that = this;
  593. if (reInit) {
  594. this.reRender(function () {
  595. that._scrollViewDidScroll({
  596. detail: {
  597. scrollLeft: that._pos.left,
  598. scrollTop: that.currentScrollTop || that.data.scrollTop || 0,
  599. ignoreScroll: true,
  600. cb: cb
  601. }
  602. }, true);
  603. });
  604. } else {
  605. this._scrollViewDidScroll({
  606. detail: {
  607. scrollLeft: that._pos.left,
  608. scrollTop: that.currentScrollTop || that.data.scrollTop || 0,
  609. ignoreScroll: true,
  610. cb: cb
  611. }
  612. }, true);
  613. }
  614. },
  615. _initPosition: function _initPosition(cb) {
  616. var that = this;
  617. that._pos = {
  618. left: that.data.scrollLeft || 0,
  619. top: that.data.scrollTop || 0,
  620. width: this.data.width,
  621. height: Math.max(500, this.data.height), // 一个屏幕的高度
  622. direction: 0
  623. };
  624. this.reRender(cb);
  625. },
  626. _widthChanged: function _widthChanged(newVal) {
  627. if (!this._isReady) return newVal;
  628. this._pos.width = newVal;
  629. this.forceUpdate();
  630. return newVal;
  631. },
  632. _heightChanged: function _heightChanged(newVal) {
  633. if (!this._isReady) return newVal;
  634. this._pos.height = Math.max(500, newVal);
  635. this.forceUpdate();
  636. return newVal;
  637. },
  638. reRender: function reRender(cb) {
  639. var _this3 = this;
  640. var beforeSlotHeight = void 0;
  641. var afterSlotHeight = void 0;
  642. var that = this;
  643. // const reRenderStart = Date.now()
  644. function newCb() {
  645. if (that._lastBeforeSlotHeight !== beforeSlotHeight || that._lastAfterSlotHeight !== afterSlotHeight) {
  646. that.setData({
  647. hasBeforeSlotHeight: true,
  648. hasAfterSlotHeight: true,
  649. beforeSlotHeight: beforeSlotHeight,
  650. afterSlotHeight: afterSlotHeight
  651. });
  652. }
  653. that._lastBeforeSlotHeight = beforeSlotHeight;
  654. that._lastAfterSlotHeight = afterSlotHeight;
  655. // console.log('_getBeforeSlotHeight use time', Date.now() - reRenderStart)
  656. if (cb) {
  657. cb();
  658. }
  659. }
  660. // 重新渲染事件发生
  661. var beforeReady = false;
  662. var afterReady = false;
  663. // fix:#16 确保获取slot节点实际高度
  664. this.setData({
  665. hasBeforeSlotHeight: false,
  666. hasAfterSlotHeight: false
  667. }, function () {
  668. _this3.createSelectorQuery().select('.slot-before').boundingClientRect(function (rect) {
  669. beforeSlotHeight = rect.height;
  670. beforeReady = true;
  671. if (afterReady) {
  672. if (newCb) {
  673. newCb();
  674. }
  675. }
  676. }).exec();
  677. _this3.createSelectorQuery().select('.slot-after').boundingClientRect(function (rect) {
  678. afterSlotHeight = rect.height;
  679. afterReady = true;
  680. if (beforeReady) {
  681. if (newCb) {
  682. newCb();
  683. }
  684. }
  685. }).exec();
  686. });
  687. },
  688. _setInnerBeforeAndAfterHeight: function _setInnerBeforeAndAfterHeight(obj) {
  689. if (typeof obj.beforeHeight !== 'undefined') {
  690. this._tmpBeforeHeight = obj.beforeHeight;
  691. }
  692. if (obj.afterHeight) {
  693. this._tmpAfterHeight = obj.afterHeight;
  694. }
  695. },
  696. _recycleInnerBatchDataChanged: function _recycleInnerBatchDataChanged(cb) {
  697. var _this4 = this;
  698. if (typeof this._tmpBeforeHeight !== 'undefined') {
  699. var setObj = {
  700. innerBeforeHeight: this._tmpBeforeHeight || 0,
  701. innerAfterHeight: this._tmpAfterHeight || 0
  702. };
  703. if (typeof this._tmpInnerScrollTop !== 'undefined') {
  704. setObj.innerScrollTop = this._tmpInnerScrollTop;
  705. }
  706. var pageObj = {};
  707. var hasPageData = false;
  708. if (typeof this._currentSetDataKey !== 'undefined') {
  709. pageObj[this._currentSetDataKey] = this._currentSetDataList;
  710. hasPageData = true;
  711. }
  712. var saveScrollWithAnimation = this.data.scrollWithAnimation;
  713. var groupSetData = function groupSetData() {
  714. // 如果有分页数据的话
  715. if (hasPageData) {
  716. _this4.page.setData(pageObj);
  717. }
  718. _this4.setData(setObj, function () {
  719. _this4.setData({
  720. scrollWithAnimation: saveScrollWithAnimation
  721. });
  722. if (typeof cb === 'function') {
  723. cb();
  724. }
  725. });
  726. };
  727. groupSetData();
  728. delete this._currentSetDataKey;
  729. delete this._currentSetDataList;
  730. this._tmpBeforeHeight = undefined;
  731. this._tmpAfterHeight = undefined;
  732. this._tmpInnerScrollTop = undefined;
  733. }
  734. },
  735. _renderByScrollTop: function _renderByScrollTop(scrollTop) {
  736. // 先setData把目标位置的数据补齐
  737. this._scrollViewDidScroll({
  738. detail: {
  739. scrollLeft: this._pos.scrollLeft,
  740. scrollTop: scrollTop,
  741. ignoreScroll: true
  742. }
  743. }, true);
  744. if (this.data.scrollWithAnimation) {
  745. this._isScrollingWithAnimation = true;
  746. }
  747. this.setData({
  748. innerScrollTop: scrollTop
  749. });
  750. },
  751. _scrollTopChanged: function _scrollTopChanged(newVal, oldVal) {
  752. var _this5 = this;
  753. // if (newVal === oldVal && newVal === 0) return
  754. if (!this._isInitScrollTop && newVal === 0) {
  755. this._isInitScrollTop = true;
  756. return newVal;
  757. }
  758. this.currentScrollTop = newVal;
  759. if (!this._isReady) {
  760. if (this._scrollTopTimerId) {
  761. clearTimeout(this._scrollTopTimerId);
  762. }
  763. this._scrollTopTimerId = setTimeout(function () {
  764. _this5._scrollTopChanged(newVal, oldVal);
  765. }, 10);
  766. return newVal;
  767. }
  768. this._isInitScrollTop = true;
  769. this._scrollTopTimerId = null;
  770. // this._lastScrollTop = oldVal
  771. if (typeof this._lastScrollTop === 'undefined') {
  772. this._lastScrollTop = this.data.scrollTop;
  773. }
  774. // 滑动距离小于一个屏幕的高度, 直接setData
  775. if (Math.abs(newVal - this._lastScrollTop) < this._pos.height) {
  776. this.setData({
  777. innerScrollTop: newVal
  778. });
  779. return newVal;
  780. }
  781. if (!this._isScrollTopChanged) {
  782. // 首次的值需要延后一点执行才能生效
  783. setTimeout(function () {
  784. _this5._isScrollTopChanged = true;
  785. _this5._renderByScrollTop(newVal);
  786. }, 10);
  787. } else {
  788. this._renderByScrollTop(newVal);
  789. }
  790. return newVal;
  791. },
  792. _scrollToIndexChanged: function _scrollToIndexChanged(newVal, oldVal) {
  793. var _this6 = this;
  794. // if (newVal === oldVal && newVal === 0) return
  795. // 首次滚动到0的不执行
  796. if (!this._isInitScrollToIndex && newVal === 0) {
  797. this._isInitScrollToIndex = true;
  798. return newVal;
  799. }
  800. if (!this._isReady) {
  801. if (this._scrollToIndexTimerId) {
  802. clearTimeout(this._scrollToIndexTimerId);
  803. }
  804. this._scrollToIndexTimerId = setTimeout(function () {
  805. _this6._scrollToIndexChanged(newVal, oldVal);
  806. }, 10);
  807. return newVal;
  808. }
  809. this._isInitScrollToIndex = true;
  810. this._scrollToIndexTimerId = null;
  811. if (typeof this._lastScrollTop === 'undefined') {
  812. this._lastScrollTop = this.data.scrollTop;
  813. }
  814. var rect = this.boundingClientRect(newVal);
  815. if (!rect) return newVal;
  816. // console.log('rect top', rect, this.data.beforeSlotHeight)
  817. var calScrollTop = rect.top + (this.data.beforeSlotHeight || 0);
  818. this.currentScrollTop = calScrollTop;
  819. if (Math.abs(calScrollTop - this._lastScrollTop) < this._pos.height) {
  820. this.setData({
  821. innerScrollTop: calScrollTop
  822. });
  823. return newVal;
  824. }
  825. if (!this._isScrollToIndexChanged) {
  826. setTimeout(function () {
  827. _this6._isScrollToIndexChanged = true;
  828. _this6._renderByScrollTop(calScrollTop);
  829. }, 10);
  830. } else {
  831. this._renderByScrollTop(calScrollTop);
  832. }
  833. return newVal;
  834. },
  835. // 提供给开发者使用的接口
  836. boundingClientRect: function boundingClientRect(idx) {
  837. if (idx < 0 || idx >= this.sizeArray.length) {
  838. return null;
  839. }
  840. return {
  841. left: 0,
  842. top: this.sizeArray[idx].beforeHeight,
  843. width: this.sizeArray[idx].width,
  844. height: this.sizeArray[idx].height
  845. };
  846. },
  847. // 获取当前出现在屏幕内数据项, 返回数据项组成的数组
  848. // 参数inViewportPx表示当数据项至少有多少像素出现在屏幕内才算是出现在屏幕内,默认是1
  849. getIndexesInViewport: function getIndexesInViewport(inViewportPx) {
  850. if (!inViewportPx) {
  851. inViewportPx = 1;
  852. }
  853. var scrollTop = this.currentScrollTop;
  854. var minTop = scrollTop + inViewportPx;
  855. if (minTop < 0) minTop = 0;
  856. var maxTop = scrollTop + this.data.height - inViewportPx;
  857. if (maxTop > this.totalHeight) maxTop = this.totalHeight;
  858. var indexes = [];
  859. for (var i = 0; i < this.sizeArray.length; i++) {
  860. if (this.sizeArray[i].beforeHeight + this.sizeArray[i].height >= minTop && this.sizeArray[i].beforeHeight <= maxTop) {
  861. indexes.push(i);
  862. }
  863. if (this.sizeArray[i].beforeHeight > maxTop) break;
  864. }
  865. return indexes;
  866. },
  867. getTotalHeight: function getTotalHeight() {
  868. return this.totalHeight;
  869. },
  870. setUseInPage: function setUseInPage(useInPage) {
  871. this.useInPage = useInPage;
  872. },
  873. setPlaceholderImage: function setPlaceholderImage(svgs, size) {
  874. var fill = 'style=\'fill:rgb(204,204,204);\'';
  875. var placeholderImages = ['data:image/svg+xml,%3Csvg height=\'' + size.height + '\' width=\'' + size.width + '\' xmlns=\'http://www.w3.org/2000/svg\'%3E'];
  876. svgs.forEach(function (svg) {
  877. placeholderImages.push('%3Crect width=\'' + svg.width + '\' x=\'' + svg.left + '\' height=\'' + svg.height + '\' y=\'' + svg.top + '\' ' + fill + ' /%3E');
  878. });
  879. placeholderImages.push('%3C/svg%3E');
  880. this.setData({
  881. placeholderImageStr: placeholderImages.join('')
  882. });
  883. }
  884. }
  885. });
  886. /***/ })
  887. /******/ });