tencent.jquery.pix.component 1.0.76 → 1.0.78
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,6 +1,9 @@
|
|
|
1
1
|
$jqpix-video-progress-bold-scale: 2 !default;
|
|
2
|
+
$jqpix-video-progress-handle-bold-scale: 0.5 !default;
|
|
2
3
|
$jqpix-video-primary-color: #ff6429 !default;
|
|
3
4
|
|
|
5
|
+
@use "sass:math";
|
|
6
|
+
|
|
4
7
|
.myplayer-shrink1 {
|
|
5
8
|
flex-shrink: 1;
|
|
6
9
|
}
|
|
@@ -270,16 +273,20 @@ $jqpix-video-primary-color: #ff6429 !default;
|
|
|
270
273
|
max-width: 100%;
|
|
271
274
|
}
|
|
272
275
|
|
|
273
|
-
.myplayer-container .myplayer-subprogress
|
|
276
|
+
.myplayer-container .myplayer-subprogress .myplayer-progress-handle {
|
|
274
277
|
content: "";
|
|
275
278
|
position: absolute;
|
|
276
|
-
width: 0.
|
|
277
|
-
height: 0.
|
|
279
|
+
width: 0.5rem;
|
|
280
|
+
height: 0.5rem;
|
|
278
281
|
top: 50%;
|
|
279
|
-
right: -0.
|
|
280
|
-
margin-top: -0.
|
|
281
|
-
|
|
282
|
-
background-color:
|
|
282
|
+
right: -0.25rem;
|
|
283
|
+
margin-top: -0.25rem;
|
|
284
|
+
border-radius: 0.25rem;
|
|
285
|
+
background-color: $jqpix-video-primary-color;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
.myplayer-container .myplayer-progress-bold .myplayer-subprogress .myplayer-progress-handle {
|
|
289
|
+
transform: scaleY($jqpix-video-progress-handle-bold-scale);
|
|
283
290
|
}
|
|
284
291
|
|
|
285
292
|
.myplayer-container .myplayer-resolution {
|
|
@@ -20,6 +20,7 @@ import VideoHTML from "./videohtml";
|
|
|
20
20
|
* @param {number} [options.autoHideControlsDelayMs=5000] 自动隐藏控制条延迟时间,单位ms
|
|
21
21
|
* @param {boolean} [options.showProgressBar=true] 是否显示进度条
|
|
22
22
|
* @param {boolean} [options.showVolumeControl=false] 是否显示音量控制
|
|
23
|
+
* @param {boolean} [options.showProgressHandle=true] 是否显示进度条滑块
|
|
23
24
|
* @param {boolean} [options.clickToPause=false] 点击播放区域是否直接暂停,true: 直接暂停,false: 先展示控制条
|
|
24
25
|
* @param {(this: VideoPlayer, type: VideoPlayerState) => any} [options.stateChanged] 状态变化回调
|
|
25
26
|
*/
|
|
@@ -59,6 +60,7 @@ VideoPlayer.prototype.init = async function () {
|
|
|
59
60
|
autoHideControlsDelayMs: 5000,
|
|
60
61
|
showProgressBar: true,
|
|
61
62
|
showVolumeControl: false,
|
|
63
|
+
showProgressHandle: true,
|
|
62
64
|
clickToPause: false,
|
|
63
65
|
...this.options
|
|
64
66
|
}
|
|
@@ -88,6 +90,11 @@ VideoPlayer.prototype.init = async function () {
|
|
|
88
90
|
$container.find('.myplayer-progress').hide();
|
|
89
91
|
}
|
|
90
92
|
|
|
93
|
+
// 根据showProgressHandle选项控制进度条滑块显示
|
|
94
|
+
if (!this.options.showProgressHandle) {
|
|
95
|
+
$container.find('.myplayer-progress-handle').hide();
|
|
96
|
+
}
|
|
97
|
+
|
|
91
98
|
// 根据showVolumeControl选项控制音量控件显示
|
|
92
99
|
if (this.options.showVolumeControl) {
|
|
93
100
|
$container.find('.myplayer-volume').show();
|
|
@@ -496,11 +503,13 @@ VideoPlayer.prototype.progressDragStart = function (e) {
|
|
|
496
503
|
if (!this.options.showProgressBar) {
|
|
497
504
|
return;
|
|
498
505
|
}
|
|
499
|
-
|
|
500
|
-
this.state.updatingProgress = false;
|
|
501
506
|
if (this.state.playing === true) {
|
|
502
507
|
this.pause();
|
|
503
508
|
}
|
|
509
|
+
|
|
510
|
+
// 更新进度条时,不更新播放时间
|
|
511
|
+
// 这个赋值必须放在最后,因为pause会设置this.state.updatingProgress为true
|
|
512
|
+
this.state.updatingProgress = false;
|
|
504
513
|
}
|
|
505
514
|
|
|
506
515
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import "./waterfall.scss"
|
|
2
|
-
import { getEnv } from "../config.js";
|
|
3
1
|
import { remToPx } from "../../utils/utils.js";
|
|
2
|
+
import { getEnv } from "../config.js";
|
|
3
|
+
import "./waterfall.scss";
|
|
4
4
|
|
|
5
5
|
let $ = null;
|
|
6
6
|
|
|
@@ -15,7 +15,7 @@ const DEFAULTS = {
|
|
|
15
15
|
startPoints: [], // 起始点距离
|
|
16
16
|
data: [], // 数据源
|
|
17
17
|
container: '', // 容器元素
|
|
18
|
-
renderItem(data, index) { // 元素首次渲染时的回调函数, 如果把updateItem设置为空,那么更新时则会兜底触发renderItem
|
|
18
|
+
renderItem(data, index, $card) { // 元素首次渲染时的回调函数, 如果把updateItem设置为空,那么更新时则会兜底触发renderItem
|
|
19
19
|
return '<div class="waterfall-item"></div>';
|
|
20
20
|
},
|
|
21
21
|
scrollDom: null, // 滚动元素,如果传入了滚动元素,那么用来计算的窗口高度就以滚动元素的高度为准
|
|
@@ -236,10 +236,17 @@ Waterfall.prototype.appendCard = function (data, dataId, { top, left }) {
|
|
|
236
236
|
data-index="${dataId}"
|
|
237
237
|
style="position: absolute;transform:translate(${left}px,${top}px);"
|
|
238
238
|
>
|
|
239
|
-
${options.renderItem(data, dataInfo.originalIndex)}
|
|
240
239
|
</div> `
|
|
241
240
|
);
|
|
242
241
|
|
|
242
|
+
$viewport.append($card);
|
|
243
|
+
|
|
244
|
+
const str = options.renderItem(data, dataInfo.originalIndex, $card);
|
|
245
|
+
|
|
246
|
+
if (str) {
|
|
247
|
+
$card.html(str);
|
|
248
|
+
}
|
|
249
|
+
|
|
243
250
|
this.renderedDataIds.add(dataId);
|
|
244
251
|
|
|
245
252
|
if (options.columns !== 1) {
|
|
@@ -247,7 +254,6 @@ Waterfall.prototype.appendCard = function (data, dataId, { top, left }) {
|
|
|
247
254
|
}
|
|
248
255
|
|
|
249
256
|
|
|
250
|
-
$viewport.append($card);
|
|
251
257
|
return $card;
|
|
252
258
|
}
|
|
253
259
|
|
|
@@ -287,14 +293,28 @@ Waterfall.prototype.updateCardsInView = async function ({ start, end, force = fa
|
|
|
287
293
|
|
|
288
294
|
const data = options.data[dataId]
|
|
289
295
|
|
|
290
|
-
//
|
|
296
|
+
// 如果当前这个节点是特殊节点,只更新位置,不参与节点复用
|
|
291
297
|
// 如果是特殊的静态占用元素卡片,需要指定节点不变更的数据,那么该数据的节点不能被其他数据使用
|
|
292
298
|
let specialNode = false;
|
|
293
299
|
if (options.shouldOccupySpace) {
|
|
294
300
|
specialNode = options.shouldOccupySpace(data) || false;
|
|
295
301
|
}
|
|
296
302
|
if (specialNode) {
|
|
297
|
-
|
|
303
|
+
// 特殊节点:只更新位置,不参与复用逻辑
|
|
304
|
+
if (row.$node && row.$node.length) {
|
|
305
|
+
// 直接更新特殊节点的位置
|
|
306
|
+
row.$node.css({
|
|
307
|
+
'transform': `translate(${row.left}px,${row.top}px)`,
|
|
308
|
+
}).attr('data-index', dataId);
|
|
309
|
+
|
|
310
|
+
// 如果是强制更新,也重新渲染内容
|
|
311
|
+
if (force) {
|
|
312
|
+
this.updateRenderUI(row.$node, data, dataId);
|
|
313
|
+
}
|
|
314
|
+
} else {
|
|
315
|
+
console.warn('Waterfall: Special node DOM not found for dataId', dataId);
|
|
316
|
+
}
|
|
317
|
+
continue; // 跳过普通节点的复用逻辑
|
|
298
318
|
}
|
|
299
319
|
|
|
300
320
|
// 在可视区域内 进行有关卡片的操作
|
|
@@ -531,6 +551,9 @@ Waterfall.prototype.createCards = function ({ end, dataId = -1 }, callback) {
|
|
|
531
551
|
column.children.push(row);
|
|
532
552
|
row.bottom = column.bottom;
|
|
533
553
|
|
|
554
|
+
// 建立 dataIdMap -> row 的引用,用于快速访问布局信息
|
|
555
|
+
dataInfo.layoutInfo = row;
|
|
556
|
+
|
|
534
557
|
// 检查是否需要继续创建卡片
|
|
535
558
|
const minHeight = this.getMinHeight();
|
|
536
559
|
const hasMoreData = this.renderedDataIds.size < this.dataIdMap.size;
|
|
@@ -590,7 +613,32 @@ Waterfall.prototype.updateRenderUI = function ($node, data, dataId) {
|
|
|
590
613
|
if (this.hasUpdateItem === true) {
|
|
591
614
|
options.updateItem($node, data, dataInfo.originalIndex)
|
|
592
615
|
} else {
|
|
593
|
-
|
|
616
|
+
const str = options.renderItem(data, dataInfo.originalIndex, $node);
|
|
617
|
+
if (str) {
|
|
618
|
+
$node.html(str);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// 辅助方法:使用 renderItem 渲染节点(用于全新节点)
|
|
624
|
+
Waterfall.prototype.renderUI = function ($node, data, dataId) {
|
|
625
|
+
const options = this.options;
|
|
626
|
+
|
|
627
|
+
// 新方案:基于数据ID的数据验证
|
|
628
|
+
if (!this.dataIdMap.has(dataId)) {
|
|
629
|
+
console.error('Waterfall: Invalid dataId in renderUI', dataId);
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const dataInfo = this.dataIdMap.get(dataId);
|
|
634
|
+
if (!dataInfo || !dataInfo.data) {
|
|
635
|
+
console.warn('Waterfall: Empty data for dataId', dataId);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
// 始终使用 renderItem,因为这是全新节点
|
|
639
|
+
const str = options.renderItem(data, dataInfo.originalIndex, $node);
|
|
640
|
+
if (str) {
|
|
641
|
+
$node.html(str);
|
|
594
642
|
}
|
|
595
643
|
}
|
|
596
644
|
|
|
@@ -600,55 +648,367 @@ Waterfall.prototype.updateData = async function (newData) {
|
|
|
600
648
|
const options = this.options;
|
|
601
649
|
options.data = newData;
|
|
602
650
|
|
|
603
|
-
//
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
//this.nextDataId = 0;
|
|
651
|
+
// 步骤1: 批量获取所有卡片的新高度(只需一次 requestAnimationFrame)
|
|
652
|
+
const dataIds = this.renderedDataIds;
|
|
653
|
+
const newHeightsMap = await this.getBatchCardNewHeights(dataIds, newData);
|
|
607
654
|
|
|
608
|
-
//
|
|
609
|
-
|
|
610
|
-
let count = options.data.length - 1;
|
|
611
|
-
let index = 0;
|
|
655
|
+
// 步骤2: 收集高度变化
|
|
656
|
+
const heightChanges = new Map(); // dataId -> {oldHeight, newHeight, heightDiff}
|
|
612
657
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
658
|
+
for (let dataId of dataIds) {
|
|
659
|
+
const oldHeight = this.getCardOldHeight(dataId);
|
|
660
|
+
const newHeight = newHeightsMap.get(dataId);
|
|
661
|
+
|
|
662
|
+
if (oldHeight !== newHeight) {
|
|
663
|
+
heightChanges.set(dataId, {
|
|
664
|
+
oldHeight,
|
|
665
|
+
newHeight,
|
|
666
|
+
heightDiff: newHeight - oldHeight
|
|
667
|
+
});
|
|
618
668
|
}
|
|
669
|
+
}
|
|
619
670
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
671
|
+
// 步骤3: 使用累积差分算法更新每一列的布局
|
|
672
|
+
for (let i = 0; i < this.columnItems.length; i++) {
|
|
673
|
+
const column = this.columnItems[i];
|
|
674
|
+
let accumulatedDiff = 0; // 累积的高度变化
|
|
675
|
+
|
|
676
|
+
for (let j = 0; j < column.children.length; j++) {
|
|
677
|
+
const row = column.children[j];
|
|
678
|
+
const dataId = row.dataId;
|
|
679
|
+
|
|
680
|
+
// 先应用之前累积的差分
|
|
681
|
+
if (accumulatedDiff !== 0) {
|
|
682
|
+
row.top += accumulatedDiff;
|
|
683
|
+
row.bottom += accumulatedDiff;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// 如果当前卡片有高度变化,累积到差分中
|
|
687
|
+
if (heightChanges.has(dataId)) {
|
|
688
|
+
const change = heightChanges.get(dataId);
|
|
689
|
+
accumulatedDiff += change.heightDiff;
|
|
690
|
+
// 更新当前卡片的 bottom
|
|
691
|
+
row.bottom += change.heightDiff;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// 更新列的总高度
|
|
696
|
+
if (accumulatedDiff !== 0) {
|
|
697
|
+
column.bottom += accumulatedDiff;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// 步骤4: 同步视图
|
|
702
|
+
this.setScrollHeight();
|
|
703
|
+
this.updateVisibleItems(true); // 这里会处理内容更新和DOM位置同步
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// 辅助方法:获取卡片的旧高度(从布局信息)
|
|
707
|
+
Waterfall.prototype.getCardOldHeight = function(dataId) {
|
|
708
|
+
const dataInfo = this.dataIdMap.get(dataId);
|
|
709
|
+
|
|
710
|
+
if (!dataInfo) {
|
|
711
|
+
// dataId 不存在
|
|
712
|
+
return 0;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
if (!dataInfo.layoutInfo) {
|
|
716
|
+
// 布局信息未初始化(卡片还未创建)
|
|
717
|
+
return 0;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const row = dataInfo.layoutInfo;
|
|
721
|
+
return row.bottom - row.top;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// 辅助方法:获取特殊节点的 DOM 引用
|
|
725
|
+
Waterfall.prototype.getSpecialNodeDOM = function(dataId) {
|
|
726
|
+
const dataInfo = this.dataIdMap.get(dataId);
|
|
727
|
+
if (dataInfo && dataInfo.layoutInfo && dataInfo.layoutInfo.$node) {
|
|
728
|
+
return dataInfo.layoutInfo.$node;
|
|
729
|
+
}
|
|
730
|
+
return null;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
// 辅助方法:批量获取卡片的新高度(用于 updateData)
|
|
734
|
+
Waterfall.prototype.getBatchCardNewHeights = async function(dataIds, newData) {
|
|
735
|
+
const options = this.options;
|
|
736
|
+
const $ = getEnv().$;
|
|
737
|
+
const $container = $(options.container);
|
|
738
|
+
const $viewport = $container.find('.waterfall-list-viewport');
|
|
739
|
+
|
|
740
|
+
// 用于记录每个卡片的信息
|
|
741
|
+
const cardInfos = new Map(); // dataId -> { $node, needCleanup, cleanupType }
|
|
742
|
+
|
|
743
|
+
// 记录从节点池借用的节点,需要在完成后归还
|
|
744
|
+
const borrowedNodes = [];
|
|
745
|
+
// 记录新创建的临时节点,需要在完成后删除
|
|
746
|
+
const tempNodesToDelete = [];
|
|
747
|
+
|
|
748
|
+
// 第一步:优先处理所有可视区域节点(activeNodes)
|
|
749
|
+
for (let dataId of dataIds) {
|
|
750
|
+
if (this.activeNodes.has(dataId)) {
|
|
751
|
+
const $node = this.activeNodes.get(dataId);
|
|
752
|
+
const data = newData[dataId];
|
|
753
|
+
|
|
754
|
+
// 重新渲染内容
|
|
755
|
+
this.updateRenderUI($node, data, dataId);
|
|
756
|
+
|
|
757
|
+
cardInfos.set(dataId, {
|
|
758
|
+
$node: $node,
|
|
759
|
+
needCleanup: false,
|
|
760
|
+
cleanupType: null
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// 第二步:处理所有非可视区域节点
|
|
766
|
+
for (let dataId of dataIds) {
|
|
767
|
+
// 跳过已在第一步处理的可视区域节点
|
|
768
|
+
if (this.activeNodes.has(dataId)) {
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const data = newData[dataId];
|
|
773
|
+
|
|
774
|
+
// 情况1: 卡片在 allReadyNodes 中
|
|
775
|
+
if (this.allReadyNodes.has(dataId)) {
|
|
776
|
+
const $existingNode = this.allReadyNodes.get(dataId);
|
|
777
|
+
|
|
778
|
+
// 特殊节点(null):通过 layoutInfo 获取 DOM 引用
|
|
779
|
+
if ($existingNode === null) {
|
|
780
|
+
const $specialNode = this.getSpecialNodeDOM(dataId);
|
|
781
|
+
|
|
782
|
+
if ($specialNode) {
|
|
783
|
+
// 特殊节点永远独占,无需检查重复
|
|
784
|
+
// 直接重新渲染特殊节点的内容
|
|
785
|
+
this.updateRenderUI($specialNode, data, dataId);
|
|
786
|
+
|
|
787
|
+
cardInfos.set(dataId, {
|
|
788
|
+
$node: $specialNode,
|
|
789
|
+
needCleanup: false,
|
|
790
|
+
cleanupType: 'special'
|
|
791
|
+
});
|
|
792
|
+
} else {
|
|
793
|
+
// 无法获取特殊节点DOM,使用旧高度
|
|
794
|
+
console.warn('Waterfall: Special node DOM not found for dataId', dataId);
|
|
795
|
+
cardInfos.set(dataId, {
|
|
796
|
+
$node: null,
|
|
797
|
+
needCleanup: false,
|
|
798
|
+
cleanupType: 'special-notfound'
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
// 情况2: 普通节点 - 从节点池获取或创建临时节点
|
|
805
|
+
let $node = getNodePoolPop(this.nodePool, this.activeNodes);
|
|
806
|
+
|
|
807
|
+
if ($node) {
|
|
808
|
+
// 从节点池借用节点
|
|
809
|
+
borrowedNodes.push($node);
|
|
810
|
+
|
|
811
|
+
// 设置位置和样式
|
|
812
|
+
$node.css({
|
|
813
|
+
'transform': 'translate(-9999px, -9999px)',
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// 设置宽度
|
|
817
|
+
if (options.columns !== 1) {
|
|
818
|
+
$node.width(this.columnWidth + 'px');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// 使用 updateRenderUI(节点池的节点已有结构)
|
|
822
|
+
this.updateRenderUI($node, data, dataId);
|
|
823
|
+
|
|
824
|
+
cardInfos.set(dataId, {
|
|
825
|
+
$node: $node,
|
|
826
|
+
needCleanup: false,
|
|
827
|
+
cleanupType: 'borrowed'
|
|
828
|
+
});
|
|
829
|
+
} else {
|
|
830
|
+
// 节点池空了,创建临时节点
|
|
831
|
+
$node = $(
|
|
832
|
+
`<div class="waterfall-item"
|
|
833
|
+
data-index="${dataId}"
|
|
834
|
+
style="position: absolute; transform: translate(-9999px, -9999px);"
|
|
835
|
+
>
|
|
836
|
+
</div>`
|
|
837
|
+
);
|
|
838
|
+
$viewport.append($node);
|
|
839
|
+
tempNodesToDelete.push($node);
|
|
840
|
+
|
|
841
|
+
// 设置宽度
|
|
842
|
+
if (options.columns !== 1) {
|
|
843
|
+
$node.width(this.columnWidth + 'px');
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// 使用 renderUI(临时节点是全新的)
|
|
847
|
+
this.renderUI($node, data, dataId);
|
|
848
|
+
|
|
849
|
+
cardInfos.set(dataId, {
|
|
850
|
+
$node: $node,
|
|
851
|
+
needCleanup: false,
|
|
852
|
+
cleanupType: 'temp'
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
// 情况3: 未找到节点,标记为使用旧高度
|
|
859
|
+
cardInfos.set(dataId, {
|
|
860
|
+
$node: null,
|
|
861
|
+
needCleanup: false,
|
|
862
|
+
cleanupType: 'notfound'
|
|
625
863
|
});
|
|
864
|
+
}
|
|
626
865
|
|
|
627
|
-
|
|
866
|
+
// 第三步:在单个 requestAnimationFrame 中统一获取所有高度
|
|
867
|
+
return new Promise(resolve => {
|
|
868
|
+
window.requestAnimationFrame(() => {
|
|
869
|
+
const heightMap = new Map(); // dataId -> newHeight
|
|
628
870
|
|
|
629
|
-
|
|
871
|
+
for (let [dataId, info] of cardInfos) {
|
|
872
|
+
if (info.$node) {
|
|
873
|
+
// 从DOM获取高度
|
|
874
|
+
heightMap.set(dataId, info.$node.height());
|
|
875
|
+
} else {
|
|
876
|
+
// 使用旧高度
|
|
877
|
+
heightMap.set(dataId, this.getCardOldHeight(dataId));
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
// 清理:归还借用的节点到节点池
|
|
882
|
+
for (let $node of borrowedNodes) {
|
|
883
|
+
$node.css('transform', 'translateY(-9999px)');
|
|
884
|
+
this.nodePool.push($node);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// 清理:删除临时节点
|
|
888
|
+
for (let $node of tempNodesToDelete) {
|
|
889
|
+
$node.remove();
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
resolve(heightMap);
|
|
893
|
+
});
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// 辅助方法:获取卡片的新高度(通过渲染)- 用于单卡片更新
|
|
898
|
+
Waterfall.prototype.getCardNewHeight = async function(dataId, data) {
|
|
899
|
+
const options = this.options;
|
|
900
|
+
const $ = getEnv().$;
|
|
630
901
|
|
|
902
|
+
// 情况1: 卡片在可视区域(activeNodes)
|
|
903
|
+
if (this.activeNodes.has(dataId)) {
|
|
904
|
+
const $node = this.activeNodes.get(dataId);
|
|
905
|
+
|
|
906
|
+
// 重新渲染内容
|
|
907
|
+
this.updateRenderUI($node, data, dataId);
|
|
908
|
+
|
|
909
|
+
// 等待DOM更新
|
|
910
|
+
return new Promise(resolve => {
|
|
911
|
+
window.requestAnimationFrame(() => {
|
|
912
|
+
resolve($node.height());
|
|
913
|
+
});
|
|
914
|
+
});
|
|
631
915
|
}
|
|
632
|
-
// options.data.forEach((item, index) => {
|
|
633
|
-
// const dataId = index; // this.nextDataId++;
|
|
634
|
-
// if (!this.allReadyNodes.has(dataId)) {
|
|
635
|
-
// this.dataIdMap.set(dataId, {
|
|
636
|
-
// data: true,// item,
|
|
637
|
-
// originalIndex: index,
|
|
638
|
-
// layoutInfo: null // 将在布局时填充
|
|
639
|
-
// });
|
|
640
|
-
// // 如果没有准备好这个数据,这里要创建一个占位节点
|
|
641
|
-
// this.createCards({ end: 0, dataId });
|
|
642
|
-
// }
|
|
643
|
-
// });
|
|
644
916
|
|
|
645
|
-
//
|
|
917
|
+
// 情况2: 卡片不在可视区域(allReadyNodes)
|
|
918
|
+
if (this.allReadyNodes.has(dataId)) {
|
|
919
|
+
const $existingNode = this.allReadyNodes.get(dataId);
|
|
920
|
+
|
|
921
|
+
// 特殊节点(null):通过 layoutInfo 获取 DOM 引用并更新
|
|
922
|
+
if ($existingNode === null) {
|
|
923
|
+
const $specialNode = this.getSpecialNodeDOM(dataId);
|
|
924
|
+
|
|
925
|
+
if ($specialNode) {
|
|
926
|
+
// 重新渲染特殊节点的内容
|
|
927
|
+
this.updateRenderUI($specialNode, data, dataId);
|
|
928
|
+
|
|
929
|
+
// 等待DOM更新后获取新高度
|
|
930
|
+
return new Promise(resolve => {
|
|
931
|
+
window.requestAnimationFrame(() => {
|
|
932
|
+
resolve($specialNode.height());
|
|
933
|
+
});
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// 无法获取特殊节点DOM,返回旧高度
|
|
938
|
+
console.warn('Waterfall: Special node DOM not found for dataId', dataId);
|
|
939
|
+
return this.getCardOldHeight(dataId);
|
|
940
|
+
}
|
|
646
941
|
|
|
942
|
+
// 普通节点:从节点池获取或创建临时节点
|
|
943
|
+
const $container = $(options.container);
|
|
944
|
+
const $viewport = $container.find('.waterfall-list-viewport');
|
|
647
945
|
|
|
946
|
+
let $node = getNodePoolPop(this.nodePool, this.activeNodes);
|
|
947
|
+
let isBorrowed = false;
|
|
948
|
+
|
|
949
|
+
if ($node) {
|
|
950
|
+
// 从节点池借用节点
|
|
951
|
+
isBorrowed = true;
|
|
952
|
+
|
|
953
|
+
// 设置位置和样式
|
|
954
|
+
$node.css({
|
|
955
|
+
'transform': 'translate(-9999px, -9999px)',
|
|
956
|
+
});
|
|
957
|
+
|
|
958
|
+
// 设置宽度
|
|
959
|
+
if (options.columns !== 1) {
|
|
960
|
+
$node.width(this.columnWidth + 'px');
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// 使用 updateRenderUI(节点池的节点已有结构)
|
|
964
|
+
this.updateRenderUI($node, data, dataId);
|
|
965
|
+
} else {
|
|
966
|
+
// 节点池空了,创建临时节点
|
|
967
|
+
$node = $(
|
|
968
|
+
`<div class="waterfall-item"
|
|
969
|
+
data-index="${dataId}"
|
|
970
|
+
style="position: absolute; transform: translate(-9999px, -9999px);"
|
|
971
|
+
>
|
|
972
|
+
</div>`
|
|
973
|
+
);
|
|
974
|
+
$viewport.append($node);
|
|
975
|
+
|
|
976
|
+
// 设置宽度
|
|
977
|
+
if (options.columns !== 1) {
|
|
978
|
+
$node.width(this.columnWidth + 'px');
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// 使用 renderUI(临时节点是全新的)
|
|
982
|
+
this.renderUI($node, data, dataId);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// 等待DOM更新
|
|
986
|
+
return new Promise(resolve => {
|
|
987
|
+
window.requestAnimationFrame(() => {
|
|
988
|
+
const newHeight = $node.height();
|
|
989
|
+
|
|
990
|
+
// 清理
|
|
991
|
+
if (isBorrowed) {
|
|
992
|
+
// 归还借用的节点到节点池
|
|
993
|
+
$node.css('transform', 'translateY(-9999px)');
|
|
994
|
+
this.nodePool.push($node);
|
|
995
|
+
} else {
|
|
996
|
+
// 删除临时节点
|
|
997
|
+
$node.remove();
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
resolve(newHeight);
|
|
1001
|
+
});
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
// 情况3: 未找到节点,返回旧高度
|
|
1006
|
+
return this.getCardOldHeight(dataId);
|
|
648
1007
|
}
|
|
649
1008
|
|
|
650
1009
|
// 某个数据进行了UI变更,触发高度重新绘制
|
|
651
|
-
Waterfall.prototype.updateCard = function (data) {
|
|
1010
|
+
Waterfall.prototype.updateCard = async function (data) {
|
|
1011
|
+
const self = this;
|
|
652
1012
|
const options = this.options;
|
|
653
1013
|
let dataId = -1;
|
|
654
1014
|
if (typeof data === 'number') {
|
|
@@ -667,49 +1027,75 @@ Waterfall.prototype.updateCard = function (data) {
|
|
|
667
1027
|
return
|
|
668
1028
|
}
|
|
669
1029
|
|
|
670
|
-
//
|
|
671
|
-
if (!this.
|
|
672
|
-
console.log('Waterfall:
|
|
1030
|
+
// 检查数据是否已经渲染过
|
|
1031
|
+
if (!this.renderedDataIds.has(dataId)) {
|
|
1032
|
+
console.log('Waterfall: dataId not rendered yet', dataId);
|
|
673
1033
|
return
|
|
674
1034
|
}
|
|
675
1035
|
|
|
676
|
-
const
|
|
677
|
-
|
|
1036
|
+
const itemData = options.data[dataId];
|
|
1037
|
+
|
|
1038
|
+
// 获取旧高度
|
|
1039
|
+
const oldHeight = this.getCardOldHeight(dataId);
|
|
1040
|
+
|
|
1041
|
+
// 获取新高度(会自动处理渲染和临时节点)
|
|
1042
|
+
const newHeight = await this.getCardNewHeight(dataId, itemData);
|
|
1043
|
+
|
|
1044
|
+
// 应用高度变化
|
|
1045
|
+
this.applyHeightChange(dataId, oldHeight, newHeight);
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// 应用高度变化到布局
|
|
1049
|
+
Waterfall.prototype.applyHeightChange = function (dataId, oldHeight, newHeight) {
|
|
1050
|
+
const minus = newHeight - oldHeight;
|
|
1051
|
+
|
|
1052
|
+
// 如果高度没有变化,直接退出
|
|
1053
|
+
if (minus === 0) {
|
|
1054
|
+
console.log('Waterfall: Card height unchanged for dataId', dataId);
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
console.log('Waterfall: Card height changed for dataId', dataId, 'from', oldHeight, 'to', newHeight, 'diff', minus);
|
|
678
1059
|
|
|
679
1060
|
// 重新计算该数据所在列的卡片位置
|
|
680
|
-
let needUpdate = false
|
|
1061
|
+
let needUpdate = false;
|
|
681
1062
|
const columnItems = this.columnItems;
|
|
1063
|
+
|
|
682
1064
|
for (let i = 0; i < columnItems.length; i++) {
|
|
683
1065
|
const column = columnItems[i];
|
|
684
|
-
let
|
|
685
|
-
|
|
1066
|
+
let foundCard = false;
|
|
1067
|
+
|
|
686
1068
|
// 这里为了简化,各列的瀑布流保持不动,只更新各列下节点的top位置,不做节点的跨列位移
|
|
687
1069
|
for (let j = 0; j < column.children.length; j++) {
|
|
688
1070
|
const row = column.children[j];
|
|
689
|
-
if (row.dataId === dataId) {
|
|
690
|
-
bool = true
|
|
691
|
-
const oldHeight = row.bottom - row.top;
|
|
692
1071
|
|
|
693
|
-
|
|
1072
|
+
if (row.dataId === dataId) {
|
|
1073
|
+
foundCard = true;
|
|
1074
|
+
needUpdate = true;
|
|
694
1075
|
|
|
695
|
-
|
|
696
|
-
// 找到了原节点数据,对比后没有变更 那么直接退出
|
|
697
|
-
return;
|
|
698
|
-
}
|
|
699
|
-
needUpdate = true
|
|
1076
|
+
// 更新当前卡片的bottom
|
|
700
1077
|
row.bottom += minus;
|
|
701
|
-
|
|
1078
|
+
|
|
1079
|
+
} else if (foundCard) {
|
|
1080
|
+
// 更新该卡片以下所有卡片的位置(同列)
|
|
702
1081
|
row.top += minus;
|
|
703
1082
|
row.bottom += minus;
|
|
704
1083
|
}
|
|
705
1084
|
}
|
|
706
|
-
|
|
1085
|
+
|
|
1086
|
+
if (foundCard) {
|
|
1087
|
+
// 更新列的总高度
|
|
707
1088
|
column.bottom += minus;
|
|
708
1089
|
break;
|
|
709
1090
|
}
|
|
710
1091
|
}
|
|
1092
|
+
|
|
711
1093
|
if (needUpdate) {
|
|
712
|
-
|
|
1094
|
+
// 更新滚动容器高度
|
|
1095
|
+
this.setScrollHeight();
|
|
1096
|
+
|
|
1097
|
+
// 强制更新渲染(更新所有卡片的实际位置)
|
|
1098
|
+
this.updateVisibleItems(true);
|
|
713
1099
|
}
|
|
714
1100
|
}
|
|
715
1101
|
|
|
@@ -761,11 +1147,11 @@ Waterfall.prototype.showLoading = function (callback = null) {
|
|
|
761
1147
|
let loadingTop = this.getMaxHeight() + options.rowGap
|
|
762
1148
|
this.$loadingNode.css('transform', `translate(0px,${loadingTop}px)`);
|
|
763
1149
|
$node = this.$loadingNode;
|
|
764
|
-
window.requestAnimationFrame(() => {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
});
|
|
1150
|
+
// window.requestAnimationFrame(() => {
|
|
1151
|
+
// setTimeout(() => {
|
|
1152
|
+
// this.$scrollDom.scrollTop(loadingTop + this.$loadingNode.height());
|
|
1153
|
+
// });
|
|
1154
|
+
// });
|
|
769
1155
|
}
|
|
770
1156
|
|
|
771
1157
|
if (callback) callback($node)
|
|
@@ -866,4 +1252,3 @@ function getNodePoolPop(nodePool, actives) {
|
|
|
866
1252
|
}
|
|
867
1253
|
return null;
|
|
868
1254
|
}
|
|
869
|
-
|
package/package.json
CHANGED