tencent.jquery.pix.component 1.0.63 → 1.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import "./banner.scss"
|
|
2
|
-
import {
|
|
2
|
+
import { windowEnv, getEnv } from "../config";
|
|
3
3
|
import { addResizeFunc, nextAnimationFrame, removeResizeFunc } from "../utils/utils";
|
|
4
4
|
|
|
5
|
+
let $ = null;
|
|
6
|
+
|
|
5
7
|
/**
|
|
6
8
|
* 在页面上放置一个banner组件
|
|
7
9
|
* @constructor
|
|
@@ -17,7 +19,9 @@ import { addResizeFunc, nextAnimationFrame, removeResizeFunc } from "../utils/ut
|
|
|
17
19
|
* @param {function} [options.pageChanged] 翻页时触发,第一个参数为新生效页面元素的jq,第二个参数为 options.list 中对应的项,第三个参数为当前页码
|
|
18
20
|
* @param {function} [options.renderCallback] 自定义渲染回调,第一个参数为 options.list 中对应的项,第二个参数为当前页码,返回值为渲染后的元素
|
|
19
21
|
*/
|
|
20
|
-
export function Banner(options = {}){
|
|
22
|
+
export function Banner(options = {}) {
|
|
23
|
+
$ = getEnv().$;
|
|
24
|
+
|
|
21
25
|
this.options = options;
|
|
22
26
|
this.options.isTitleEnabled ??= true;
|
|
23
27
|
this.options.autoResize ??= true;
|
|
@@ -30,7 +34,7 @@ export function Banner(options = {}){
|
|
|
30
34
|
this.init();
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
Banner.prototype.setWidth = function(width) {
|
|
37
|
+
Banner.prototype.setWidth = function (width) {
|
|
34
38
|
this.signWidth = width;
|
|
35
39
|
this.allWidth = this.signWidth * (this.options.list.length + 2);
|
|
36
40
|
$(this.options.container).width(width);
|
|
@@ -39,7 +43,7 @@ Banner.prototype.setWidth = function(width) {
|
|
|
39
43
|
this.setTranslate(-this.signWidth * (this.index + 1));
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
Banner.prototype.fitWidth = function() {
|
|
46
|
+
Banner.prototype.fitWidth = function () {
|
|
43
47
|
console.log('banner fitWidth');
|
|
44
48
|
const newWidth = $(this.options.container).parent().width();
|
|
45
49
|
if (newWidth != this.signWidth) {
|
|
@@ -50,7 +54,7 @@ Banner.prototype.fitWidth = function() {
|
|
|
50
54
|
/**
|
|
51
55
|
* 跳转到上一页
|
|
52
56
|
*/
|
|
53
|
-
Banner.prototype.prevPage = async function() {
|
|
57
|
+
Banner.prototype.prevPage = async function () {
|
|
54
58
|
this.pauseAutoPlay();
|
|
55
59
|
await this.gotoPageUnchecked(this.index - 1);
|
|
56
60
|
this.startAutoPlay();
|
|
@@ -59,7 +63,7 @@ Banner.prototype.prevPage = async function() {
|
|
|
59
63
|
/**
|
|
60
64
|
* 跳转到下一页
|
|
61
65
|
*/
|
|
62
|
-
Banner.prototype.nextPage = async function() {
|
|
66
|
+
Banner.prototype.nextPage = async function () {
|
|
63
67
|
this.pauseAutoPlay();
|
|
64
68
|
await this.gotoPageUnchecked(this.index + 1);
|
|
65
69
|
this.startAutoPlay();
|
|
@@ -69,7 +73,7 @@ Banner.prototype.nextPage = async function() {
|
|
|
69
73
|
* 跳转到指定页
|
|
70
74
|
* @param {number} newIdx 要切换到的页码 [0, len)
|
|
71
75
|
*/
|
|
72
|
-
Banner.prototype.gotoPage = async function(newIdx) {
|
|
76
|
+
Banner.prototype.gotoPage = async function (newIdx) {
|
|
73
77
|
if (newIdx < 0 || newIdx >= this.options.list.length) {
|
|
74
78
|
throw new Error('index out of range');
|
|
75
79
|
}
|
|
@@ -81,7 +85,7 @@ Banner.prototype.gotoPage = async function(newIdx) {
|
|
|
81
85
|
this.startAutoPlay();
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
Banner.prototype.gotoPageUnchecked = async function(newIdx) {
|
|
88
|
+
Banner.prototype.gotoPageUnchecked = async function (newIdx) {
|
|
85
89
|
const len = this.options.list.length;
|
|
86
90
|
let wrap = 0;
|
|
87
91
|
if (newIdx <= -1) {
|
|
@@ -100,14 +104,14 @@ Banner.prototype.gotoPageUnchecked = async function(newIdx) {
|
|
|
100
104
|
}
|
|
101
105
|
|
|
102
106
|
this.$inner.css('transition', 'transform 0.3s ease-out');
|
|
103
|
-
const newTranslate = -this.signWidth * (newIdx+1);
|
|
107
|
+
const newTranslate = -this.signWidth * (newIdx + 1);
|
|
104
108
|
this.setTranslate(newTranslate);
|
|
105
109
|
if (this.options.isTitleEnabled) {
|
|
106
|
-
this.$pagination.children().eq(newIdx+1).addClass('active').siblings().removeClass('active');
|
|
110
|
+
this.$pagination.children().eq(newIdx + 1).addClass('active').siblings().removeClass('active');
|
|
107
111
|
this.$titleBox.text(this.options.list[newIdx].title);
|
|
108
112
|
}
|
|
109
113
|
if (this.options.pageChanged && newIdx != this.index) {
|
|
110
|
-
this.options.pageChanged(this.$inner.children().eq(newIdx+1), this.options.list[newIdx], newIdx);
|
|
114
|
+
this.options.pageChanged(this.$inner.children().eq(newIdx + 1), this.options.list[newIdx], newIdx);
|
|
111
115
|
}
|
|
112
116
|
this.index = newIdx;
|
|
113
117
|
}
|
|
@@ -116,11 +120,11 @@ Banner.prototype.gotoPageUnchecked = async function(newIdx) {
|
|
|
116
120
|
* 获取当前页的序号
|
|
117
121
|
* @returns {number} 当前页的序号 [0,len]
|
|
118
122
|
*/
|
|
119
|
-
Banner.prototype.getCurrentPage = function() {
|
|
123
|
+
Banner.prototype.getCurrentPage = function () {
|
|
120
124
|
return this.index;
|
|
121
125
|
}
|
|
122
126
|
|
|
123
|
-
Banner.prototype.init = function(){
|
|
127
|
+
Banner.prototype.init = function () {
|
|
124
128
|
const $t = $(this.options.container)
|
|
125
129
|
console.log('banner container:', $t.dom);
|
|
126
130
|
const signWidth = $t.width()
|
|
@@ -139,10 +143,10 @@ Banner.prototype.init = function(){
|
|
|
139
143
|
});
|
|
140
144
|
}
|
|
141
145
|
this.startAutoPlay();
|
|
142
|
-
console.log('list:', this.options.list, ' signWidth:',signWidth)
|
|
146
|
+
console.log('list:', this.options.list, ' signWidth:', signWidth)
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
Banner.prototype.startAutoPlay = function() {
|
|
149
|
+
Banner.prototype.startAutoPlay = function () {
|
|
146
150
|
if (this.options.durationMs > 0 && this.timer == null) {
|
|
147
151
|
this.timer = setInterval(() => {
|
|
148
152
|
this.nextPage()
|
|
@@ -150,7 +154,7 @@ Banner.prototype.startAutoPlay = function() {
|
|
|
150
154
|
}
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
Banner.prototype.pauseAutoPlay = function() {
|
|
157
|
+
Banner.prototype.pauseAutoPlay = function () {
|
|
154
158
|
if (this.timer == null) {
|
|
155
159
|
return;
|
|
156
160
|
}
|
|
@@ -158,25 +162,25 @@ Banner.prototype.pauseAutoPlay = function() {
|
|
|
158
162
|
this.timer = null;
|
|
159
163
|
}
|
|
160
164
|
|
|
161
|
-
Banner.prototype.createHtml = function(){
|
|
165
|
+
Banner.prototype.createHtml = function () {
|
|
162
166
|
const len = this.options.list.length
|
|
163
|
-
if(len<1){
|
|
167
|
+
if (len < 1) {
|
|
164
168
|
return
|
|
165
169
|
}
|
|
166
170
|
const $t = $(this.options.container)
|
|
167
171
|
$t.empty();
|
|
168
172
|
const signWidth = this.signWidth = $t.width()
|
|
169
|
-
|
|
173
|
+
|
|
170
174
|
const allWidth = this.allWidth = signWidth * (len + 2)
|
|
171
175
|
const $inner = this.$inner = $(`<div class="banner-inner-transform"></div>`)
|
|
172
176
|
this.currentTranslate = -signWidth * (this.index + 1)
|
|
173
|
-
$inner.width(allWidth).css('transform'
|
|
177
|
+
$inner.width(allWidth).css('transform', `translateX(${this.currentTranslate}px)`)
|
|
174
178
|
|
|
175
179
|
// 加第0个位置
|
|
176
|
-
$inner.append(`<div class="banner-inner-li" data-background-url="${this.options.list[len-1].url}" style="width:${this.signWidth}px;">
|
|
180
|
+
$inner.append(`<div class="banner-inner-li" data-background-url="${this.options.list[len - 1].url}" style="width:${this.signWidth}px;">
|
|
177
181
|
</div>
|
|
178
182
|
`)
|
|
179
|
-
for(let i=0;i<len;i++){
|
|
183
|
+
for (let i = 0; i < len; i++) {
|
|
180
184
|
const item = this.options.list[i]
|
|
181
185
|
const $li = $(`<div class="banner-inner-li" data-background-url="${item.url}" style="width:${this.signWidth}px;">
|
|
182
186
|
</div>
|
|
@@ -210,7 +214,7 @@ Banner.prototype.createHtml = function(){
|
|
|
210
214
|
this.$titleBox = $titleBox
|
|
211
215
|
this.$pagination = $pagination
|
|
212
216
|
|
|
213
|
-
$pagination.children().eq(this.index+1).addClass('active').siblings().removeClass('active')
|
|
217
|
+
$pagination.children().eq(this.index + 1).addClass('active').siblings().removeClass('active')
|
|
214
218
|
|
|
215
219
|
$t.append($pagination)
|
|
216
220
|
}
|
|
@@ -223,21 +227,21 @@ Banner.prototype.createHtml = function(){
|
|
|
223
227
|
/**
|
|
224
228
|
* 由 this.options.renderCallback 生成页面
|
|
225
229
|
*/
|
|
226
|
-
Banner.prototype.createCustomHtml = function() {
|
|
230
|
+
Banner.prototype.createCustomHtml = function () {
|
|
227
231
|
const len = this.options.list.length
|
|
228
|
-
if(len<1){
|
|
232
|
+
if (len < 1) {
|
|
229
233
|
return
|
|
230
234
|
}
|
|
231
235
|
const $t = $(this.options.container)
|
|
232
236
|
$t.empty();
|
|
233
237
|
const signWidth = this.signWidth = $t.width()
|
|
234
|
-
|
|
238
|
+
|
|
235
239
|
const allWidth = this.allWidth = signWidth * (len + 2)
|
|
236
240
|
const $inner = this.$inner = $(`<div class="banner-inner-transform"></div>`)
|
|
237
241
|
this.currentTranslate = -signWidth * (this.index + 1)
|
|
238
|
-
$inner.width(allWidth).css('transform'
|
|
242
|
+
$inner.width(allWidth).css('transform', `translateX(${this.currentTranslate}px)`)
|
|
239
243
|
|
|
240
|
-
let $li = $(this.options.renderCallback(this.options.list[len-1], len-1));
|
|
244
|
+
let $li = $(this.options.renderCallback(this.options.list[len - 1], len - 1));
|
|
241
245
|
$li.width(signWidth);
|
|
242
246
|
$inner.append($li);
|
|
243
247
|
|
|
@@ -254,7 +258,7 @@ Banner.prototype.createCustomHtml = function() {
|
|
|
254
258
|
$t.append($inner);
|
|
255
259
|
}
|
|
256
260
|
|
|
257
|
-
Banner.prototype.bindEvent = function(){
|
|
261
|
+
Banner.prototype.bindEvent = function () {
|
|
258
262
|
const self = this
|
|
259
263
|
const len = this.options.list.length;
|
|
260
264
|
const $inner = this.$inner;
|
|
@@ -317,7 +321,7 @@ Banner.prototype.bindEvent = function(){
|
|
|
317
321
|
} else if (newTranslate < -this.signWidth * len) {
|
|
318
322
|
newTranslate += this.signWidth * len;
|
|
319
323
|
}
|
|
320
|
-
|
|
324
|
+
|
|
321
325
|
// 在移动到新位置前,先变换当前位置到正确区域
|
|
322
326
|
await this.normalizeTranslate();
|
|
323
327
|
} else {
|
|
@@ -325,7 +329,7 @@ Banner.prototype.bindEvent = function(){
|
|
|
325
329
|
}
|
|
326
330
|
|
|
327
331
|
$inner.css('transition', 'transform 0.3s ease-out')
|
|
328
|
-
$inner.css('transform'
|
|
332
|
+
$inner.css('transform', `translateX(${this.currentTranslate}px)`);
|
|
329
333
|
|
|
330
334
|
if (newTranslate !== this.currentTranslate) {
|
|
331
335
|
this.setTranslate(newTranslate);
|
|
@@ -335,11 +339,11 @@ Banner.prototype.bindEvent = function(){
|
|
|
335
339
|
// 由于 newTranslate 包含了前后的循环占位(位置 0 和 len+1 ),需要减1后取模
|
|
336
340
|
const newIndex = (Math.round(-newTranslate / this.signWidth) + len - 1) % len;
|
|
337
341
|
if (this.options.isTitleEnabled) {
|
|
338
|
-
this.$pagination.children().eq(newIndex+1).addClass('active').siblings().removeClass('active');
|
|
342
|
+
this.$pagination.children().eq(newIndex + 1).addClass('active').siblings().removeClass('active');
|
|
339
343
|
this.$titleBox.text(this.options.list[newIndex].title);
|
|
340
344
|
}
|
|
341
345
|
if (this.options.pageChanged && newIndex != this.index) {
|
|
342
|
-
this.options.pageChanged(this.$inner.children().eq(newIndex+1), this.options.list[newIndex], newIndex);
|
|
346
|
+
this.options.pageChanged(this.$inner.children().eq(newIndex + 1), this.options.list[newIndex], newIndex);
|
|
343
347
|
}
|
|
344
348
|
this.index = newIndex;
|
|
345
349
|
this.startAutoPlay();
|
|
@@ -350,15 +354,15 @@ Banner.prototype.bindEvent = function(){
|
|
|
350
354
|
if (windowEnv === 'h5') { // 浏览器环境
|
|
351
355
|
$inner
|
|
352
356
|
.attr('draggable', 'false')
|
|
353
|
-
.on('pointerdown', function(e) {
|
|
357
|
+
.on('pointerdown', function (e) {
|
|
354
358
|
pStart(e.originalEvent.clientX, () => {
|
|
355
359
|
this.setPointerCapture(e.originalEvent.pointerId);
|
|
356
360
|
});
|
|
357
361
|
})
|
|
358
|
-
.on('pointermove', function(e) {
|
|
362
|
+
.on('pointermove', function (e) {
|
|
359
363
|
pMove(e.originalEvent.clientX);
|
|
360
364
|
})
|
|
361
|
-
.on('pointerup', async function(e) {
|
|
365
|
+
.on('pointerup', async function (e) {
|
|
362
366
|
await pEnd(() => {
|
|
363
367
|
this.releasePointerCapture(e.originalEvent.pointerId);
|
|
364
368
|
});
|
|
@@ -380,23 +384,23 @@ Banner.prototype.bindEvent = function(){
|
|
|
380
384
|
$inner.on('transitionend', () => {
|
|
381
385
|
$inner.css('transition', 'none');
|
|
382
386
|
})
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
387
|
+
.on('click', function (e) {
|
|
388
|
+
if (!self.options.click) {
|
|
389
|
+
return
|
|
390
|
+
}
|
|
391
|
+
if (windowEnv === 'h5' && Math.abs(e.clientX - originalX) > 10) {
|
|
392
|
+
// 防止浏览器中拖动时触发click
|
|
393
|
+
return
|
|
394
|
+
}
|
|
391
395
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
396
|
+
const $t = self.$inner.children().eq(self.index + 1);
|
|
397
|
+
self.options.click($t, self.options.list[self.index], self.index);
|
|
398
|
+
});
|
|
395
399
|
}
|
|
396
400
|
|
|
397
401
|
Banner.prototype.setTranslate = function (transX) {
|
|
398
402
|
this.currentTranslate = transX;
|
|
399
|
-
this.$inner.css('transform'
|
|
403
|
+
this.$inner.css('transform', `translateX(${this.currentTranslate}px)`);
|
|
400
404
|
}
|
|
401
405
|
|
|
402
406
|
/**
|
|
@@ -420,7 +424,7 @@ Banner.prototype.normalizeTranslate = async function () {
|
|
|
420
424
|
* 加载所有背景图片,将data-background-url属性设置为背景图片
|
|
421
425
|
*/
|
|
422
426
|
Banner.prototype.loadAllBackgrounds = function () {
|
|
423
|
-
this.$inner.children().each(function() {
|
|
427
|
+
this.$inner.children().each(function () {
|
|
424
428
|
$(this).css('background-image', `url(${$(this).attr('data-background-url')})`)
|
|
425
429
|
});
|
|
426
430
|
// console.log('banner images loaded');
|
package/components/list/list.js
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getEnv } from "../config";
|
|
2
|
+
|
|
3
|
+
let $ = null;
|
|
4
|
+
|
|
2
5
|
// 默认配置
|
|
3
6
|
const DEFAULTS = {
|
|
4
7
|
itemHeight: 50, // 单条数据高度
|
|
@@ -14,6 +17,8 @@ const DEFAULTS = {
|
|
|
14
17
|
};
|
|
15
18
|
|
|
16
19
|
export function List(options = {}) {
|
|
20
|
+
$ = getEnv().$;
|
|
21
|
+
|
|
17
22
|
this.options = Object.assign({}, DEFAULTS, options);
|
|
18
23
|
// 标记是否有更新元素用的回调函数
|
|
19
24
|
this.hasUpdateItem = options.updateItem && (options.updateItem.constructor === Function) ? true : false;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import "./waterfall.scss"
|
|
1
2
|
import { getEnv } from "../config.js";
|
|
2
3
|
import { remToPx } from "../../utils/utils.js";
|
|
3
4
|
|
|
@@ -19,6 +20,10 @@ const DEFAULTS = {
|
|
|
19
20
|
// 传入 $node, data, index
|
|
20
21
|
updateItem: null, // 元素更新时的回调函数
|
|
21
22
|
onscroll: null, // 滚动事件回调函数
|
|
23
|
+
shouldOccupySpace: null, // 是否是静态数据的回调函数,静态数据能够占用元素
|
|
24
|
+
showLoading: null, // 展示loading的回调函数 params:$node
|
|
25
|
+
hideLoading: null, // 隐藏loading的回调函数
|
|
26
|
+
createLoading: null, // 创建loading的回调函数
|
|
22
27
|
};
|
|
23
28
|
|
|
24
29
|
export function Waterfall(optionsInput = {}) {
|
|
@@ -30,6 +35,16 @@ export function Waterfall(optionsInput = {}) {
|
|
|
30
35
|
// 标记是否有更新元素用的回调函数
|
|
31
36
|
this.hasUpdateItem = options.updateItem && (options.updateItem.constructor === Function) ? true : false;
|
|
32
37
|
|
|
38
|
+
// 新方案:数据ID映射机制
|
|
39
|
+
this.dataIdMap = new Map(); // 数据ID -> 布局信息映射
|
|
40
|
+
this.nextDataId = 0; // 下一个数据ID
|
|
41
|
+
this.renderedDataIds = new Set(); // 已渲染的数据ID集合
|
|
42
|
+
|
|
43
|
+
this.$loadingNode = null;
|
|
44
|
+
|
|
45
|
+
this.isShowLoading = false; // 是否展示loading
|
|
46
|
+
|
|
47
|
+
|
|
33
48
|
// 间隔字符串转数字
|
|
34
49
|
if (options.columnGap.constructor === String) {
|
|
35
50
|
// 如果是rem单位,则需要计算
|
|
@@ -85,13 +100,29 @@ export function Waterfall(optionsInput = {}) {
|
|
|
85
100
|
this.columnItems.push(this.createColumn(i));
|
|
86
101
|
}
|
|
87
102
|
|
|
88
|
-
this.renderIndex = 0; //
|
|
89
|
-
this.activeNodes = new Map(); //
|
|
103
|
+
this.renderIndex = 0; // 渲染索引(保留兼容性)
|
|
104
|
+
this.activeNodes = new Map(); // 当前活跃节点(数据ID -> DOM)
|
|
90
105
|
this.nodePool = []; // DOM 节点池
|
|
91
106
|
|
|
107
|
+
// 新方案:初始化数据ID映射
|
|
108
|
+
this.dataIdMap = new Map(); // 数据ID -> 布局信息映射
|
|
109
|
+
this.nextDataId = 0; // 下一个数据ID
|
|
110
|
+
this.renderedDataIds = new Set(); // 已渲染的数据ID集合
|
|
111
|
+
|
|
112
|
+
// 为初始数据分配数据ID
|
|
113
|
+
this.options.data.forEach((item, index) => {
|
|
114
|
+
const dataId = index; //this.nextDataId++;
|
|
115
|
+
this.dataIdMap.set(dataId, {
|
|
116
|
+
data: true,//item,
|
|
117
|
+
originalIndex: index,
|
|
118
|
+
layoutInfo: null // 将在布局时填充
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
92
122
|
this.init();
|
|
93
123
|
}
|
|
94
124
|
|
|
125
|
+
|
|
95
126
|
Waterfall.prototype.init = function () {
|
|
96
127
|
const self = this;
|
|
97
128
|
const options = this.options;
|
|
@@ -99,8 +130,8 @@ Waterfall.prototype.init = function () {
|
|
|
99
130
|
|
|
100
131
|
this.nodePool = []; // DOM 节点池
|
|
101
132
|
this.activeNodes = new Map(); // 当前活跃节点(索引 -> DOM)
|
|
102
|
-
|
|
103
|
-
|
|
133
|
+
this.allReadyNodes = new Map(); // 所有节点(索引 -> DOM)
|
|
134
|
+
this.renderIndex = 0; // 渲染索引(保留兼容性)
|
|
104
135
|
|
|
105
136
|
$container.html(`
|
|
106
137
|
<div class="waterfall-list-scroll" style="">
|
|
@@ -108,6 +139,19 @@ Waterfall.prototype.init = function () {
|
|
|
108
139
|
</div>
|
|
109
140
|
`);
|
|
110
141
|
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
// 如果有定义loading函数 那么创建一个loading节点元素
|
|
145
|
+
console.log('options.createLoading', options.createLoading)
|
|
146
|
+
if (options.createLoading) {
|
|
147
|
+
this.$loadingNode = $(
|
|
148
|
+
`<div class="waterfall-loading" style="transform: translate(0px, -99999px)"></div>`
|
|
149
|
+
);
|
|
150
|
+
$container.find('.waterfall-list-viewport').append(this.$loadingNode);
|
|
151
|
+
|
|
152
|
+
options.createLoading(this.$loadingNode);
|
|
153
|
+
}
|
|
154
|
+
|
|
111
155
|
// 绑定滚动事件(节流处理)
|
|
112
156
|
$container.off().on('scroll', function () {
|
|
113
157
|
self.scrollTop = $(this).scrollTop();
|
|
@@ -134,8 +178,9 @@ Waterfall.prototype.updateVisibleItems = function (force = false) {
|
|
|
134
178
|
const options = this.options;
|
|
135
179
|
const $container = $(options.container);
|
|
136
180
|
|
|
137
|
-
const startTop =
|
|
181
|
+
const startTop = self.scrollTop; // 当前滚动位置
|
|
138
182
|
const endTop = startTop + $container.height();
|
|
183
|
+
// console.log('startTop', startTop)
|
|
139
184
|
|
|
140
185
|
// 进行可见区域的渲染更新
|
|
141
186
|
this.updateCardsInView({
|
|
@@ -143,23 +188,44 @@ Waterfall.prototype.updateVisibleItems = function (force = false) {
|
|
|
143
188
|
end: endTop,
|
|
144
189
|
force
|
|
145
190
|
});
|
|
191
|
+
|
|
146
192
|
}
|
|
147
193
|
|
|
148
194
|
// 新增卡片
|
|
149
|
-
Waterfall.prototype.appendCard = function (data,
|
|
195
|
+
Waterfall.prototype.appendCard = function (data, dataId, { top, left }) {
|
|
150
196
|
const self = this;
|
|
151
197
|
const options = this.options;
|
|
152
198
|
const $container = $(options.container);
|
|
153
199
|
const $viewport = $container.find('.waterfall-list-viewport');
|
|
200
|
+
|
|
201
|
+
// 新方案:基于数据ID的数据验证
|
|
202
|
+
if (!this.dataIdMap.has(dataId)) {
|
|
203
|
+
console.error('Waterfall: Invalid dataId in appendCard', dataId);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const dataInfo = this.dataIdMap.get(dataId);
|
|
208
|
+
if (!dataInfo || !dataInfo.data) {
|
|
209
|
+
console.warn('Waterfall: Empty data for dataId', dataId);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
154
213
|
const $card = $(
|
|
155
214
|
`<div class="waterfall-item"
|
|
156
|
-
data-index="${
|
|
157
|
-
style="position: absolute;transform:translate(${left}px,${top}px);
|
|
215
|
+
data-index="${dataId}"
|
|
216
|
+
style="position: absolute;transform:translate(${left}px,${top}px);"
|
|
158
217
|
>
|
|
159
|
-
${options.renderItem(data,
|
|
218
|
+
${options.renderItem(data, dataInfo.originalIndex)}
|
|
160
219
|
</div> `
|
|
161
220
|
);
|
|
162
221
|
|
|
222
|
+
this.renderedDataIds.add(dataId);
|
|
223
|
+
|
|
224
|
+
if (options.columns !== 1) {
|
|
225
|
+
$card.width(this.columnWidth + 'px');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
|
|
163
229
|
$viewport.append($card);
|
|
164
230
|
return $card;
|
|
165
231
|
}
|
|
@@ -174,49 +240,105 @@ Waterfall.prototype.updateCardsInView = function ({ start, end, force = false })
|
|
|
174
240
|
this.createCards({ end: endBuffer });
|
|
175
241
|
}
|
|
176
242
|
|
|
177
|
-
|
|
178
|
-
|
|
243
|
+
const startNum = start - options.bufferHeight;
|
|
244
|
+
const endNum = end + options.bufferHeight;
|
|
245
|
+
// 新方案:基于数据ID映射机制
|
|
179
246
|
const newActiveNodes = new Map();
|
|
180
247
|
for (let i = 0; i < this.columns; i++) {
|
|
181
248
|
const column = this.columnItems[i];
|
|
182
249
|
|
|
183
250
|
for (let j = 0; j < column.children.length; j++) {
|
|
184
251
|
const row = column.children[j];
|
|
185
|
-
const
|
|
252
|
+
const dataId = row.dataId; // 使用dataId替代renderIndex
|
|
253
|
+
|
|
254
|
+
// 验证数据ID有效性
|
|
255
|
+
if (!this.dataIdMap.has(dataId)) {
|
|
256
|
+
console.warn('Waterfall: Invalid dataId detected', dataId);
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const dataInfo = this.dataIdMap.get(dataId);
|
|
261
|
+
|
|
262
|
+
if (!dataInfo) {
|
|
263
|
+
console.warn('Waterfall: Invalid data for dataId', dataId);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const data = options.data[dataId]
|
|
268
|
+
|
|
269
|
+
// 如果当前这个节点是特殊节点 不用做处理
|
|
270
|
+
// 如果是特殊的静态占用元素卡片,需要指定节点不变更的数据,那么该数据的节点不能被其他数据使用
|
|
271
|
+
let specialNode = false;
|
|
272
|
+
if (options.shouldOccupySpace) {
|
|
273
|
+
specialNode = options.shouldOccupySpace(data) || false;
|
|
274
|
+
}
|
|
275
|
+
if (specialNode) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
|
|
186
279
|
// 在可视区域内 进行有关卡片的操作
|
|
187
|
-
|
|
280
|
+
const bool = row.top <= endNum && row.bottom >= startNum;
|
|
281
|
+
if (bool) {
|
|
188
282
|
// 理论上什么都不动,因为卡片的位置不会变
|
|
189
|
-
const $card = this.activeNodes.get(
|
|
283
|
+
const $card = this.activeNodes.get(dataId);
|
|
190
284
|
|
|
191
285
|
let $node = null;
|
|
192
|
-
|
|
286
|
+
|
|
287
|
+
// 遍历当前的节点是否被占用,如果被占用的话,就得要从nodePool中取一个
|
|
288
|
+
let bool = true;
|
|
193
289
|
if ($card) {
|
|
290
|
+
bool = hasNodeInActives(newActiveNodes, $card)
|
|
291
|
+
}
|
|
292
|
+
// 如果卡片已经在DOM中,则不用更新位置
|
|
293
|
+
if (bool === false) {
|
|
194
294
|
$node = $card;
|
|
195
|
-
this.activeNodes.delete(
|
|
295
|
+
this.activeNodes.delete(dataId);
|
|
296
|
+
// 如果是强更,这里才会采取更新
|
|
196
297
|
if (force) {
|
|
197
|
-
this.updateRenderUI($node,
|
|
298
|
+
this.updateRenderUI($node, data, dataId);
|
|
198
299
|
}
|
|
300
|
+
|
|
199
301
|
} else {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
302
|
+
const $card = this.allReadyNodes.get(dataId);
|
|
303
|
+
// 遍历当前的节点是否被占用,如果被占用的话,就得要从nodePool中取一个
|
|
304
|
+
let bool = true;
|
|
305
|
+
if ($card) {
|
|
306
|
+
bool = hasNodeInActives(newActiveNodes, $card)
|
|
307
|
+
}
|
|
308
|
+
if (bool === false) {
|
|
309
|
+
// 如果成功获取到card并没有占用 那么就复用这个card
|
|
310
|
+
$node = $card;
|
|
311
|
+
|
|
312
|
+
this.updateRenderUI($node, data, dataId);
|
|
313
|
+
|
|
209
314
|
} else {
|
|
210
|
-
|
|
211
|
-
$node.
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
315
|
+
// 卡片不在DOM中,则更新位置
|
|
316
|
+
$node = getNodePoolPop(this.nodePool, newActiveNodes);
|
|
317
|
+
if ($node === null) {
|
|
318
|
+
// 这里是往上方拖动时,可能需要补建的情况
|
|
319
|
+
$node = this.appendCard(data, dataId, {
|
|
320
|
+
top: row.top, left: row.left
|
|
321
|
+
});
|
|
322
|
+
row.$node = $node;
|
|
323
|
+
} else {
|
|
324
|
+
this.updateRenderUI($node, data, dataId);
|
|
325
|
+
row.$node = $node;
|
|
326
|
+
}
|
|
216
327
|
}
|
|
217
328
|
}
|
|
329
|
+
$node.css({
|
|
330
|
+
'transform': `translate(${row.left}px,${row.top}px)`,
|
|
331
|
+
}).attr('data-index', dataId);
|
|
218
332
|
|
|
219
|
-
newActiveNodes.set(
|
|
333
|
+
newActiveNodes.set(dataId, $node);
|
|
334
|
+
|
|
335
|
+
// 清除掉在NodePool中的card
|
|
336
|
+
const index = this.nodePool.indexOf($card);
|
|
337
|
+
if (index !== -1) {
|
|
338
|
+
this.nodePool.splice(index, 1);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// console.log('302-row', row.dataId, dataInfo);
|
|
220
342
|
}
|
|
221
343
|
}
|
|
222
344
|
}
|
|
@@ -224,11 +346,16 @@ Waterfall.prototype.updateCardsInView = function ({ start, end, force = false })
|
|
|
224
346
|
// 阶段2:处理不活跃节点
|
|
225
347
|
this.activeNodes.forEach($node => {
|
|
226
348
|
$node.css('transform', `translateY(-9999px)`);// 移出可视区域
|
|
227
|
-
this.nodePool.
|
|
349
|
+
if (this.nodePool.indexOf($node) === -1) {
|
|
350
|
+
this.nodePool.push($node);
|
|
351
|
+
}
|
|
228
352
|
});
|
|
229
353
|
this.activeNodes = newActiveNodes;
|
|
354
|
+
// console.log('this.activeNodes', this.activeNodes);
|
|
355
|
+
// console.log('this.nodePool', this.nodePool);
|
|
230
356
|
}
|
|
231
357
|
|
|
358
|
+
|
|
232
359
|
Waterfall.prototype.getMaxHeight = function () {
|
|
233
360
|
let maxHeight = 0;
|
|
234
361
|
for (let i = 0; i < this.columns; i++) {
|
|
@@ -276,79 +403,127 @@ Waterfall.prototype.getMinHeightColumn = function () {
|
|
|
276
403
|
}
|
|
277
404
|
|
|
278
405
|
// 创建卡片
|
|
279
|
-
Waterfall.prototype.createCards = function ({ end }) {
|
|
406
|
+
Waterfall.prototype.createCards = function ({ end, dataId = -1 }) {
|
|
280
407
|
const self = this;
|
|
281
408
|
const options = this.options;
|
|
282
409
|
const $container = $(options.container);
|
|
283
|
-
const renderIndex = this.renderIndex;
|
|
284
410
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
411
|
+
// 新方案:获取下一个未渲染的数据ID
|
|
412
|
+
let nextDataId = null;
|
|
413
|
+
for (let [dataId, dataInfo] of this.dataIdMap) {
|
|
414
|
+
if (!this.renderedDataIds.has(dataId)) {
|
|
415
|
+
nextDataId = dataId;
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
// 如果没有更多数据需要渲染
|
|
422
|
+
if (nextDataId === null) {
|
|
423
|
+
// const maxHeight = this.getMaxHeight();
|
|
424
|
+
// $container.find('.waterfall-list-scroll').css('height', maxHeight + options.marginBottom + 'px');
|
|
425
|
+
this.setScrollHeight();
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const dataInfo = this.dataIdMap.get(nextDataId);
|
|
430
|
+
if (!dataInfo || !dataInfo.data) {
|
|
431
|
+
console.warn('Waterfall: Invalid data for dataId', nextDataId);
|
|
289
432
|
return;
|
|
290
433
|
}
|
|
291
434
|
|
|
435
|
+
if (this.renderIndex >= options.data.length) {
|
|
436
|
+
this.setScrollHeight();
|
|
437
|
+
return
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const data = options.data[nextDataId];
|
|
441
|
+
|
|
292
442
|
let column = this.getMinHeightColumn();
|
|
293
443
|
if (column === null) {
|
|
294
|
-
// 没有可用的列,则在第一列创建,说明此时还没有数据
|
|
295
444
|
column = this.columnItems[0];
|
|
296
445
|
}
|
|
297
446
|
|
|
298
447
|
const top = column.bottom === 0 ? options.marginTop : (column.bottom + options.rowGap);
|
|
448
|
+
const position = { top, left: column.left };
|
|
449
|
+
const row = createDefaultRow(position);
|
|
299
450
|
|
|
300
|
-
|
|
301
|
-
const position = {
|
|
302
|
-
top,
|
|
303
|
-
left: column.left,
|
|
304
|
-
}
|
|
451
|
+
this.renderIndex += 1;
|
|
305
452
|
|
|
306
|
-
|
|
453
|
+
let specialNode = false;
|
|
307
454
|
|
|
455
|
+
// 如果是特殊的卡片,需要指定节点不变更的数据,那么该数据的节点不能被其他数据使用
|
|
456
|
+
if (options.shouldOccupySpace) {
|
|
457
|
+
specialNode = options.shouldOccupySpace(data) || false;
|
|
458
|
+
}
|
|
308
459
|
|
|
309
|
-
//
|
|
460
|
+
// 添加卡片,使用dataId作为唯一标识
|
|
310
461
|
let $card = null;
|
|
311
|
-
if (this.nodePool.length === 0) {
|
|
312
|
-
$card = this.appendCard(
|
|
462
|
+
if (this.nodePool.length === 0 || specialNode === true) {
|
|
463
|
+
$card = this.appendCard(data, nextDataId, position);
|
|
313
464
|
} else {
|
|
314
|
-
$
|
|
315
|
-
$
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
465
|
+
const $tmp = getNodePoolPop(this.nodePool, this.activeNodes);
|
|
466
|
+
if ($tmp) {
|
|
467
|
+
$card = $tmp;
|
|
468
|
+
$card.css({
|
|
469
|
+
'transform': `translate(${row.left}px,${row.top}px)`,
|
|
470
|
+
}).attr('data-index', nextDataId);
|
|
471
|
+
this.updateRenderUI($card, data, nextDataId);
|
|
472
|
+
} else {
|
|
473
|
+
$card = this.appendCard(data, nextDataId, position);
|
|
474
|
+
}
|
|
320
475
|
|
|
476
|
+
}
|
|
321
477
|
|
|
322
478
|
row.$node = $card;
|
|
479
|
+
row.dataId = nextDataId; // 使用dataId替代renderIndex
|
|
480
|
+
if (dataId !== -1) {
|
|
481
|
+
row.dataId = dataId;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// 记录布局信息
|
|
485
|
+
// dataInfo.layoutInfo = {
|
|
486
|
+
// //columnIndex: this.columnItems.indexOf(column),
|
|
487
|
+
// //position: position,
|
|
488
|
+
// // row: row
|
|
489
|
+
// };
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
|
|
323
493
|
|
|
324
|
-
|
|
325
|
-
|
|
494
|
+
if (specialNode === false) {
|
|
495
|
+
// 把新增的卡片放进 activeNodes 当成活跃节点元素,那么是 可以动态使用的
|
|
496
|
+
this.activeNodes.set(nextDataId, $card);
|
|
326
497
|
|
|
498
|
+
this.allReadyNodes.set(nextDataId, $card);
|
|
499
|
+
} else {
|
|
500
|
+
// 如果是特殊的,这里不要记录了
|
|
501
|
+
this.allReadyNodes.set(nextDataId, null);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
this.renderedDataIds.add(nextDataId);
|
|
327
505
|
|
|
328
506
|
// 更新列的底部距离
|
|
329
507
|
column.bottom = top + $card.height();
|
|
330
508
|
column.children.push(row);
|
|
331
|
-
|
|
332
|
-
// 计算当前卡片的位置
|
|
333
509
|
row.bottom = column.bottom;
|
|
334
|
-
row.renderIndex = renderIndex;
|
|
335
|
-
|
|
336
|
-
this.renderIndex += 1;
|
|
337
|
-
|
|
338
|
-
let hasNextData = this.renderIndex < options.data.length;
|
|
339
510
|
|
|
511
|
+
// 检查是否需要继续创建卡片
|
|
340
512
|
const minHeight = this.getMinHeight();
|
|
341
|
-
|
|
513
|
+
const hasMoreData = this.renderedDataIds.size < this.dataIdMap.size;
|
|
514
|
+
|
|
515
|
+
if (hasMoreData && (minHeight < end)) {
|
|
342
516
|
window.requestAnimationFrame(() => {
|
|
343
517
|
this.createCards({ end });
|
|
344
518
|
});
|
|
345
519
|
} else {
|
|
346
|
-
const maxHeight = this.getMaxHeight();
|
|
347
|
-
//
|
|
348
|
-
|
|
520
|
+
// const maxHeight = this.getMaxHeight();
|
|
521
|
+
// $(options.container).find('.waterfall-list-scroll').css('height', maxHeight + options.marginBottom + 'px');
|
|
522
|
+
this.setScrollHeight();
|
|
349
523
|
}
|
|
350
524
|
}
|
|
351
525
|
|
|
526
|
+
|
|
352
527
|
Waterfall.prototype.createColumn = function (index) {
|
|
353
528
|
const res = {
|
|
354
529
|
left: 0,
|
|
@@ -365,18 +540,220 @@ Waterfall.prototype.createColumn = function (index) {
|
|
|
365
540
|
return res;
|
|
366
541
|
}
|
|
367
542
|
|
|
368
|
-
Waterfall.prototype.updateRenderUI = function ($node, data,
|
|
543
|
+
Waterfall.prototype.updateRenderUI = function ($node, data, dataId) {
|
|
369
544
|
const options = this.options;
|
|
545
|
+
|
|
546
|
+
// 新方案:基于数据ID的数据验证
|
|
547
|
+
if (!this.dataIdMap.has(dataId)) {
|
|
548
|
+
console.error('Waterfall: Invalid dataId in updateRenderUI', dataId);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const dataInfo = this.dataIdMap.get(dataId);
|
|
553
|
+
if (!dataInfo || !dataInfo.data) {
|
|
554
|
+
console.warn('Waterfall: Empty data for dataId', dataId);
|
|
555
|
+
}
|
|
556
|
+
|
|
370
557
|
if (this.hasUpdateItem === true) {
|
|
371
|
-
options.updateItem($node, data,
|
|
558
|
+
options.updateItem($node, data, dataInfo.originalIndex)
|
|
372
559
|
} else {
|
|
373
|
-
$node.html(options.renderItem(data,
|
|
560
|
+
$node.html(options.renderItem(data, dataInfo.originalIndex));
|
|
374
561
|
}
|
|
375
562
|
}
|
|
376
563
|
|
|
564
|
+
|
|
565
|
+
|
|
377
566
|
Waterfall.prototype.updateData = function (newData) {
|
|
378
|
-
|
|
567
|
+
const options = this.options;
|
|
568
|
+
options.data = newData;
|
|
569
|
+
|
|
570
|
+
// 新方案:重新建立数据ID映射
|
|
571
|
+
//this.dataIdMap.clear();
|
|
572
|
+
//this.renderedDataIds.clear();
|
|
573
|
+
//this.nextDataId = 0;
|
|
574
|
+
|
|
575
|
+
// 为每个数据项分配唯一ID
|
|
576
|
+
options.data.forEach((item, index) => {
|
|
577
|
+
const dataId = index; // this.nextDataId++;
|
|
578
|
+
if (!this.allReadyNodes.has(dataId)) {
|
|
579
|
+
this.dataIdMap.set(dataId, {
|
|
580
|
+
data: true,// item,
|
|
581
|
+
originalIndex: index,
|
|
582
|
+
layoutInfo: null // 将在布局时填充
|
|
583
|
+
});
|
|
584
|
+
// 如果没有准备好这个数据,这里要创建一个占位节点
|
|
585
|
+
this.createCards({ end: 0, dataId });
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
|
|
379
589
|
this.updateVisibleItems(true); // 强制更新渲染
|
|
590
|
+
|
|
591
|
+
// 重新计算所有卡片位置并更新位置
|
|
592
|
+
// this.updatePointCards();
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// 某个数据进行了UI变更,触发高度重新绘制
|
|
596
|
+
Waterfall.prototype.updateCard = function (data) {
|
|
597
|
+
const options = this.options;
|
|
598
|
+
let dataId = -1;
|
|
599
|
+
if (typeof data === 'number') {
|
|
600
|
+
dataId = data;
|
|
601
|
+
} else {
|
|
602
|
+
for (let i = 0; i < options.data.length; i++) {
|
|
603
|
+
if (options.data[i] === data) {
|
|
604
|
+
dataId = i;
|
|
605
|
+
break;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
if (dataId === -1) {
|
|
610
|
+
// 没有匹配到数据,进行退出
|
|
611
|
+
console.log('Waterfall: Invalid data in options.data');
|
|
612
|
+
return
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// 从activeNodes中获取该数据的节点
|
|
616
|
+
if (!this.activeNodes.has(dataId)) {
|
|
617
|
+
console.log('Waterfall: Invalid dataId in activeNodes');
|
|
618
|
+
return
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const $node = this.activeNodes.get(dataId);
|
|
622
|
+
const height = $node.height();
|
|
623
|
+
|
|
624
|
+
// 重新计算该数据所在列的卡片位置
|
|
625
|
+
let needUpdate = false
|
|
626
|
+
const columnItems = this.columnItems;
|
|
627
|
+
for (let i = 0; i < columnItems.length; i++) {
|
|
628
|
+
const column = columnItems[i];
|
|
629
|
+
let bool = false
|
|
630
|
+
let minus = 0
|
|
631
|
+
// 这里为了简化,各列的瀑布流保持不动,只更新各列下节点的top位置,不做节点的跨列位移
|
|
632
|
+
for (let j = 0; j < column.children.length; j++) {
|
|
633
|
+
const row = column.children[j];
|
|
634
|
+
if (row.dataId === dataId) {
|
|
635
|
+
bool = true
|
|
636
|
+
const oldHeight = row.bottom - row.top;
|
|
637
|
+
|
|
638
|
+
minus = height - oldHeight;
|
|
639
|
+
|
|
640
|
+
if (minus === 0) {
|
|
641
|
+
// 找到了原节点数据,对比后没有变更 那么直接退出
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
needUpdate = true
|
|
645
|
+
row.bottom += minus;
|
|
646
|
+
} else if (minus !== 0) {
|
|
647
|
+
row.top += minus;
|
|
648
|
+
row.bottom += minus;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (bool) {
|
|
652
|
+
column.bottom += minus;
|
|
653
|
+
break;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (needUpdate) {
|
|
657
|
+
this.updateVisibleItems(true); // 强制更新渲染
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// 重新计算设置一遍所有的卡片位置
|
|
662
|
+
Waterfall.prototype.updatePointCards = function () {
|
|
663
|
+
// 有问题 不要用
|
|
664
|
+
return
|
|
665
|
+
const self = this;
|
|
666
|
+
const options = this.options;
|
|
667
|
+
const columnItems = this.columnItems;
|
|
668
|
+
let top = options.marginTop;
|
|
669
|
+
for (let i = 0; i < columnItems.length; i++) {
|
|
670
|
+
const column = columnItems[i];
|
|
671
|
+
// 这里为了简化,各列的瀑布流保持不动,只更新各列下节点的top位置,不做节点的跨列位移
|
|
672
|
+
for (let j = 0; j < column.children.length; j++) {
|
|
673
|
+
const row = column.children[j];
|
|
674
|
+
const $card = row.$node;
|
|
675
|
+
|
|
676
|
+
// 验证数据ID有效性
|
|
677
|
+
if (!this.dataIdMap.has(row.dataId)) {
|
|
678
|
+
console.warn('Waterfall: Invalid dataId in updatePointCards', row.dataId);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// 第一个的top不需要更新
|
|
683
|
+
if (j === 0) {
|
|
684
|
+
row.bottom = top + $card.height();
|
|
685
|
+
} else {
|
|
686
|
+
row.top = column.children[j - 1].bottom + options.rowGap;
|
|
687
|
+
row.bottom = row.top + $card.height();
|
|
688
|
+
// 更新卡片位置
|
|
689
|
+
// $card.css({
|
|
690
|
+
// 'transform': `translate(${row.left}px,${row.top}px)`,
|
|
691
|
+
// })
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
}
|
|
695
|
+
// 设置一次该列的bottom
|
|
696
|
+
column.bottom = getBottomByColumn(column);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// 展示loading的回调函数
|
|
701
|
+
Waterfall.prototype.showLoading = function (callback = null) {
|
|
702
|
+
this.isShowLoading = true;
|
|
703
|
+
const options = this.options;
|
|
704
|
+
const $container = $(options.container);
|
|
705
|
+
let $node = null
|
|
706
|
+
if (this.$loadingNode) {
|
|
707
|
+
let loadingTop = this.getMaxHeight() + options.rowGap
|
|
708
|
+
this.$loadingNode.css('transform', `translate(0px,${loadingTop}px)`);
|
|
709
|
+
$node = this.$loadingNode
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (callback) callback($node)
|
|
713
|
+
this.setScrollHeight();
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// 隐藏loading的回调函数
|
|
717
|
+
Waterfall.prototype.hideLoading = function (callback = null) {
|
|
718
|
+
this.isShowLoading = false;
|
|
719
|
+
const options = this.options;
|
|
720
|
+
const $container = $(options.container);
|
|
721
|
+
let $node = null
|
|
722
|
+
if (this.$loadingNode) {
|
|
723
|
+
let h1 = this.getMaxHeight() + options.marginBottom
|
|
724
|
+
this.$loadingNode.css('transform', `translate(0px,-99999px)`);
|
|
725
|
+
//如果要设置高度,那么这里判断一下当前是否正在做updata 一般这里被调用时,数据已经读到,在updata的同一时间调用了该函数
|
|
726
|
+
// 如果两个时刻高度是一致的 那么数据就是一致的 这里重新设置回来高度即可
|
|
727
|
+
window.requestAnimationFrame(() => {
|
|
728
|
+
// let h2 = this.getMaxHeight() + options.marginBottom
|
|
729
|
+
// if (h1 === h2) {
|
|
730
|
+
// const $scroll = $(options.container).find('.waterfall-list-scroll')
|
|
731
|
+
// const h = $scroll.height()
|
|
732
|
+
// if (h !== h1) {
|
|
733
|
+
// $scroll.css('height', h1 + 'px');
|
|
734
|
+
// }
|
|
735
|
+
// }
|
|
736
|
+
this.setScrollHeight();
|
|
737
|
+
})
|
|
738
|
+
$node = this.$loadingNode
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (callback) callback($node)
|
|
742
|
+
this.setScrollHeight();
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// 设置滚动条列表的高度
|
|
746
|
+
Waterfall.prototype.setScrollHeight = function () {
|
|
747
|
+
const options = this.options;
|
|
748
|
+
const $container = $(options.container);
|
|
749
|
+
let h = this.getMaxHeight();
|
|
750
|
+
if (this.isShowLoading === true) {
|
|
751
|
+
if (this.$loadingNode) {
|
|
752
|
+
h += options.rowGap + this.$loadingNode.height();
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
h += options.marginBottom;
|
|
756
|
+
$container.find('.waterfall-list-scroll').css('height', h + 'px');
|
|
380
757
|
}
|
|
381
758
|
|
|
382
759
|
function createDefaultRow({ top, left }) {
|
|
@@ -385,13 +762,37 @@ function createDefaultRow({ top, left }) {
|
|
|
385
762
|
left,
|
|
386
763
|
bottom: 0,
|
|
387
764
|
$node: null,
|
|
388
|
-
|
|
765
|
+
dataId: -1 // 新方案:使用dataId替代renderIndex
|
|
389
766
|
}
|
|
390
767
|
}
|
|
391
768
|
|
|
392
|
-
|
|
393
|
-
|
|
769
|
+
|
|
770
|
+
function getBottomByColumn(column) {
|
|
771
|
+
if (column.children.length === 0) {
|
|
394
772
|
return 0;
|
|
395
773
|
}
|
|
396
|
-
|
|
774
|
+
const child = column.children;
|
|
775
|
+
return child[child.length - 1].bottom;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function hasNodeInActives(mapObj, $node) {
|
|
779
|
+
let bool = false
|
|
780
|
+
mapObj.forEach((item) => {
|
|
781
|
+
if (item === $node) {
|
|
782
|
+
bool = true;
|
|
783
|
+
}
|
|
784
|
+
});
|
|
785
|
+
return bool;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// 从众多jq对象数组取得一个不重复的
|
|
789
|
+
function getNodePoolPop(nodePool, actives) {
|
|
790
|
+
for (let i = 0; i < nodePool.length; i++) {
|
|
791
|
+
const $node = nodePool[i];
|
|
792
|
+
if (!hasNodeInActives(actives, $node)) {
|
|
793
|
+
nodePool.splice(i, 1);
|
|
794
|
+
return $node;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return null;
|
|
397
798
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
.waterfall-list-scroll {
|
|
2
|
+
height: 100%;
|
|
3
|
+
.waterfall-loading {
|
|
4
|
+
position: absolute;
|
|
5
|
+
width: 100%;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
}
|
|
9
|
+
.waterfall-item {
|
|
10
|
+
flex: 1;
|
|
11
|
+
flex-shrink: 1;
|
|
12
|
+
flex-grow: 1;
|
|
13
|
+
flex-basis: 0;
|
|
14
|
+
width: 100%;
|
|
15
|
+
position: absolute;
|
|
16
|
+
}
|
|
17
|
+
}
|