@tarojs/components-advanced 4.0.13-alpha.0 → 4.0.13
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.
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +4 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/water-flow/flow-item.d.ts +11 -0
- package/dist/components/water-flow/flow-item.js +70 -0
- package/dist/components/water-flow/flow-item.js.map +1 -0
- package/dist/components/water-flow/flow-section.d.ts +7 -0
- package/dist/components/water-flow/flow-section.js +66 -0
- package/dist/components/water-flow/flow-section.js.map +1 -0
- package/dist/components/water-flow/index.d.ts +4 -0
- package/dist/components/water-flow/index.js +4 -0
- package/dist/components/water-flow/index.js.map +1 -0
- package/dist/components/water-flow/interface.d.ts +54 -0
- package/dist/components/water-flow/node.d.ts +63 -0
- package/dist/components/water-flow/node.js +99 -0
- package/dist/components/water-flow/node.js.map +1 -0
- package/dist/components/water-flow/root.d.ts +144 -0
- package/dist/components/water-flow/root.js +395 -0
- package/dist/components/water-flow/root.js.map +1 -0
- package/dist/components/water-flow/section.d.ts +110 -0
- package/dist/components/water-flow/section.js +226 -0
- package/dist/components/water-flow/section.js.map +1 -0
- package/dist/components/water-flow/stateful-event-bus.d.ts +33 -0
- package/dist/components/water-flow/stateful-event-bus.js +70 -0
- package/dist/components/water-flow/stateful-event-bus.js.map +1 -0
- package/dist/components/water-flow/use-memoized-fn.d.ts +4 -0
- package/dist/components/water-flow/use-memoized-fn.js +16 -0
- package/dist/components/water-flow/use-memoized-fn.js.map +1 -0
- package/dist/components/water-flow/use-observed-attr.d.ts +2 -0
- package/dist/components/water-flow/use-observed-attr.js +20 -0
- package/dist/components/water-flow/use-observed-attr.js.map +1 -0
- package/dist/components/water-flow/use-unmount.d.ts +1 -0
- package/dist/components/water-flow/utils.d.ts +31 -0
- package/dist/components/water-flow/utils.js +45 -0
- package/dist/components/water-flow/utils.js.map +1 -0
- package/dist/components/water-flow/water-flow.d.ts +4 -0
- package/dist/components/water-flow/water-flow.js +146 -0
- package/dist/components/water-flow/water-flow.js.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import Taro from '@tarojs/taro';
|
|
2
|
+
import '../../utils/index.js';
|
|
3
|
+
import { StatefulEventBus } from './stateful-event-bus.js';
|
|
4
|
+
import { getSysInfo, isSameRenderRange } from './utils.js';
|
|
5
|
+
import { getRectSizeSync } from '../../utils/dom.js';
|
|
6
|
+
|
|
7
|
+
/* eslint-disable no-labels */
|
|
8
|
+
const { windowHeight, windowWidth } = getSysInfo();
|
|
9
|
+
const RootEvents = {
|
|
10
|
+
ReachUpperThreshold: Symbol.for('ReachUpperThreshold'),
|
|
11
|
+
ReachLowerThreshold: Symbol.for('ReachLowerThreshold'),
|
|
12
|
+
Resize: Symbol.for('Resize'),
|
|
13
|
+
AllSectionsLayouted: Symbol.for('AllSectionsLayouted'),
|
|
14
|
+
InitialRenderCompleted: Symbol.for('InitialRenderCompleted'),
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* 数据模型继承自有状态的事件总线,便于在节点之间通信,以及通过 useSyncExternalStore 关联 React 视图
|
|
18
|
+
*/
|
|
19
|
+
class Root extends StatefulEventBus {
|
|
20
|
+
constructor(props) {
|
|
21
|
+
const { id, cacheCount, lowerThresholdCount, upperThresholdCount } = props;
|
|
22
|
+
super({
|
|
23
|
+
isScrolling: false,
|
|
24
|
+
scrollOffset: 0,
|
|
25
|
+
scrollDirection: 'forward',
|
|
26
|
+
scrollHeight: windowHeight,
|
|
27
|
+
renderRange: [0, 0],
|
|
28
|
+
containerSize: {
|
|
29
|
+
width: windowWidth,
|
|
30
|
+
height: windowHeight,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* 分组映射表,便于查找分组
|
|
35
|
+
*/
|
|
36
|
+
this.sectionMap = new Map();
|
|
37
|
+
/**
|
|
38
|
+
* 节点映射表,便于查找节点
|
|
39
|
+
*/
|
|
40
|
+
this.nodeMap = new Map();
|
|
41
|
+
/**
|
|
42
|
+
* 分组列表,基于计算出的渲染的分组区间范围 sections.slice(start, end + 1) 进行渲染
|
|
43
|
+
*/
|
|
44
|
+
this.sections = [];
|
|
45
|
+
/**
|
|
46
|
+
* 设置预加载的 Item 条数。
|
|
47
|
+
*/
|
|
48
|
+
this.cacheCount = 1;
|
|
49
|
+
this.upperThresholdCount = 0;
|
|
50
|
+
this.lowerThresholdCount = 0;
|
|
51
|
+
/**
|
|
52
|
+
* 触发滚动阈值对应的 scrollTop 值
|
|
53
|
+
*/
|
|
54
|
+
this.upperThresholdScrollTop = -Infinity;
|
|
55
|
+
/**
|
|
56
|
+
* 触发滚动阈值对应的 scrollTop 值
|
|
57
|
+
*/
|
|
58
|
+
this.lowerThresholdScrollTop = Infinity;
|
|
59
|
+
Object.assign(this, {
|
|
60
|
+
id,
|
|
61
|
+
cacheCount,
|
|
62
|
+
lowerThresholdCount,
|
|
63
|
+
upperThresholdCount,
|
|
64
|
+
});
|
|
65
|
+
this.setupSubscriptions();
|
|
66
|
+
getRectSizeSync(`#${id}`, 100).then(({ width = windowWidth, height = windowHeight }) => {
|
|
67
|
+
this.setStateIn('containerSize', {
|
|
68
|
+
width,
|
|
69
|
+
height,
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
this.renderInitialLayout();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* 设置订阅事件
|
|
76
|
+
*/
|
|
77
|
+
setupSubscriptions() {
|
|
78
|
+
/**
|
|
79
|
+
* 滚动过程中计算渲染的分组区间
|
|
80
|
+
* 滚动过程中分组的最大高度会发生更新,可以在这时计算滚动高度
|
|
81
|
+
*/
|
|
82
|
+
this.sub('scrollOffset', () => {
|
|
83
|
+
this.setStateIn('renderRange', this.getSectionRenderRange());
|
|
84
|
+
this.updateScrollHeight();
|
|
85
|
+
this.handleReachThreshold();
|
|
86
|
+
});
|
|
87
|
+
const lowerThresholdScrollTopDisposer = this.sub('scrollOffset', () => {
|
|
88
|
+
const sectionSize = this.sections.length;
|
|
89
|
+
const lastSection = this.sections[sectionSize - 1];
|
|
90
|
+
// 最后一个分组的每一列最后一行都已经完成了布局计算,那么这个时候的总高度应该是准确的
|
|
91
|
+
if (lastSection.columnMap.every((column) => column[column.length - 1].getState().layouted)) {
|
|
92
|
+
this.setLowerThresholdScrollTop();
|
|
93
|
+
lowerThresholdScrollTopDisposer();
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
this.sub(RootEvents.AllSectionsLayouted, () => {
|
|
97
|
+
this.setUpperThresholdScrollTop();
|
|
98
|
+
});
|
|
99
|
+
this.sub(RootEvents.Resize, () => {
|
|
100
|
+
this.setUpperThresholdScrollTop();
|
|
101
|
+
this.setLowerThresholdScrollTop();
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 渐进式渲染
|
|
106
|
+
*
|
|
107
|
+
* 因为初始没法知道每个分组的高度信息,不知道渲染边界,所以需要渐进式渲染
|
|
108
|
+
*
|
|
109
|
+
* 当目前的渲染批次的首个分组的scrollTop大于容器的高度,说明容器可视区域已经填满,没必要再往下渲染了
|
|
110
|
+
*
|
|
111
|
+
* @param [i=0] 从第几个分组开始渲染
|
|
112
|
+
*
|
|
113
|
+
*/
|
|
114
|
+
renderInitialLayout(i = 0) {
|
|
115
|
+
Taro.nextTick(() => {
|
|
116
|
+
const sectionSize = this.sections.length;
|
|
117
|
+
if (i >= sectionSize || i < 0) {
|
|
118
|
+
this.pub(RootEvents.InitialRenderCompleted, null);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const section = this.sections[i];
|
|
122
|
+
section.layoutedSignal.promise.then(() => {
|
|
123
|
+
this.setStateIn('renderRange', [
|
|
124
|
+
0,
|
|
125
|
+
i + 1 > sectionSize ? sectionSize - 1 : i + 1,
|
|
126
|
+
]);
|
|
127
|
+
// 容器可视区域已经填满了,没必要再继续
|
|
128
|
+
if (section.getState().scrollTop > this.getState().containerSize.height) {
|
|
129
|
+
this.pub(RootEvents.InitialRenderCompleted, section);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this.renderInitialLayout(i + 1);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 计算滚动阈值对应的 scrollTop 并设置 upperThresholdScrollTop
|
|
138
|
+
* 当距顶部还有 upperThresholdCount 个 FlowItem 时的 scrollTop 值
|
|
139
|
+
*/
|
|
140
|
+
setUpperThresholdScrollTop() {
|
|
141
|
+
// 如果没有设置阈值或阈值为0,则返回0
|
|
142
|
+
if (!this.upperThresholdCount) {
|
|
143
|
+
this.upperThresholdScrollTop = 0;
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
const sectionSize = this.sections.length;
|
|
147
|
+
const tracker = Array.from({ length: sectionSize }, () => new Map() // Map<列, { 当前列累计个数,当前列累计高度 }>
|
|
148
|
+
);
|
|
149
|
+
// 从第一个分组开始扫描
|
|
150
|
+
loopSeciton: for (let i = 0; i < sectionSize; i++) {
|
|
151
|
+
const section = this.sections[i];
|
|
152
|
+
const sectionTracker = tracker[i];
|
|
153
|
+
const columnMap = section.columnMap;
|
|
154
|
+
// 扫描当前分组的每一列
|
|
155
|
+
for (let col = 0; col < columnMap.length; col++) {
|
|
156
|
+
const column = columnMap[col];
|
|
157
|
+
const columnSize = column.length;
|
|
158
|
+
if (!sectionTracker.has(col)) {
|
|
159
|
+
if (i === 0) {
|
|
160
|
+
sectionTracker.set(col, { accCount: 0, accHeight: 0 });
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
const previousSectionTracker = tracker[i - 1];
|
|
164
|
+
sectionTracker.set(col, {
|
|
165
|
+
accCount: Math.max(...[...previousSectionTracker.values()].map((nodeTracker) => nodeTracker.accCount)),
|
|
166
|
+
accHeight: section.getState().scrollTop,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const colTracker = sectionTracker.get(col);
|
|
171
|
+
// 扫描当前列的每一行
|
|
172
|
+
loopItem: for (let j = 0; j < columnSize; j++) {
|
|
173
|
+
colTracker.accCount += 1;
|
|
174
|
+
colTracker.accHeight +=
|
|
175
|
+
column[j].getState().height + (j === 0 ? 0 : section.rowGap);
|
|
176
|
+
if (colTracker.accCount >= this.upperThresholdCount) {
|
|
177
|
+
break loopItem;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
for (const [, colTracker] of sectionTracker) {
|
|
182
|
+
if (colTracker.accCount >= this.upperThresholdCount) {
|
|
183
|
+
this.upperThresholdScrollTop = colTracker.accHeight;
|
|
184
|
+
break loopSeciton;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return this.upperThresholdScrollTop;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 计算滚动阈值对应的 scrollTop 并设置 lowerThresholdScrollTop
|
|
192
|
+
* 当距底部还有 lowerThresholdCount 个 FlowItem 时的 scrollTop 值
|
|
193
|
+
*/
|
|
194
|
+
setLowerThresholdScrollTop() {
|
|
195
|
+
if (this.lowerThresholdCount === 0) {
|
|
196
|
+
this.lowerThresholdScrollTop =
|
|
197
|
+
this.getState().scrollHeight - this.getState().containerSize.height;
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
const sectionSize = this.sections.length;
|
|
201
|
+
const tracker = Array.from({ length: sectionSize }, () => new Map());
|
|
202
|
+
// 从最后一个分组开始计算
|
|
203
|
+
loopSeciton: for (let i = sectionSize - 1; i > 0; i--) {
|
|
204
|
+
const section = this.sections[i];
|
|
205
|
+
const sectionTracker = tracker[i];
|
|
206
|
+
const columnMap = section.columnMap;
|
|
207
|
+
// 扫描当前分组的每一列
|
|
208
|
+
for (let col = 0; col < columnMap.length; col++) {
|
|
209
|
+
const column = columnMap[col];
|
|
210
|
+
const columnSize = column.length;
|
|
211
|
+
if (!sectionTracker.has(col)) {
|
|
212
|
+
if (i === sectionSize - 1) {
|
|
213
|
+
sectionTracker.set(col, { accCount: 0, scrollTop: 0 });
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
const belowSectionTracker = tracker[i + 1];
|
|
217
|
+
sectionTracker.set(col, {
|
|
218
|
+
accCount: Math.max(...[...belowSectionTracker.values()].map((nodeTracker) => nodeTracker.accCount)),
|
|
219
|
+
scrollTop: 0,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const colTracker = sectionTracker.get(col);
|
|
224
|
+
// 从当前列的最后一行开始往前扫描
|
|
225
|
+
loopItem: for (let j = columnSize - 1; j >= 0; j--) {
|
|
226
|
+
colTracker.accCount += 1;
|
|
227
|
+
colTracker.scrollTop = column[j].getState().scrollTop;
|
|
228
|
+
if (colTracker.accCount >= this.lowerThresholdCount) {
|
|
229
|
+
break loopItem;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
for (const [, colTracker] of sectionTracker) {
|
|
234
|
+
if (colTracker.accCount >= this.lowerThresholdCount) {
|
|
235
|
+
this.lowerThresholdScrollTop = colTracker.scrollTop;
|
|
236
|
+
break loopSeciton;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return this.lowerThresholdScrollTop;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* 处理滚动到阈值的情况
|
|
244
|
+
* 检测当前滚动位置是否达到了上下阈值,并触发相应的事件
|
|
245
|
+
*/
|
|
246
|
+
handleReachThreshold() {
|
|
247
|
+
const { upperThresholdScrollTop } = this;
|
|
248
|
+
const { scrollOffset, scrollDirection, containerSize } = this.getState();
|
|
249
|
+
if (scrollDirection === 'backward' &&
|
|
250
|
+
this.upperThresholdScrollTop !== -Infinity &&
|
|
251
|
+
scrollOffset <= upperThresholdScrollTop) {
|
|
252
|
+
this.pub(RootEvents.ReachUpperThreshold);
|
|
253
|
+
}
|
|
254
|
+
if (scrollDirection === 'forward' &&
|
|
255
|
+
this.lowerThresholdCount !== Infinity &&
|
|
256
|
+
scrollOffset + containerSize.height >= this.lowerThresholdScrollTop) {
|
|
257
|
+
this.pub(RootEvents.ReachLowerThreshold);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* 容器的滚动上边界
|
|
262
|
+
*/
|
|
263
|
+
get scrollBoundaryStart() {
|
|
264
|
+
return this.getState().scrollOffset;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* 容器的滚动下边界
|
|
268
|
+
*/
|
|
269
|
+
get scrollBoundaryEnd() {
|
|
270
|
+
return this.scrollBoundaryStart + this.getStateIn('containerSize').height;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* 计算每个section的底部位置
|
|
274
|
+
*
|
|
275
|
+
* sectionBottomRange = [ [section1.top, section1.bottom], [section2.top, section2.bottom], ..., [sectionN.top, sectionN.bottom] ]
|
|
276
|
+
*
|
|
277
|
+
* @returns [number,number][]
|
|
278
|
+
*/
|
|
279
|
+
get sectionRange() {
|
|
280
|
+
const length = this.sections.length;
|
|
281
|
+
if (length === 0)
|
|
282
|
+
return [];
|
|
283
|
+
const range = Array.from({ length }, () => [
|
|
284
|
+
0,
|
|
285
|
+
this.sections[0].maxColumnHeight,
|
|
286
|
+
]);
|
|
287
|
+
for (let i = 1; i < length; i++) {
|
|
288
|
+
const previous = range[i - 1];
|
|
289
|
+
range[i] = [previous[1], previous[1] + this.sections[i].maxColumnHeight];
|
|
290
|
+
}
|
|
291
|
+
return range;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* 计算滚动高度
|
|
295
|
+
*/
|
|
296
|
+
updateScrollHeight() {
|
|
297
|
+
this.setStateIn('scrollHeight', this.sectionRange[this.sectionRange.length - 1][1]);
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* 注册分组
|
|
301
|
+
*/
|
|
302
|
+
registerSection(section) {
|
|
303
|
+
const { id, order } = section;
|
|
304
|
+
this.sectionMap.set(id, section);
|
|
305
|
+
this.sections[order] = section;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* 注册节点
|
|
309
|
+
*/
|
|
310
|
+
registerNode(node) {
|
|
311
|
+
this.nodeMap.set(node.id, node);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* 查找分组
|
|
315
|
+
*/
|
|
316
|
+
findSection(id) {
|
|
317
|
+
return this.sectionMap.get(id);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 查找节点
|
|
321
|
+
*/
|
|
322
|
+
findNode(id) {
|
|
323
|
+
return this.nodeMap.get(id);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* 获取分组渲染区间
|
|
327
|
+
*/
|
|
328
|
+
getSectionRenderRange() {
|
|
329
|
+
const result = [Infinity, -Infinity];
|
|
330
|
+
for (let i = 0; i < this.sections.length; i++) {
|
|
331
|
+
const section = this.sections[i];
|
|
332
|
+
if (section.isInRange) {
|
|
333
|
+
result[0] = Math.min(result[0], i);
|
|
334
|
+
result[1] = Math.max(result[1], i);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (result[0] === Infinity) {
|
|
338
|
+
result[0] = 0;
|
|
339
|
+
}
|
|
340
|
+
if (result[1] === -Infinity) {
|
|
341
|
+
result[1] = this.sections.length - 1;
|
|
342
|
+
}
|
|
343
|
+
const scrollDirection = this.getState().scrollDirection;
|
|
344
|
+
const [backwardCache, forwardCache] = this.calcCacheSection(result);
|
|
345
|
+
const backwardDistance = scrollDirection === 'backward' ? backwardCache : 0;
|
|
346
|
+
const forwardDistance = scrollDirection === 'forward' ? forwardCache : 0;
|
|
347
|
+
const overscanBackward = result[0] - backwardDistance;
|
|
348
|
+
const overscanForward = result[1] + forwardDistance;
|
|
349
|
+
result[0] = overscanBackward < 0 ? 0 : overscanBackward;
|
|
350
|
+
result[1] =
|
|
351
|
+
overscanForward > this.sections.length
|
|
352
|
+
? this.sections.length - 1
|
|
353
|
+
: overscanForward;
|
|
354
|
+
return isSameRenderRange(result, this.getState().renderRange)
|
|
355
|
+
? this.getState().renderRange
|
|
356
|
+
: result;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* 计算预渲染的分组个数
|
|
360
|
+
*/
|
|
361
|
+
calcCacheSection(renderRange) {
|
|
362
|
+
var _a, _b, _c, _d;
|
|
363
|
+
const clientHeight = this.getState().containerSize.height;
|
|
364
|
+
const sectionCount = this.sectionMap.size;
|
|
365
|
+
let [start, end] = renderRange;
|
|
366
|
+
let cacheBackward = 1;
|
|
367
|
+
let cacheForward = 1;
|
|
368
|
+
if (start > 0) {
|
|
369
|
+
let acc = (_b = (_a = this.sections[--start]) === null || _a === void 0 ? void 0 : _a.getState().height) !== null && _b !== void 0 ? _b : 0;
|
|
370
|
+
while (--start > 0) {
|
|
371
|
+
const prevSection = this.sections[start];
|
|
372
|
+
acc += prevSection.getState().height;
|
|
373
|
+
cacheBackward += 1;
|
|
374
|
+
if (acc >= clientHeight >>> 1) {
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
if (end < sectionCount - 1) {
|
|
380
|
+
let acc = (_d = (_c = this.sections[++end]) === null || _c === void 0 ? void 0 : _c.getState().height) !== null && _d !== void 0 ? _d : 0;
|
|
381
|
+
while (++end < sectionCount - 1) {
|
|
382
|
+
const nextSection = this.sections[end];
|
|
383
|
+
acc += nextSection.getState().height;
|
|
384
|
+
cacheForward += 1;
|
|
385
|
+
if (acc >= clientHeight >>> 1) {
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return [cacheBackward, cacheForward];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
export { Root, RootEvents };
|
|
395
|
+
//# sourceMappingURL=root.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root.js","sources":["../../../src/components/water-flow/root.ts"],"sourcesContent":["/* eslint-disable no-labels */\nimport Taro from '@tarojs/taro'\n\nimport { getRectSizeSync } from '../../utils'\nimport { Node } from './node'\nimport { Section } from './section'\nimport { StatefulEventBus } from './stateful-event-bus'\nimport { getSysInfo, isSameRenderRange } from './utils'\n\nimport type { BaseProps, ScrollDirection, Size, WaterFlowProps } from './interface'\n\nexport type RootProps = Pick<\nWaterFlowProps,\n'cacheCount' | 'lowerThresholdCount' | 'upperThresholdCount'\n> &\nRequired<Pick<BaseProps, 'id'>>;\n\nconst { windowHeight, windowWidth } = getSysInfo()\n\ntype RootState = {\n /** 是否在滚动中 */\n isScrolling: boolean\n /** 滚动偏移量 */\n scrollOffset: number\n /**\n * 滚动方向\n *\n * - forward 向下滚动\n *\n * - backward 向上滚动\n */\n scrollDirection: ScrollDirection\n /** 滚动高度 */\n scrollHeight: number\n /** 容器的尺寸信息 */\n containerSize: Size\n /** 渲染的分组区间范围 */\n renderRange: [number, number]\n};\n\nexport const RootEvents = {\n ReachUpperThreshold: Symbol.for('ReachUpperThreshold'),\n ReachLowerThreshold: Symbol.for('ReachLowerThreshold'),\n Resize: Symbol.for('Resize'),\n AllSectionsLayouted: Symbol.for('AllSectionsLayouted'),\n InitialRenderCompleted: Symbol.for('InitialRenderCompleted'),\n}\n\ntype Events = keyof typeof RootEvents;\n\n/**\n * 数据模型继承自有状态的事件总线,便于在节点之间通信,以及通过 useSyncExternalStore 关联 React 视图\n */\nexport class Root extends StatefulEventBus<RootState, Events> {\n /**\n * 瀑布流根节点唯一标识\n */\n id: string\n /**\n * 分组映射表,便于查找分组\n */\n sectionMap: Map<string, Section> = new Map()\n /**\n * 节点映射表,便于查找节点\n */\n nodeMap: Map<string, Node> = new Map()\n /**\n * 分组列表,基于计算出的渲染的分组区间范围 sections.slice(start, end + 1) 进行渲染\n */\n sections: Section[] = []\n /**\n * 设置预加载的 Item 条数。\n */\n cacheCount = 1\n\n upperThresholdCount = 0\n\n lowerThresholdCount = 0\n\n /**\n * 触发滚动阈值对应的 scrollTop 值\n */\n upperThresholdScrollTop = -Infinity\n\n /**\n * 触发滚动阈值对应的 scrollTop 值\n */\n lowerThresholdScrollTop = Infinity\n\n constructor(props: RootProps) {\n const { id, cacheCount, lowerThresholdCount, upperThresholdCount } = props\n super({\n isScrolling: false,\n scrollOffset: 0,\n scrollDirection: 'forward',\n scrollHeight: windowHeight,\n renderRange: [0, 0],\n containerSize: {\n width: windowWidth,\n height: windowHeight,\n },\n })\n Object.assign(this, {\n id,\n cacheCount,\n lowerThresholdCount,\n upperThresholdCount,\n })\n this.setupSubscriptions()\n getRectSizeSync(`#${id}`, 100).then(\n ({ width = windowWidth, height = windowHeight }) => {\n this.setStateIn('containerSize', {\n width,\n height,\n })\n }\n )\n this.renderInitialLayout()\n }\n\n /**\n * 设置订阅事件\n */\n private setupSubscriptions() {\n /**\n * 滚动过程中计算渲染的分组区间\n * 滚动过程中分组的最大高度会发生更新,可以在这时计算滚动高度\n */\n this.sub('scrollOffset', () => {\n this.setStateIn('renderRange', this.getSectionRenderRange())\n this.updateScrollHeight()\n this.handleReachThreshold()\n })\n\n const lowerThresholdScrollTopDisposer = this.sub('scrollOffset', () => {\n const sectionSize = this.sections.length\n const lastSection = this.sections[sectionSize - 1]\n // 最后一个分组的每一列最后一行都已经完成了布局计算,那么这个时候的总高度应该是准确的\n if (\n lastSection.columnMap.every(\n (column) => column[column.length - 1].getState().layouted\n )\n ) {\n this.setLowerThresholdScrollTop()\n lowerThresholdScrollTopDisposer()\n }\n })\n\n this.sub(RootEvents.AllSectionsLayouted, () => {\n this.setUpperThresholdScrollTop()\n })\n\n this.sub(RootEvents.Resize, () => {\n this.setUpperThresholdScrollTop()\n this.setLowerThresholdScrollTop()\n })\n }\n\n /**\n * 渐进式渲染\n *\n * 因为初始没法知道每个分组的高度信息,不知道渲染边界,所以需要渐进式渲染\n *\n * 当目前的渲染批次的首个分组的scrollTop大于容器的高度,说明容器可视区域已经填满,没必要再往下渲染了\n *\n * @param [i=0] 从第几个分组开始渲染\n *\n */\n renderInitialLayout(i = 0) {\n Taro.nextTick(() => {\n const sectionSize = this.sections.length\n\n if (i >= sectionSize || i < 0) {\n this.pub(RootEvents.InitialRenderCompleted, null)\n return\n }\n const section = this.sections[i]\n section.layoutedSignal.promise.then(() => {\n this.setStateIn('renderRange', [\n 0,\n i + 1 > sectionSize ? sectionSize - 1 : i + 1,\n ])\n // 容器可视区域已经填满了,没必要再继续\n if (\n section.getState().scrollTop > this.getState().containerSize.height\n ) {\n this.pub(RootEvents.InitialRenderCompleted, section)\n return\n }\n\n this.renderInitialLayout(i + 1)\n })\n })\n }\n\n /**\n * 计算滚动阈值对应的 scrollTop 并设置 upperThresholdScrollTop\n * 当距顶部还有 upperThresholdCount 个 FlowItem 时的 scrollTop 值\n */\n private setUpperThresholdScrollTop() {\n // 如果没有设置阈值或阈值为0,则返回0\n if (!this.upperThresholdCount) {\n this.upperThresholdScrollTop = 0\n return 0\n }\n const sectionSize = this.sections.length\n const tracker = Array.from(\n { length: sectionSize },\n () => new Map<number, { accCount: number, accHeight: number }>() // Map<列, { 当前列累计个数,当前列累计高度 }>\n )\n\n // 从第一个分组开始扫描\n loopSeciton: for (let i = 0; i < sectionSize; i++) {\n const section = this.sections[i]\n const sectionTracker = tracker[i]\n const columnMap = section.columnMap\n // 扫描当前分组的每一列\n for (let col = 0; col < columnMap.length; col++) {\n const column = columnMap[col]\n const columnSize = column.length\n if (!sectionTracker.has(col)) {\n if (i === 0) {\n sectionTracker.set(col, { accCount: 0, accHeight: 0 })\n } else {\n const previousSectionTracker = tracker[i - 1]\n sectionTracker.set(col, {\n accCount: Math.max(\n ...[...previousSectionTracker.values()].map(\n (nodeTracker) => nodeTracker.accCount\n )\n ),\n accHeight: section.getState().scrollTop,\n })\n }\n }\n const colTracker = sectionTracker.get(col)!\n // 扫描当前列的每一行\n loopItem: for (let j = 0; j < columnSize; j++) {\n colTracker.accCount += 1\n colTracker.accHeight +=\n column[j].getState().height + (j === 0 ? 0 : section.rowGap)\n if (colTracker.accCount >= this.upperThresholdCount) {\n break loopItem\n }\n }\n }\n for (const [, colTracker] of sectionTracker) {\n if (colTracker.accCount >= this.upperThresholdCount) {\n this.upperThresholdScrollTop = colTracker.accHeight\n break loopSeciton\n }\n }\n }\n return this.upperThresholdScrollTop\n }\n\n /**\n * 计算滚动阈值对应的 scrollTop 并设置 lowerThresholdScrollTop\n * 当距底部还有 lowerThresholdCount 个 FlowItem 时的 scrollTop 值\n */\n private setLowerThresholdScrollTop() {\n if (this.lowerThresholdCount === 0) {\n this.lowerThresholdScrollTop =\n this.getState().scrollHeight - this.getState().containerSize.height\n return 0\n }\n const sectionSize = this.sections.length\n const tracker = Array.from(\n { length: sectionSize },\n () => new Map<number, { accCount: number, scrollTop: number }>()\n )\n // 从最后一个分组开始计算\n loopSeciton: for (let i = sectionSize - 1; i > 0; i--) {\n const section = this.sections[i]\n const sectionTracker = tracker[i]\n const columnMap = section.columnMap\n // 扫描当前分组的每一列\n for (let col = 0; col < columnMap.length; col++) {\n const column = columnMap[col]\n const columnSize = column.length\n if (!sectionTracker.has(col)) {\n if (i === sectionSize - 1) {\n sectionTracker.set(col, { accCount: 0, scrollTop: 0 })\n } else {\n const belowSectionTracker = tracker[i + 1]\n sectionTracker.set(col, {\n accCount: Math.max(\n ...[...belowSectionTracker.values()].map(\n (nodeTracker) => nodeTracker.accCount\n )\n ),\n scrollTop: 0,\n })\n }\n }\n const colTracker = sectionTracker.get(col)!\n // 从当前列的最后一行开始往前扫描\n loopItem: for (let j = columnSize - 1; j >= 0; j--) {\n colTracker.accCount += 1\n colTracker.scrollTop = column[j].getState().scrollTop\n if (colTracker.accCount >= this.lowerThresholdCount) {\n break loopItem\n }\n }\n }\n\n for (const [, colTracker] of sectionTracker) {\n if (colTracker.accCount >= this.lowerThresholdCount) {\n this.lowerThresholdScrollTop = colTracker.scrollTop\n break loopSeciton\n }\n }\n }\n\n return this.lowerThresholdScrollTop\n }\n\n /**\n * 处理滚动到阈值的情况\n * 检测当前滚动位置是否达到了上下阈值,并触发相应的事件\n */\n private handleReachThreshold() {\n const { upperThresholdScrollTop } = this\n const { scrollOffset, scrollDirection, containerSize } = this.getState()\n if (\n scrollDirection === 'backward' &&\n this.upperThresholdScrollTop !== -Infinity &&\n scrollOffset <= upperThresholdScrollTop\n ) {\n this.pub(RootEvents.ReachUpperThreshold)\n }\n if (\n scrollDirection === 'forward' &&\n this.lowerThresholdCount !== Infinity &&\n scrollOffset + containerSize.height >= this.lowerThresholdScrollTop\n ) {\n this.pub(RootEvents.ReachLowerThreshold)\n }\n }\n\n /**\n * 容器的滚动上边界\n */\n get scrollBoundaryStart() {\n return this.getState().scrollOffset\n }\n\n /**\n * 容器的滚动下边界\n */\n get scrollBoundaryEnd() {\n return this.scrollBoundaryStart + this.getStateIn('containerSize').height\n }\n\n /**\n * 计算每个section的底部位置\n *\n * sectionBottomRange = [ [section1.top, section1.bottom], [section2.top, section2.bottom], ..., [sectionN.top, sectionN.bottom] ]\n *\n * @returns [number,number][]\n */\n get sectionRange() {\n const length = this.sections.length\n if (length === 0) return []\n const range = Array.from({ length }, () => [\n 0,\n this.sections[0].maxColumnHeight,\n ])\n for (let i = 1; i < length; i++) {\n const previous = range[i - 1]\n range[i] = [previous[1], previous[1] + this.sections[i].maxColumnHeight]\n }\n\n return range\n }\n\n /**\n * 计算滚动高度\n */\n private updateScrollHeight() {\n this.setStateIn(\n 'scrollHeight',\n this.sectionRange[this.sectionRange.length - 1][1]\n )\n }\n\n /**\n * 注册分组\n */\n public registerSection(section: Section) {\n const { id, order } = section\n this.sectionMap.set(id, section)\n this.sections[order] = section\n }\n\n /**\n * 注册节点\n */\n public registerNode(node: Node) {\n this.nodeMap.set(node.id, node)\n }\n\n /**\n * 查找分组\n */\n public findSection(id: string) {\n return this.sectionMap.get(id)\n }\n\n /**\n * 查找节点\n */\n public findNode(id: string) {\n return this.nodeMap.get(id)\n }\n\n /**\n * 获取分组渲染区间\n */\n public getSectionRenderRange() {\n const result: [number, number] = [Infinity, -Infinity]\n\n for (let i = 0; i < this.sections.length; i++) {\n const section = this.sections[i]\n if (section.isInRange) {\n result[0] = Math.min(result[0], i)\n result[1] = Math.max(result[1], i)\n }\n }\n\n if (result[0] === Infinity) {\n result[0] = 0\n }\n\n if (result[1] === -Infinity) {\n result[1] = this.sections.length - 1\n }\n\n const scrollDirection = this.getState().scrollDirection\n const [backwardCache, forwardCache] = this.calcCacheSection(result)\n const backwardDistance = scrollDirection === 'backward' ? backwardCache : 0\n const forwardDistance = scrollDirection === 'forward' ? forwardCache : 0\n\n const overscanBackward = result[0] - backwardDistance\n const overscanForward = result[1] + forwardDistance\n\n result[0] = overscanBackward < 0 ? 0 : overscanBackward\n\n result[1] =\n overscanForward > this.sections.length\n ? this.sections.length - 1\n : overscanForward\n\n return isSameRenderRange(result, this.getState().renderRange)\n ? this.getState().renderRange\n : result\n }\n\n /**\n * 计算预渲染的分组个数\n */\n private calcCacheSection(renderRange: [number, number]) {\n const clientHeight = this.getState().containerSize.height\n const sectionCount = this.sectionMap.size\n let [start, end] = renderRange\n let cacheBackward = 1\n let cacheForward = 1\n\n if (start > 0) {\n let acc = this.sections[--start]?.getState().height ?? 0\n while (--start > 0) {\n const prevSection = this.sections[start]\n acc += prevSection.getState().height\n cacheBackward += 1\n if (acc >= clientHeight >>> 1) {\n break\n }\n }\n }\n\n if (end < sectionCount - 1) {\n let acc = this.sections[++end]?.getState().height ?? 0\n while (++end < sectionCount - 1) {\n const nextSection = this.sections[end]\n acc += nextSection.getState().height\n cacheForward += 1\n if (acc >= clientHeight >>> 1) {\n break\n }\n }\n }\n\n return [cacheBackward, cacheForward]\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Node } from './node';
|
|
2
|
+
import { Root } from './root';
|
|
3
|
+
import { StatefulEventBus } from './stateful-event-bus';
|
|
4
|
+
export interface SectionProps {
|
|
5
|
+
/** 分组的唯一标识 */
|
|
6
|
+
id: string;
|
|
7
|
+
/** 分组的列数 */
|
|
8
|
+
col: number;
|
|
9
|
+
/** 分组的顺序 */
|
|
10
|
+
order: number;
|
|
11
|
+
/** 节点数量 */
|
|
12
|
+
count: number;
|
|
13
|
+
/** 该分组的行间距 */
|
|
14
|
+
rowGap: number;
|
|
15
|
+
/** 该分组的列间距 */
|
|
16
|
+
columnGap: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 分组状态
|
|
20
|
+
*/
|
|
21
|
+
type SectionState = {
|
|
22
|
+
/**
|
|
23
|
+
* 分组是否已经布局计算
|
|
24
|
+
*/
|
|
25
|
+
layouted: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* 分组顶部距离容器顶部的距离
|
|
28
|
+
*/
|
|
29
|
+
scrollTop: number;
|
|
30
|
+
/**
|
|
31
|
+
* 分组的高度
|
|
32
|
+
*/
|
|
33
|
+
height: number;
|
|
34
|
+
/**
|
|
35
|
+
* 每个列的渲染范围
|
|
36
|
+
* 每个元素为一个二元组,第一个元素为起始索引,第二个元素为结束索引
|
|
37
|
+
*/
|
|
38
|
+
renderRange: [number, number][];
|
|
39
|
+
};
|
|
40
|
+
export declare const SectionEvents: {
|
|
41
|
+
AllNodesLayouted: symbol;
|
|
42
|
+
Resize: symbol;
|
|
43
|
+
};
|
|
44
|
+
export declare class Section extends StatefulEventBus<SectionState> {
|
|
45
|
+
root: Root;
|
|
46
|
+
id: string;
|
|
47
|
+
order: number;
|
|
48
|
+
col: number;
|
|
49
|
+
columnMap: Node[][];
|
|
50
|
+
nodes: Map<string, Node>;
|
|
51
|
+
defaultSize: number;
|
|
52
|
+
count: number;
|
|
53
|
+
rowGap: number;
|
|
54
|
+
columnGap: number;
|
|
55
|
+
layoutedSignal: {
|
|
56
|
+
promise: Promise<void>;
|
|
57
|
+
resolve: () => void;
|
|
58
|
+
reject: (reason?: any) => void;
|
|
59
|
+
};
|
|
60
|
+
constructor(root: Root, props: SectionProps);
|
|
61
|
+
/**
|
|
62
|
+
* 订阅事件
|
|
63
|
+
*/
|
|
64
|
+
private setupSubscriptions;
|
|
65
|
+
/**
|
|
66
|
+
* 获取当前分组的最大高度,分组的最大高度由分组最高列决定
|
|
67
|
+
* @returns 当前分组的最大高度
|
|
68
|
+
*/
|
|
69
|
+
get maxColumnHeight(): number;
|
|
70
|
+
/**
|
|
71
|
+
* 当前分组是否在可视区域
|
|
72
|
+
*
|
|
73
|
+
* 滚动偏移为 scrollOffset,容器的高度为 containerSize.height
|
|
74
|
+
*
|
|
75
|
+
* 当前容器滚动上边界(scrollBoundaryStart)为 scrollOffset,容器滚动的下边界(scrollBoundaryEnd)为 scrollOffset + containerSize.height
|
|
76
|
+
*
|
|
77
|
+
* 如果分组的 scrollTop 小于滚动下边界并且 scrollTop + 分组高度大于滚动上边界,那么分组在可视区域
|
|
78
|
+
*/
|
|
79
|
+
get isInRange(): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* 注册节点
|
|
82
|
+
*/
|
|
83
|
+
private register;
|
|
84
|
+
/**
|
|
85
|
+
* 初始化分组内的列,即将一维数组转二维数组
|
|
86
|
+
*/
|
|
87
|
+
private initializeColumnMap;
|
|
88
|
+
/**
|
|
89
|
+
* 更新当前分组之后的分组的位置信息
|
|
90
|
+
*/
|
|
91
|
+
private updateBehindSectionsPosition;
|
|
92
|
+
/**
|
|
93
|
+
* 更新指定列的节点位置信息
|
|
94
|
+
* @param col 列索引
|
|
95
|
+
*/
|
|
96
|
+
private updateColumnNode;
|
|
97
|
+
/**
|
|
98
|
+
* 更新每列节点的位置
|
|
99
|
+
*/
|
|
100
|
+
private updateNodes;
|
|
101
|
+
/**
|
|
102
|
+
* 计算当前分组的 scrollTop,即该分组之前的所有分组的最大列高度之和
|
|
103
|
+
*/
|
|
104
|
+
private calcScrollTop;
|
|
105
|
+
/**
|
|
106
|
+
* 计算当前分组内每列应该渲染的节点索引区间
|
|
107
|
+
*/
|
|
108
|
+
getNodeRenderRange(): [number, number][];
|
|
109
|
+
}
|
|
110
|
+
export {};
|