tencent.jquery.pix.component 1.0.6-6.beta1
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/components/banner/banner.js +461 -0
- package/components/banner/banner.scss +46 -0
- package/components/config.js +23 -0
- package/components/list/list.js +217 -0
- package/components/tips/tipv2.js +557 -0
- package/components/utils/env.js +7 -0
- package/components/utils/utils.js +65 -0
- package/components/video/resources/images/control-bg.png +0 -0
- package/components/video/resources/images/exit-full-screen.png +0 -0
- package/components/video/resources/images/full-screen.png +0 -0
- package/components/video/resources/images/mute.png +0 -0
- package/components/video/resources/images/origin-play.png +0 -0
- package/components/video/resources/images/pause.png +0 -0
- package/components/video/resources/images/play.png +0 -0
- package/components/video/resources/images/replay.png +0 -0
- package/components/video/resources/images/volume.png +0 -0
- package/components/video/videocss.scss +497 -0
- package/components/video/videohtml.js +85 -0
- package/components/video/videoplayer.js +425 -0
- package/components/waterfall/waterfall.js +819 -0
- package/components/waterfall/waterfall.scss +17 -0
- package/index.js +10 -0
- package/package.json +16 -0
- package/readme.md +15 -0
- package/utils/utils.js +16 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { getEnv } from "../config";
|
|
2
|
+
|
|
3
|
+
let $ = null;
|
|
4
|
+
|
|
5
|
+
// 默认配置
|
|
6
|
+
const DEFAULTS = {
|
|
7
|
+
itemHeight: 50, // 单条数据高度
|
|
8
|
+
buffer: 3, // 缓冲区预加载条数
|
|
9
|
+
data: [], // 数据源
|
|
10
|
+
container: '', // 容器元素
|
|
11
|
+
renderItem(data, index) { // 元素首次渲染时的回调函数, 如果把updateItem设置为空,那么更新时则会兜底触发renderItem
|
|
12
|
+
return '<div class="virtual-item"></div>';
|
|
13
|
+
},
|
|
14
|
+
// 传入 $node, data, index
|
|
15
|
+
updateItem: null, // 元素更新时的回调函数
|
|
16
|
+
onscroll: null, // 滚动事件回调函数
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function List(options = {}) {
|
|
20
|
+
$ = getEnv().$;
|
|
21
|
+
|
|
22
|
+
this.options = Object.assign({}, DEFAULTS, options);
|
|
23
|
+
// 标记是否有更新元素用的回调函数
|
|
24
|
+
this.hasUpdateItem = options.updateItem && (options.updateItem.constructor === Function) ? true : false;
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
this.init();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
List.prototype.init = function () {
|
|
31
|
+
const self = this;
|
|
32
|
+
const options = this.options;
|
|
33
|
+
const $container = $(options.container);
|
|
34
|
+
|
|
35
|
+
this.nodePool = []; // DOM 节点池
|
|
36
|
+
this.activeNodes = new Map(); // 当前活跃节点(索引 -> DOM)
|
|
37
|
+
|
|
38
|
+
// 高度字符串转数字
|
|
39
|
+
if (options.itemHeight.constructor === String) {
|
|
40
|
+
// 如果是rem单位,则需要计算
|
|
41
|
+
if (options.itemHeight.indexOf('rem') > -1) {
|
|
42
|
+
let fonSize = parseFloat($(document.body).css("font-size").replace('px', ''));
|
|
43
|
+
let num = parseFloat(options.itemHeight.replace('rem', ''));
|
|
44
|
+
options.itemHeight = fonSize * num;
|
|
45
|
+
} else {
|
|
46
|
+
options.itemHeight = parseFloat(options.itemHeight);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 设置滚动容器高度 = 数据总数 * 单条高度
|
|
51
|
+
const totalHeight = options.data.length * options.itemHeight;
|
|
52
|
+
|
|
53
|
+
$container.html(`
|
|
54
|
+
<div class="virtual-list-scroll" style="height: ${totalHeight}px">
|
|
55
|
+
<div class="virtual-list-viewport"></div>
|
|
56
|
+
</div>
|
|
57
|
+
`);
|
|
58
|
+
|
|
59
|
+
// 绑定滚动事件(节流处理)
|
|
60
|
+
$container.off().on('scroll', function () {
|
|
61
|
+
self.scrollTop = $(this).scrollTop();
|
|
62
|
+
|
|
63
|
+
window.requestAnimationFrame(() => {
|
|
64
|
+
self.updateVisibleItems();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (options.onscroll && options.onscroll.constructor === Function) {
|
|
68
|
+
options.onscroll(this, self.scrollTop);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
this.scrollTop = $container.scrollTop(); // 当前滚动位置
|
|
73
|
+
|
|
74
|
+
// 首次渲染
|
|
75
|
+
self.updateVisibleItems();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// force 强制更新渲染
|
|
79
|
+
List.prototype.updateVisibleItems = function (force = false) {
|
|
80
|
+
const self = this;
|
|
81
|
+
const options = this.options;
|
|
82
|
+
const $container = $(options.container);
|
|
83
|
+
|
|
84
|
+
let startIndex = 0; // 起始索引
|
|
85
|
+
let endIndex = 0; // 结束索引
|
|
86
|
+
|
|
87
|
+
const viewportHeight = $container.height();
|
|
88
|
+
// 计算当前可见项范围(含缓冲区)
|
|
89
|
+
startIndex = Math.max(0, Math.floor(self.scrollTop / options.itemHeight) - options.buffer);
|
|
90
|
+
endIndex = Math.min(
|
|
91
|
+
options.data.length - 1,
|
|
92
|
+
Math.ceil((self.scrollTop + viewportHeight) / options.itemHeight) + options.buffer
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// 生成当前可见项的 DOM
|
|
96
|
+
const $viewport = $container.find('.virtual-list-viewport');
|
|
97
|
+
|
|
98
|
+
/**方案1 */
|
|
99
|
+
const newActiveNodes = new Map();
|
|
100
|
+
|
|
101
|
+
// 阶段1:复用已有节点
|
|
102
|
+
for (let i = startIndex; i <= endIndex; i++) {
|
|
103
|
+
const $existingNode = this.activeNodes.get(i);
|
|
104
|
+
const top = i * options.itemHeight;
|
|
105
|
+
if ($existingNode) {
|
|
106
|
+
// 只有当位置不对应上才做位置有关的UI变更,否则top则为计算好了 不需要再次设置y值
|
|
107
|
+
newActiveNodes.set(i, $existingNode);
|
|
108
|
+
const index = parseInt($existingNode.attr('data-index'));
|
|
109
|
+
if (force === true || index !== i) {
|
|
110
|
+
// 直接更新位置和内容
|
|
111
|
+
$existingNode.css('transform', `translateY(${top}px)`);
|
|
112
|
+
$existingNode.attr('data-index', i)
|
|
113
|
+
|
|
114
|
+
self.updateRenderUI($existingNode, options.data[i], i);
|
|
115
|
+
|
|
116
|
+
}
|
|
117
|
+
this.activeNodes.delete(i);
|
|
118
|
+
} else {
|
|
119
|
+
// 从节点池获取或创建新节点
|
|
120
|
+
let nodePool = this.nodePool;
|
|
121
|
+
let $node = null;
|
|
122
|
+
if (nodePool.length === 0) {
|
|
123
|
+
$node = $(
|
|
124
|
+
`<div class="virtual-item"
|
|
125
|
+
style="position: absolute; transform:translateY(${top}px); height: ${options.itemHeight}px"
|
|
126
|
+
data-index="${i}">
|
|
127
|
+
${options.renderItem(options.data[i], i)}
|
|
128
|
+
</div>`
|
|
129
|
+
);
|
|
130
|
+
$viewport.append($node);
|
|
131
|
+
} else {
|
|
132
|
+
$node = $(this.nodePool.pop())
|
|
133
|
+
$node.css('transform', `translateY(${top}px)`).attr('data-index', i);
|
|
134
|
+
self.updateRenderUI($node, options.data[i], i);
|
|
135
|
+
}
|
|
136
|
+
newActiveNodes.set(i, $node);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 阶段2:回收不再使用的节点
|
|
141
|
+
this.activeNodes.forEach($node => {
|
|
142
|
+
$node.css('transform', `translateY(-9999px)`);// 移出可视区域
|
|
143
|
+
this.nodePool.push($node);
|
|
144
|
+
});
|
|
145
|
+
this.activeNodes = newActiveNodes;
|
|
146
|
+
|
|
147
|
+
/**方案2 */
|
|
148
|
+
// const $existingChildren = $viewport.children(); // 获取现有 DOM 元素集合
|
|
149
|
+
// const usedIndices = new Set(); // 记录当前需要保留的索引
|
|
150
|
+
|
|
151
|
+
// // 步骤 1:更新或保留现有元素
|
|
152
|
+
// if ($existingChildren.length > 0) {
|
|
153
|
+
// for (let j = 0; j < $existingChildren.length; j++) {
|
|
154
|
+
// const $el = $($existingChildren[j]);
|
|
155
|
+
// const oldIndex = parseInt($el.data('data-index'));
|
|
156
|
+
// if (oldIndex >= startIndex && oldIndex <= endIndex) {
|
|
157
|
+
// // 仍在可视范围内 → 更新位置和内容
|
|
158
|
+
// const newTop = oldIndex * options.itemHeight;
|
|
159
|
+
// $el.css('top', `${newTop}px`);
|
|
160
|
+
// usedIndices.add(oldIndex);
|
|
161
|
+
// } else {
|
|
162
|
+
// // 移出可视范围 → 从 DOM 中删除
|
|
163
|
+
// $el.remove();
|
|
164
|
+
// }
|
|
165
|
+
// }
|
|
166
|
+
// }
|
|
167
|
+
|
|
168
|
+
// // 步骤 2:补充新增元素
|
|
169
|
+
// for (let i = startIndex; i <= endIndex; i++) {
|
|
170
|
+
// if (!usedIndices.has(i)) {
|
|
171
|
+
// // 需要新增的元素
|
|
172
|
+
// const top = i * options.itemHeight;
|
|
173
|
+
// const $element = $(
|
|
174
|
+
// `<div class="virtual-item"
|
|
175
|
+
// style="position: absolute; top: ${top}px; height: ${options.itemHeight}px"
|
|
176
|
+
// data-index="${i}">
|
|
177
|
+
// ${options.renderItem(options.data[i], i)}
|
|
178
|
+
// </div>`
|
|
179
|
+
// );
|
|
180
|
+
// // 插入到正确位置(按索引顺序)
|
|
181
|
+
// let inserted = false;
|
|
182
|
+
// for (let j = 0; j < $existingChildren.length; j++) {
|
|
183
|
+
// const $el = $($existingChildren[j]);
|
|
184
|
+
// const currentIndex = parseInt($el.data('data-index'));
|
|
185
|
+
// if (currentIndex > i) {
|
|
186
|
+
// $element.insertBefore($el);
|
|
187
|
+
// inserted = true;
|
|
188
|
+
// break;
|
|
189
|
+
// }
|
|
190
|
+
// }
|
|
191
|
+
|
|
192
|
+
// if (!inserted) {
|
|
193
|
+
// $viewport.append($element);
|
|
194
|
+
// }
|
|
195
|
+
// }
|
|
196
|
+
// }
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 外部更新数据方法
|
|
201
|
+
List.prototype.updateData = function (newData) {
|
|
202
|
+
const options = this.options;
|
|
203
|
+
options.data = newData;
|
|
204
|
+
const totalHeight = options.data.length * options.itemHeight
|
|
205
|
+
// 重新计算高度
|
|
206
|
+
$(this.options.container).children().height(`${totalHeight}px`);
|
|
207
|
+
this.updateVisibleItems(true); // 强制更新渲染
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
List.prototype.updateRenderUI = function ($node, data, index) {
|
|
211
|
+
const options = this.options;
|
|
212
|
+
if (this.hasUpdateItem === true) {
|
|
213
|
+
options.updateItem($node, data, index)
|
|
214
|
+
} else {
|
|
215
|
+
$node.html(options.renderItem(data, index));
|
|
216
|
+
}
|
|
217
|
+
}
|