tencent.jquery.pix.component 1.0.53

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.
@@ -0,0 +1,393 @@
1
+ import { $, windowEnv } from "../config.js";
2
+ import { remToPx } from "../../utils/utils.js";
3
+
4
+ // 默认配置
5
+ const DEFAULTS = {
6
+ columns: 2, // 列数
7
+ columnGap: 10, // 左右间隔
8
+ rowGap: 12, // 上下间隔
9
+ marginTop: 0, // 距离顶部距离
10
+ marginBottom: 12, // 最后一行距离底部距离
11
+ bufferHeight: '2rem', // 缓冲高度,进行可能的预先添加高度
12
+ data: [], // 数据源
13
+ container: '', // 容器元素
14
+ renderItem(data, index) { // 元素首次渲染时的回调函数, 如果把updateItem设置为空,那么更新时则会兜底触发renderItem
15
+ return '<div class="waterfall-item"></div>';
16
+ },
17
+ // 传入 $node, data, index
18
+ updateItem: null, // 元素更新时的回调函数
19
+ onscroll: null, // 滚动事件回调函数
20
+ };
21
+
22
+ export function Waterfall(optionsInput = {}) {
23
+ this.options = Object.assign({}, DEFAULTS, optionsInput);
24
+ const options = this.options;
25
+ const $container = $(options.container);
26
+ // 标记是否有更新元素用的回调函数
27
+ this.hasUpdateItem = options.updateItem && (options.updateItem.constructor === Function) ? true : false;
28
+
29
+ // 间隔字符串转数字
30
+ if (options.columnGap.constructor === String) {
31
+ // 如果是rem单位,则需要计算
32
+ if (options.columnGap.indexOf('rem') > -1) {
33
+ options.columnGap = remToPx(options.columnGap);
34
+ } else {
35
+ options.columnGap = parseFloat(options.columnGap);
36
+ }
37
+ }
38
+ if (options.rowGap.constructor === String) {
39
+ // 如果是rem单位,则需要计算
40
+ if (options.rowGap.indexOf('rem') > -1) {
41
+ options.rowGap = remToPx(options.rowGap);
42
+ } else {
43
+ options.rowGap = parseFloat(options.rowGap);
44
+ }
45
+ }
46
+ if (options.bufferHeight.constructor === String) {
47
+ // 如果是rem单位,则需要计算
48
+ if (options.bufferHeight.indexOf('rem') > -1) {
49
+ options.bufferHeight = remToPx(options.bufferHeight);
50
+ } else {
51
+ options.bufferHeight = parseFloat(options.bufferHeight);
52
+ }
53
+ }
54
+ if (options.marginTop.constructor === String) {
55
+ // 如果是rem单位,则需要计算
56
+ if (options.marginTop.indexOf('rem') > -1) {
57
+ options.marginTop = remToPx(options.marginTop);
58
+ } else {
59
+ options.marginTop = parseFloat(options.marginTop);
60
+ }
61
+ }
62
+ if (options.marginBottom.constructor === String) {
63
+ // 如果是rem单位,则需要计算
64
+ if (options.marginBottom.indexOf('rem') > -1) {
65
+ options.marginBottom = remToPx(options.marginBottom);
66
+ } else {
67
+ options.marginBottom = parseFloat(options.marginBottom);
68
+ }
69
+ }
70
+
71
+ const allWidth = $container.width();
72
+ this.allWidth = allWidth;
73
+ // 计算每列的宽度
74
+ this.columnWidth = (allWidth - (options.columns - 1) * options.columnGap) / options.columns;
75
+
76
+ // 记录列数
77
+ this.columns = options.columns;
78
+
79
+ this.columnItems = []; // 列元素数组
80
+ for (let i = 0; i < this.columns; i++) {
81
+ this.columnItems.push(this.createColumn(i));
82
+ }
83
+
84
+ this.renderIndex = 0; // 渲染索引
85
+ this.activeNodes = new Map(); // 当前活跃节点(索引 -> DOM)
86
+ this.nodePool = []; // DOM 节点池
87
+
88
+ this.init();
89
+ }
90
+
91
+ Waterfall.prototype.init = function () {
92
+ const self = this;
93
+ const options = this.options;
94
+ const $container = $(options.container);
95
+
96
+ this.nodePool = []; // DOM 节点池
97
+ this.activeNodes = new Map(); // 当前活跃节点(索引 -> DOM)
98
+
99
+
100
+
101
+ $container.html(`
102
+ <div class="waterfall-list-scroll" style="">
103
+ <div class="waterfall-list-viewport"></div>
104
+ </div>
105
+ `);
106
+
107
+ // 绑定滚动事件(节流处理)
108
+ $container.off().on('scroll', function () {
109
+ self.scrollTop = $(this).scrollTop();
110
+
111
+ window.requestAnimationFrame(() => {
112
+ self.updateVisibleItems();
113
+ });
114
+
115
+ if (options.onscroll && options.onscroll.constructor === Function) {
116
+ options.onscroll(this, self.scrollTop);
117
+ }
118
+ });
119
+
120
+ this.scrollTop = $container.scrollTop(); // 当前滚动位置
121
+
122
+ // 首次渲染
123
+ self.updateVisibleItems();
124
+
125
+ }
126
+
127
+ // force 强制更新渲染
128
+ Waterfall.prototype.updateVisibleItems = function (force = false) {
129
+ const self = this;
130
+ const options = this.options;
131
+ const $container = $(options.container);
132
+
133
+ const startTop = this.scrollTop;
134
+ const endTop = startTop + $container.height();
135
+
136
+ // 进行可见区域的渲染更新
137
+ this.updateCardsInView({
138
+ start: startTop,
139
+ end: endTop,
140
+ force
141
+ });
142
+ }
143
+
144
+ // 新增卡片
145
+ Waterfall.prototype.appendCard = function (data, index, { top, left }) {
146
+ const self = this;
147
+ const options = this.options;
148
+ const $container = $(options.container);
149
+ const $viewport = $container.find('.waterfall-list-viewport');
150
+ const $card = $(
151
+ `<div class="waterfall-item"
152
+ data-index="${index}"
153
+ style="position: absolute;transform:translate(${left}px,${top}px); width:${this.columnWidth}px;"
154
+ >
155
+ ${options.renderItem(data, index)}
156
+ </div> `
157
+ );
158
+
159
+ $viewport.append($card);
160
+ return $card;
161
+ }
162
+
163
+ // 获取指定高度下的卡片索引
164
+ Waterfall.prototype.updateCardsInView = function ({ start, end, force = false }) {
165
+ const options = this.options;
166
+ const minHeight = this.getMinHeight();
167
+ const endBuffer = end + options.bufferHeight;
168
+ if (minHeight < endBuffer) {
169
+ // 如果不够 进行补建
170
+ this.createCards({ end: endBuffer });
171
+ }
172
+
173
+ // 基于已有的列信息,进行高度可视区域下的判定 操作已有信息。 如果是新建信息 则依赖上方的createCards方法进行创建,那么在上方会异步创建新卡片。
174
+ // 阶段1:复用已有节点
175
+ const newActiveNodes = new Map();
176
+ for (let i = 0; i < this.columns; i++) {
177
+ const column = this.columnItems[i];
178
+
179
+ for (let j = 0; j < column.children.length; j++) {
180
+ const row = column.children[j];
181
+ const renderIndex = row.renderIndex;
182
+ // 在可视区域内 进行有关卡片的操作
183
+ if (row.top <= end && row.bottom >= start) {
184
+ // 理论上什么都不动,因为卡片的位置不会变
185
+ const $card = this.activeNodes.get(renderIndex);
186
+
187
+ let $node = null;
188
+ // 如果卡片已经在DOM中,则不用更新位置
189
+ if ($card) {
190
+ $node = $card;
191
+ this.activeNodes.delete(renderIndex);
192
+ if (force) {
193
+ this.updateRenderUI($node, options.data[renderIndex], renderIndex);
194
+ }
195
+ } else {
196
+ // 卡片不在DOM中,则更新位置
197
+ let nodePool = this.nodePool;
198
+ if (nodePool.length === 0) {
199
+ // 这里是往上方拖动时,可能需要补建的情况
200
+ // 如果池子没有节点,那么进行创建
201
+ $node = this.appendCard(options.data[renderIndex], renderIndex, {
202
+ top: row.top, left: row.left
203
+ })
204
+ row.$node = $node;
205
+ } else {
206
+ $node = $(this.nodePool.pop());
207
+ $node.css({
208
+ 'transform': `translate(${row.left}px,${row.top}px)`,
209
+ }).attr('data-index', renderIndex);
210
+ this.updateRenderUI($node, options.data[renderIndex], renderIndex);
211
+ row.$node = $node;
212
+ }
213
+ }
214
+
215
+ newActiveNodes.set(renderIndex, $node);
216
+ }
217
+ }
218
+ }
219
+
220
+ // 阶段2:处理不活跃节点
221
+ this.activeNodes.forEach($node => {
222
+ $node.css('transform', `translateY(-9999px)`);// 移出可视区域
223
+ this.nodePool.push($node);
224
+ });
225
+ this.activeNodes = newActiveNodes;
226
+ }
227
+
228
+ Waterfall.prototype.getMaxHeight = function () {
229
+ let maxHeight = 0;
230
+ for (let i = 0; i < this.columns; i++) {
231
+ const column = this.columnItems[i]
232
+
233
+ // 获取每组元素列表的最后一个bottom值
234
+ maxHeight = Math.max(maxHeight, column.bottom);
235
+
236
+ }
237
+ return maxHeight;
238
+ }
239
+
240
+ Waterfall.prototype.getMinHeight = function () {
241
+ let minHeight = 0;
242
+ for (let i = 0; i < this.columns; i++) {
243
+ const column = this.columnItems[i]
244
+ if (minHeight === 0) {
245
+ minHeight = column.bottom;
246
+ }
247
+ // 获取每组元素列表的最后一个bottom值
248
+ minHeight = Math.min(minHeight, column.bottom);
249
+
250
+ }
251
+ return minHeight;
252
+ }
253
+
254
+ Waterfall.prototype.getMinHeightColumn = function () {
255
+ let minHeight = -1;
256
+ let mimHeightColumn = null;
257
+ for (let i = 0; i < this.columns; i++) {
258
+ const column = this.columnItems[i]
259
+
260
+ // 获取每组元素列表的最后一个bottom值
261
+ if (minHeight > column.bottom) {
262
+ minHeight = column.bottom;
263
+ mimHeightColumn = column;
264
+ } else if (minHeight === -1) {
265
+ minHeight = column.bottom;
266
+ mimHeightColumn = column;
267
+ }
268
+
269
+ }
270
+
271
+ return mimHeightColumn;
272
+ }
273
+
274
+ // 创建卡片
275
+ Waterfall.prototype.createCards = function ({ end }) {
276
+ const self = this;
277
+ const options = this.options;
278
+ const $container = $(options.container);
279
+ const renderIndex = this.renderIndex;
280
+
281
+ if (renderIndex >= options.data.length) {
282
+ const maxHeight = this.getMaxHeight();
283
+ // 设置一次整体高度
284
+ $(options.container).find('.waterfall-list-scroll').css('height', maxHeight + options.marginBottom)
285
+ return;
286
+ }
287
+
288
+ let column = this.getMinHeightColumn();
289
+ if (column === null) {
290
+ // 没有可用的列,则在第一列创建,说明此时还没有数据
291
+ column = this.columnItems[0];
292
+ }
293
+
294
+ const top = column.bottom === 0 ? options.marginTop : (column.bottom + options.rowGap);
295
+
296
+ // 设置卡片位置
297
+ const position = {
298
+ top,
299
+ left: column.left,
300
+ }
301
+
302
+ const row = createDefaultRow(position);
303
+
304
+
305
+ // 添加卡片
306
+ let $card = null;
307
+ if (this.nodePool.length === 0) {
308
+ $card = this.appendCard(options.data[renderIndex], renderIndex, position);
309
+ } else {
310
+ $card = $(this.nodePool.pop());
311
+ $card.css({
312
+ 'transform': `translate(${row.left}px,${row.top}px)`,
313
+ }).attr('data-index', renderIndex);
314
+ this.updateRenderUI($card, options.data[renderIndex], renderIndex);
315
+ }
316
+
317
+
318
+ row.$node = $card;
319
+
320
+ // 把新增的卡片放进 activeNodes, 当前是 展示状态的
321
+ this.activeNodes.set(renderIndex, $card);
322
+
323
+
324
+ // 更新列的底部距离
325
+ column.bottom = top + $card.height();
326
+ column.children.push(row);
327
+
328
+ // 计算当前卡片的位置
329
+ row.bottom = column.bottom;
330
+ row.renderIndex = renderIndex;
331
+
332
+ this.renderIndex += 1;
333
+
334
+ let hasNextData = this.renderIndex < options.data.length;
335
+
336
+ const minHeight = this.getMinHeight();
337
+ if (hasNextData && (minHeight < end)) {
338
+ window.requestAnimationFrame(() => {
339
+ this.createCards({ end });
340
+ });
341
+ } else {
342
+ const maxHeight = this.getMaxHeight();
343
+ // 设置一次整体高度
344
+ $(options.container).find('.waterfall-list-scroll').css('height', maxHeight + options.marginBottom)
345
+ }
346
+ }
347
+
348
+ Waterfall.prototype.createColumn = function (index) {
349
+ const res = {
350
+ left: 0,
351
+ bottom: 0,
352
+ width: 0,
353
+ children: []
354
+ }
355
+
356
+ const options = this.options;
357
+ const columnWidth = this.columnWidth;
358
+
359
+ res.width = this.columnWidth;
360
+ res.left = (index * (columnWidth + options.columnGap));
361
+ return res;
362
+ }
363
+
364
+ Waterfall.prototype.updateRenderUI = function ($node, data, index) {
365
+ const options = this.options;
366
+ if (this.hasUpdateItem === true) {
367
+ options.updateItem($node, data, index)
368
+ } else {
369
+ $node.html(options.renderItem(data, index));
370
+ }
371
+ }
372
+
373
+ Waterfall.prototype.updateData = function (newData) {
374
+ this.options.data = newData;
375
+ this.updateVisibleItems(true); // 强制更新渲染
376
+ }
377
+
378
+ function createDefaultRow({ top, left }) {
379
+ return {
380
+ top,
381
+ left,
382
+ bottom: 0,
383
+ $node: null,
384
+ renderIndex: -1
385
+ }
386
+ }
387
+
388
+ function getBottomByColumn(rows) {
389
+ if (rows.children.length === 0) {
390
+ return 0;
391
+ }
392
+ return rows[rows.length - 1].bottom;
393
+ }
package/index.js ADDED
@@ -0,0 +1,10 @@
1
+ export { VideoPlayer } from './components/video/videoplayer';
2
+ export { tips, showTips } from './components/tips/tipv2';
3
+ export { Banner } from './components/banner/banner';
4
+ export { List } from './components/list/list';
5
+ export { Waterfall } from './components/waterfall/waterfall';
6
+ export { getEnv, setEnv } from './components/config';
7
+ export { getFontSize, remToPx } from './utils/utils';
8
+
9
+
10
+
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "tencent.jquery.pix.component",
3
+ "version": "1.0.53",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "files": [
7
+ "components",
8
+ "utils"
9
+ ],
10
+ "scripts": {
11
+ "test": "echo \"Error: no test specified\" && exit 1"
12
+ },
13
+ "author": "winkchen, jackyjhe",
14
+ "license": "ISC",
15
+ "dependencies": {}
16
+ }
package/readme.md ADDED
@@ -0,0 +1,15 @@
1
+ # jquery.pix.component
2
+
3
+ 基于jquery.pix开发的pixui通用组件库。
4
+
5
+ - 用于web浏览器基于正常的jquery版本(1.9系列)
6
+
7
+ - 用于pix小应用基于@tencent/jquery.pix
8
+
9
+ git 地址
10
+
11
+ https://git.woa.com/linksee-web/pix/jquery.pix.component
12
+
13
+ 在线文档
14
+
15
+ https://static-exp.native.qq.com/webfront/pixcomponent/QNgV5k6r/dist/#/
package/utils/utils.js ADDED
@@ -0,0 +1,16 @@
1
+ import { $ } from "../components/config";
2
+
3
+ let fontSize = -1;
4
+
5
+ // 获取字体大小
6
+ export const getFontSize = () => {
7
+ if (fontSize === -1) {
8
+ fontSize = parseFloat($(document.body).css("font-size").replace('px', ''));
9
+ }
10
+ return fontSize;
11
+ }
12
+
13
+ // rem 转 px
14
+ export const remToPx = (rem) => {
15
+ return getFontSize() * parseFloat(rem.toString().replace('rem', ''));
16
+ }