@dolphinweex/weex-harmony 0.1.27 → 0.1.28
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.
@@ -4,106 +4,113 @@
|
|
4
4
|
:class="{ editMode: data.isEditing }"
|
5
5
|
:style="gridItemStyle"
|
6
6
|
>
|
7
|
-
<div class="
|
8
|
-
<
|
9
|
-
<div
|
10
|
-
v-for="(item, index) in processedList"
|
11
|
-
:key="item.itemView.id"
|
12
|
-
class="grid-item"
|
13
|
-
:class="{
|
14
|
-
'being-dragged': draggingIndex === index,
|
15
|
-
hidden: isDragClone && draggingIndex === index,
|
16
|
-
'hover-target': hoverIndex === index && draggingIndex !== index,
|
17
|
-
'disabled-item': item.itemView.enable === false,
|
18
|
-
}"
|
19
|
-
:style="{
|
20
|
-
overflow: data.isEditing ? 'visible' : 'hidden',
|
21
|
-
}"
|
22
|
-
@touchstart="(event) => startLongPress(event, index)"
|
23
|
-
@touchmove="checkLongPressMove"
|
24
|
-
@touchend="endTouch"
|
25
|
-
@touchcancel="endTouch"
|
26
|
-
:data-index="index"
|
27
|
-
>
|
28
|
-
<div
|
29
|
-
v-if="item.bgAnimView && item.bgAnimView.animUrl && !data.isEditing"
|
30
|
-
class="lottie-container bg-anim"
|
31
|
-
:style="applyNormalAttribute(item.bgAnimView)"
|
32
|
-
:ref="`bgAnim-${item.itemView.id}`"
|
33
|
-
></div>
|
34
|
-
<div
|
35
|
-
v-if="
|
36
|
-
item.frontAnimView &&
|
37
|
-
item.frontAnimView.animUrl &&
|
38
|
-
!data.isEditing
|
39
|
-
"
|
40
|
-
class="lottie-container front-anim"
|
41
|
-
:style="applyNormalAttribute(item.frontAnimView)"
|
42
|
-
:ref="`frontAnim-${item.itemView.id}`"
|
43
|
-
></div>
|
7
|
+
<div class="items-container">
|
8
|
+
<div class="grid-items-wrapper">
|
9
|
+
<transition-group name="grid-fade" tag="div" class="grid-inner-wrapper">
|
44
10
|
<div
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
11
|
+
v-for="(item, index) in processedList"
|
12
|
+
:key="item.itemView.id"
|
13
|
+
class="grid-item"
|
14
|
+
:class="{
|
15
|
+
'being-dragged': draggingIndex === index,
|
16
|
+
hidden: isDragClone && draggingIndex === index,
|
17
|
+
'hover-target': hoverIndex === index && draggingIndex !== index,
|
18
|
+
'disabled-item': item.itemView.enable === false,
|
19
|
+
}"
|
20
|
+
:style="{
|
21
|
+
overflow: data.isEditing ? 'visible' : 'hidden',
|
22
|
+
}"
|
23
|
+
:data-index="index"
|
24
|
+
@touchstart="(event) => startLongPress(event, index)"
|
25
|
+
@touchmove="checkLongPressMove"
|
26
|
+
@touchend="endTouch"
|
27
|
+
@touchcancel="endTouch"
|
50
28
|
>
|
51
|
-
<div
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
29
|
+
<div
|
30
|
+
v-if="
|
31
|
+
item.bgAnimView && item.bgAnimView.animUrl && !data.isEditing
|
32
|
+
"
|
33
|
+
class="lottie-container bg-anim"
|
34
|
+
:style="applyNormalAttribute(item.bgAnimView)"
|
35
|
+
:ref="`bgAnim-${item.itemView.id}`"
|
36
|
+
></div>
|
37
|
+
<div
|
38
|
+
v-if="
|
39
|
+
item.frontAnimView &&
|
40
|
+
item.frontAnimView.animUrl &&
|
41
|
+
!data.isEditing
|
42
|
+
"
|
43
|
+
class="lottie-container front-anim"
|
44
|
+
:style="applyNormalAttribute(item.frontAnimView)"
|
45
|
+
:ref="`frontAnim-${item.itemView.id}`"
|
46
|
+
></div>
|
47
|
+
<div
|
48
|
+
:style="applyNormalAttribute(item.itemView)"
|
49
|
+
class="grid-box"
|
50
|
+
:class="{ shaking: data.isEditing }"
|
51
|
+
@click.stop="(e) => itemViewClick(e, item, index)"
|
52
|
+
>
|
53
|
+
<div class="flexBox">
|
54
|
+
<img
|
55
|
+
:src="item.iconImage.url"
|
56
|
+
:style="applyNormalAttribute(item.iconImage)"
|
57
|
+
/>
|
58
|
+
<div>
|
59
|
+
<div @click.stop.prevent v-if="!data.isEditing">
|
60
|
+
<button
|
61
|
+
class="actionButton"
|
62
|
+
@click.stop.prevent="(e) => actionClick(e, item, index)"
|
63
|
+
:style="applyNormalAttribute(item.actionButton)"
|
64
|
+
>
|
65
|
+
{{ item.actionButton.text }}
|
66
|
+
</button>
|
67
|
+
<div
|
68
|
+
v-if="
|
69
|
+
item.loadingAnimView && item.loadingAnimView.animUrl
|
70
|
+
"
|
71
|
+
class="loading-anim"
|
72
|
+
:style="applyNormalAttribute(item.loadingAnimView)"
|
73
|
+
:ref="`loadingAnim-${item.itemView.id}`"
|
74
|
+
></div>
|
75
|
+
</div>
|
76
|
+
<div @click.stop.prevent v-if="!data.isEditing">
|
77
|
+
<img
|
78
|
+
:src="item.rightIcon.url"
|
79
|
+
:style="applyNormalAttribute(item.rightIcon)"
|
80
|
+
@click.stop.prevent="(e) => rightIconClick(e)"
|
81
|
+
/>
|
82
|
+
</div>
|
83
|
+
<div @click.stop.prevent v-if="!data.isEditing">
|
84
|
+
<img
|
85
|
+
class="leftTopIcon"
|
86
|
+
:src="item.leftTopIcon.url"
|
87
|
+
:style="applyNormalAttribute(item.leftTopIcon)"
|
88
|
+
@click.stop.prevent
|
89
|
+
/>
|
90
|
+
</div>
|
91
|
+
<div @click.stop.prevent v-if="data.isEditing">
|
92
|
+
<img
|
93
|
+
class="isSelected"
|
94
|
+
@click.stop.prevent="
|
95
|
+
(e) => selectIconClick(e, item, index)
|
96
|
+
"
|
97
|
+
:src="
|
98
|
+
item.itemView.isSelected
|
99
|
+
? item.selectIcon.selectedUrl
|
100
|
+
: item.selectIcon.url
|
101
|
+
"
|
102
|
+
:style="applyNormalAttribute(item.selectIcon)"
|
103
|
+
/>
|
104
|
+
</div>
|
98
105
|
</div>
|
99
106
|
</div>
|
100
|
-
|
101
|
-
|
102
|
-
|
107
|
+
<div class="itemText" :style="[applyNormalAttribute(item.text)]">
|
108
|
+
{{ item.text.text }}
|
109
|
+
</div>
|
103
110
|
</div>
|
104
111
|
</div>
|
105
|
-
</
|
106
|
-
</
|
112
|
+
</transition-group>
|
113
|
+
</div>
|
107
114
|
</div>
|
108
115
|
|
109
116
|
<!-- 拖拽克隆元素 -->
|
@@ -169,7 +176,7 @@ export default {
|
|
169
176
|
processedList: [],
|
170
177
|
selected: null,
|
171
178
|
longPressTimer: null,
|
172
|
-
longPressDuration:
|
179
|
+
longPressDuration: 350,
|
173
180
|
longPressStartIndex: null,
|
174
181
|
draggingIndex: null,
|
175
182
|
hoverIndex: null,
|
@@ -178,7 +185,6 @@ export default {
|
|
178
185
|
isAnimating: false,
|
179
186
|
touchStartX: 0,
|
180
187
|
touchStartY: 0,
|
181
|
-
touchStartTime: 0, // 记录触摸开始时间,用于计算滑动速度
|
182
188
|
draggedItemRect: null,
|
183
189
|
isDragClone: false,
|
184
190
|
dragCloneX: 0,
|
@@ -188,17 +194,13 @@ export default {
|
|
188
194
|
gridBoxWidth: 0,
|
189
195
|
lottieAnimations: {},
|
190
196
|
isScrolling: false,
|
191
|
-
scrollThreshold:
|
197
|
+
scrollThreshold: 5,
|
192
198
|
fixedItemWidth: 0,
|
193
199
|
fixedItemHeight: 0,
|
194
200
|
animationFrameId: null,
|
195
201
|
lastBestTarget: null,
|
196
202
|
targetChangeTime: null,
|
197
203
|
pendingTarget: null,
|
198
|
-
weexListInnerEl: null,
|
199
|
-
weexRefreshEl: null,
|
200
|
-
originListInnerStyle: null,
|
201
|
-
originRefreshStyle: null,
|
202
204
|
};
|
203
205
|
},
|
204
206
|
computed: {
|
@@ -206,9 +208,6 @@ export default {
|
|
206
208
|
return {
|
207
209
|
'--grid-cols': this.data.spanCount || this.cols,
|
208
210
|
'--grid-gap': `${this.data.spaceSize || 8}px`,
|
209
|
-
'--grid-margin': `${
|
210
|
-
this.data.layoutConfig.marginStart + this.data.layoutConfig.marginEnd
|
211
|
-
}px`,
|
212
211
|
marginTop: this.data.layoutConfig.marginTop * scla,
|
213
212
|
marginBottom: this.data.layoutConfig.marginBottom * scla,
|
214
213
|
marginLeft: this.data.layoutConfig.marginStart * scla,
|
@@ -252,14 +251,18 @@ export default {
|
|
252
251
|
const dataIndex = this.data.list.findIndex(
|
253
252
|
(item) => item.itemView.id == id
|
254
253
|
);
|
255
|
-
|
256
|
-
if(dataIndex !== -1) {
|
257
|
-
// 使用Vue的$set确保响应式更新
|
258
|
-
this.$set(this.data.list, dataIndex, JSON.parse(JSON.stringify(data)));
|
259
|
-
}
|
254
|
+
|
260
255
|
if (index === -1) {
|
261
256
|
return;
|
262
257
|
}
|
258
|
+
|
259
|
+
// 检查是否有动画URL的变化
|
260
|
+
const oldBgAnimUrl = this.processedList[index].bgAnimView.animUrl || '';
|
261
|
+
const oldLoadingAnimUrl =
|
262
|
+
this.processedList[index].loadingAnimView.animUrl || '';
|
263
|
+
const oldFrontAnimUrl =
|
264
|
+
this.processedList[index].frontAnimView.animUrl || '';
|
265
|
+
|
263
266
|
const newBgAnimUrl = data.bgAnimView.animUrl || '';
|
264
267
|
const newLoadingAnimUrl = data.loadingAnimView.animUrl || '';
|
265
268
|
const newFrontAnimUrl = data.frontAnimView.animUrl || '';
|
@@ -267,40 +270,72 @@ export default {
|
|
267
270
|
this.processedList[index] = data;
|
268
271
|
this.processedList = [...this.processedList];
|
269
272
|
|
273
|
+
// 更新原始数据以确保watch能触发
|
274
|
+
if (dataIndex !== -1) {
|
275
|
+
// 使用Vue的$set确保响应式更新
|
276
|
+
this.$set(this.data.list, dataIndex, JSON.parse(JSON.stringify(data)));
|
277
|
+
}
|
270
278
|
|
271
|
-
//
|
272
|
-
|
279
|
+
// 如果动画URL有变化,重新初始化相关的Lottie动画
|
280
|
+
console.log('检测到动画URL变化,重新初始化动画');
|
281
|
+
|
282
|
+
// 等待下一帧DOM更新完成后再操作
|
273
283
|
this.$nextTick(() => {
|
274
284
|
// 清理旧的动画实例
|
275
285
|
const itemId = String(data.itemView.id);
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
)
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
)
|
303
|
-
|
286
|
+
|
287
|
+
// 清除所有相关的动画实例
|
288
|
+
const animKeys = [
|
289
|
+
`bg-${itemId}`,
|
290
|
+
`loading-${itemId}`,
|
291
|
+
`front-${itemId}`,
|
292
|
+
];
|
293
|
+
animKeys.forEach((key) => {
|
294
|
+
if (this.lottieAnimations[key]) {
|
295
|
+
console.log(`销毁动画实例: ${key}`);
|
296
|
+
const anim = this.lottieAnimations[key];
|
297
|
+
try {
|
298
|
+
anim.removeEventListener('complete');
|
299
|
+
} catch (e) {
|
300
|
+
console.warn(`移除事件监听器失败: ${e.message}`);
|
301
|
+
}
|
302
|
+
anim.destroy();
|
303
|
+
delete this.lottieAnimations[key];
|
304
|
+
}
|
305
|
+
});
|
306
|
+
|
307
|
+
console.log('清理完成,开始重新初始化动画');
|
308
|
+
|
309
|
+
// 延迟一点时间确保DOM完全更新
|
310
|
+
setTimeout(() => {
|
311
|
+
// 重新初始化背景动画
|
312
|
+
if (oldBgAnimUrl !== newBgAnimUrl) {
|
313
|
+
this.loadLottieAnimation(
|
314
|
+
data.bgAnimView,
|
315
|
+
`bgAnim-${itemId}`,
|
316
|
+
`bg-${itemId}`,
|
317
|
+
data
|
318
|
+
);
|
319
|
+
}
|
320
|
+
|
321
|
+
if (oldLoadingAnimUrl !== newLoadingAnimUrl) {
|
322
|
+
this.loadLottieAnimation(
|
323
|
+
data.loadingAnimView,
|
324
|
+
`loadingAnim-${itemId}`,
|
325
|
+
`loading-${itemId}`,
|
326
|
+
data
|
327
|
+
);
|
328
|
+
}
|
329
|
+
|
330
|
+
if (oldFrontAnimUrl !== newFrontAnimUrl) {
|
331
|
+
this.loadLottieAnimation(
|
332
|
+
data.frontAnimView,
|
333
|
+
`frontAnim-${itemId}`,
|
334
|
+
`front-${itemId}`,
|
335
|
+
data
|
336
|
+
);
|
337
|
+
}
|
338
|
+
}, 100); // 增加延迟确保DOM已更新和旧动画已完全销毁
|
304
339
|
});
|
305
340
|
},
|
306
341
|
getListData(callback) {
|
@@ -315,6 +350,7 @@ export default {
|
|
315
350
|
callback({ listData: data });
|
316
351
|
},
|
317
352
|
rightIconClick(e) {
|
353
|
+
console.log('rightIconClick执行,阻止事件冒泡');
|
318
354
|
e.preventDefault();
|
319
355
|
e.stopPropagation();
|
320
356
|
this.$emit('onClickRightIcon', e);
|
@@ -329,7 +365,9 @@ export default {
|
|
329
365
|
selectIconClick(e, item, index) {
|
330
366
|
e.preventDefault();
|
331
367
|
e.stopPropagation(); // 阻止事件冒泡到itemViewClick
|
332
|
-
item.
|
368
|
+
if (item.selectIcon.visibility) {
|
369
|
+
item.itemView.isSelected = !item.itemView.isSelected;
|
370
|
+
}
|
333
371
|
const target = {
|
334
372
|
...item.itemView,
|
335
373
|
id: String(item.itemView.id),
|
@@ -346,27 +384,37 @@ export default {
|
|
346
384
|
if (e.defaultPrevented) {
|
347
385
|
return;
|
348
386
|
}
|
349
|
-
e.preventDefault();
|
350
|
-
e.stopPropagation();
|
351
|
-
// 确保取消任何长按计时器
|
352
|
-
this.cancelLongPress();
|
353
|
-
if (this.data.isEditing) {
|
354
|
-
item.itemView.isSelected = !item.itemView.isSelected;
|
355
|
-
this.$emit('onSelectItems', target);
|
356
|
-
return;
|
357
|
-
}
|
358
387
|
const target = {
|
359
388
|
...item.itemView,
|
360
389
|
id: String(item.itemView.id),
|
361
390
|
index,
|
362
391
|
...e,
|
363
392
|
};
|
393
|
+
console.log('itemViewClick');
|
394
|
+
// 确保取消任何长按计时器
|
395
|
+
this.cancelLongPress();
|
396
|
+
if (this.data.isEditing) {
|
397
|
+
if (item.selectIcon.visibility) {
|
398
|
+
item.itemView.isSelected = !item.itemView.isSelected;
|
399
|
+
}
|
400
|
+
return;
|
401
|
+
}
|
402
|
+
e.preventDefault();
|
403
|
+
e.stopPropagation();
|
404
|
+
|
364
405
|
this.$emit('onClickItem', target);
|
406
|
+
this.data.isEditing&&item.selectIcon.visibility&&this.$emit('onSelectItems', target);
|
407
|
+
|
365
408
|
},
|
366
409
|
|
367
410
|
actionClick(e, item, index) {
|
411
|
+
console.log('actionClick执行,阻止事件冒泡');
|
368
412
|
e.preventDefault();
|
369
413
|
e.stopPropagation(); // 阻止事件冒泡到itemViewClick
|
414
|
+
|
415
|
+
// 在触发事件前,先清理所有其他项目的动画实例
|
416
|
+
this.cleanupOtherAnimations(item.itemView.id);
|
417
|
+
|
370
418
|
const target = {
|
371
419
|
...item.itemView,
|
372
420
|
id: String(item.itemView.id),
|
@@ -377,6 +425,58 @@ export default {
|
|
377
425
|
return false;
|
378
426
|
},
|
379
427
|
|
428
|
+
// 新增方法:清理除指定项目外的所有动画实例
|
429
|
+
cleanupOtherAnimations(currentItemId) {
|
430
|
+
console.log('清理其他项目的动画实例,当前项目ID:', currentItemId);
|
431
|
+
|
432
|
+
Object.keys(this.lottieAnimations).forEach((key) => {
|
433
|
+
if (!key.includes(`-${currentItemId}`)) {
|
434
|
+
if (this.lottieAnimations[key]) {
|
435
|
+
console.log(`销毁其他项目的动画实例: ${key}`);
|
436
|
+
const anim = this.lottieAnimations[key];
|
437
|
+
|
438
|
+
// 不要立即销毁,而是先停止动画
|
439
|
+
anim.pause();
|
440
|
+
|
441
|
+
// 使用 setTimeout 延迟销毁,给新动画一个启动的时间
|
442
|
+
setTimeout(() => {
|
443
|
+
try {
|
444
|
+
if (this.lottieAnimations[key] === anim) {
|
445
|
+
anim.removeEventListener('complete');
|
446
|
+
anim.destroy();
|
447
|
+
delete this.lottieAnimations[key];
|
448
|
+
|
449
|
+
// 提取项目ID和动画类型
|
450
|
+
const parts = key.split('-');
|
451
|
+
if (parts.length === 2) {
|
452
|
+
const animType = parts[0];
|
453
|
+
const itemId = parts[1];
|
454
|
+
|
455
|
+
// 延迟隐藏对应的动画容器
|
456
|
+
this.$nextTick(() => {
|
457
|
+
const refName = `${animType}Anim-${itemId}`;
|
458
|
+
const animContainer =
|
459
|
+
this.$refs[refName] && this.$refs[refName][0];
|
460
|
+
if (animContainer) {
|
461
|
+
// 使用 opacity 过渡而不是直接设置 display: none
|
462
|
+
animContainer.style.opacity = '0';
|
463
|
+
animContainer.style.transition = 'opacity 0.2s';
|
464
|
+
setTimeout(() => {
|
465
|
+
animContainer.style.display = 'none';
|
466
|
+
}, 200);
|
467
|
+
}
|
468
|
+
});
|
469
|
+
}
|
470
|
+
}
|
471
|
+
} catch (e) {
|
472
|
+
console.warn(`清理动画失败: ${e.message}`);
|
473
|
+
}
|
474
|
+
}, 50);
|
475
|
+
}
|
476
|
+
}
|
477
|
+
});
|
478
|
+
},
|
479
|
+
|
380
480
|
// 生成处理后的数据列表
|
381
481
|
generateDataList() {
|
382
482
|
if (!this.data || !this.data.list || !this.data.globalData) {
|
@@ -405,12 +505,12 @@ export default {
|
|
405
505
|
// 使用mergeDate合并全局数据和项目数据
|
406
506
|
processedItem[type] = this.mergeDate(item[type] || {}, type);
|
407
507
|
});
|
408
|
-
|
409
508
|
return processedItem;
|
410
509
|
});
|
411
510
|
},
|
412
511
|
getItemStyle(item, type) {
|
413
512
|
const result = this.mergeDate(item[type] || {}, type);
|
513
|
+
console.log(type, this.applyNormalAttribute(result), 'ggg');
|
414
514
|
return this.applyNormalAttribute(result);
|
415
515
|
},
|
416
516
|
mergeDate(target, type) {
|
@@ -476,44 +576,43 @@ export default {
|
|
476
576
|
},
|
477
577
|
// 开始长按
|
478
578
|
startLongPress(e, index) {
|
479
|
-
console.log(e, 'cdj-------startLongPress');
|
480
579
|
if (!this.data.isEditable) {
|
481
580
|
return;
|
482
581
|
}
|
483
|
-
|
484
|
-
|
485
|
-
//
|
486
|
-
|
582
|
+
this.$emit('onDragEventStart', e);
|
583
|
+
|
584
|
+
// 只在编辑模式下阻止事件冒泡和默认行为
|
585
|
+
if (this.data.isEditing) {
|
586
|
+
if (e.oriEvent) {
|
587
|
+
// e.oriEvent.preventDefault();
|
588
|
+
// e.oriEvent.stopPropagation();
|
589
|
+
// e.oriEvent.stopImmediatePropagation();
|
590
|
+
}
|
591
|
+
// e.preventDefault && e.preventDefault();
|
592
|
+
// e.stopPropagation && e.stopPropagation();
|
593
|
+
// e.stopImmediatePropagation && e.stopImmediatePropagation();
|
594
|
+
}
|
595
|
+
|
487
596
|
// 清除任何现有计时器
|
488
597
|
this.cancelLongPress();
|
489
|
-
|
490
598
|
// 记录起始触摸位置和索引
|
491
599
|
const touch = e.changedTouches[0];
|
492
600
|
this.touchStartX = touch.pageX;
|
493
601
|
this.touchStartY = touch.pageY;
|
602
|
+
console.log('开始长按ddddddddd', touch, touch.pageY, touch.pageX);
|
494
603
|
this.longPressStartIndex = index;
|
495
604
|
this.isScrolling = false; // 重置滚动状态
|
496
605
|
|
497
|
-
//
|
498
|
-
this.
|
499
|
-
|
500
|
-
|
501
|
-
this.$emit('drag-end', {
|
502
|
-
items: [...this.itemsList],
|
503
|
-
});
|
504
|
-
}
|
505
|
-
|
506
|
-
// 通知外部触摸开始事件
|
507
|
-
this.$emit('onDragEventStart', e);
|
606
|
+
// 添加临时事件监听器,在长按期间阻止滚动
|
607
|
+
document.addEventListener('touchmove', this.onTouchMove, {
|
608
|
+
passive: false // 设置为false以允许阻止默认行为
|
609
|
+
});
|
508
610
|
|
509
|
-
// 设置新的长按计时器
|
611
|
+
// 设置新的长按计时器
|
510
612
|
this.longPressTimer = setTimeout(() => {
|
511
613
|
// 只有在没有滚动的情况下才激活拖拽模式
|
512
614
|
if (!this.isScrolling) {
|
513
|
-
console.log('长按时间到,激活拖拽模式');
|
514
615
|
this.activateDragMode(index);
|
515
|
-
} else {
|
516
|
-
console.log('检测到滚动,取消拖拽激活');
|
517
616
|
}
|
518
617
|
}, this.longPressDuration);
|
519
618
|
},
|
@@ -525,109 +624,93 @@ export default {
|
|
525
624
|
}
|
526
625
|
|
527
626
|
const touch = e.changedTouches[0];
|
627
|
+
const moveX = Math.abs(touch.pageX - this.touchStartX);
|
628
|
+
const moveY = Math.abs(touch.pageY - this.touchStartY);
|
629
|
+
|
630
|
+
// 如果水平移动大于垂直移动,则阻止事件传播
|
631
|
+
if (moveX > moveY &&!this.isTouchDragging && this.data.isEditing) {
|
632
|
+
if (e.oriEvent) {
|
633
|
+
e.oriEvent.preventDefault();
|
634
|
+
e.oriEvent.stopPropagation();
|
635
|
+
e.oriEvent.stopImmediatePropagation();
|
636
|
+
e.preventDefault && e.preventDefault();
|
637
|
+
e.stopPropagation && e.stopPropagation();
|
638
|
+
e.stopImmediatePropagation && e.stopImmediatePropagation();
|
639
|
+
}
|
640
|
+
}
|
641
|
+
|
528
642
|
// 如果已经在拖拽模式,交给onTouchMove处理
|
529
643
|
if (this.isTouchDragging) {
|
530
644
|
this.onTouchMove(e);
|
531
645
|
return;
|
532
646
|
}
|
533
647
|
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
// 计算触摸移动速度 (像素/毫秒)
|
538
|
-
const currentTime = Date.now();
|
539
|
-
const timeDiff = currentTime - this.touchStartTime;
|
540
|
-
const speedY = moveY / Math.max(1, timeDiff); // 垂直滑动速度
|
541
|
-
|
542
|
-
// 速度检测:快速滑动可能表示滚动意图
|
543
|
-
const isQuickSwipe = speedY > 0.5; // 如果垂直滑动速度大于0.5px/ms,判定为快速滑动
|
544
|
-
|
545
|
-
// 更敏感地检测滚动意图
|
546
|
-
// 1. 垂直移动显著大于水平移动
|
547
|
-
// 2. 垂直移动超过阈值的两倍
|
548
|
-
// 3. 快速垂直滑动
|
549
|
-
if (
|
550
|
-
(moveY > moveX * 1.2 && moveY > this.scrollThreshold) ||
|
551
|
-
moveY > this.scrollThreshold * 2 ||
|
552
|
-
(isQuickSwipe && moveY > this.scrollThreshold)
|
553
|
-
) {
|
554
|
-
console.log('检测到滚动意图,取消长按', { moveY, moveX, speedY });
|
648
|
+
// 检测是否在滚动
|
649
|
+
if (moveY > this.scrollThreshold * 3) {
|
650
|
+
// 增加滚动阈值以更容易识别滚动意图
|
555
651
|
this.isScrolling = true;
|
556
652
|
this.cancelLongPress();
|
557
653
|
return;
|
558
654
|
}
|
559
655
|
|
560
|
-
// 如果移动超过阈值,取消长按
|
656
|
+
// 如果移动超过阈值,取消长按
|
561
657
|
if (moveX > this.scrollThreshold || moveY > this.scrollThreshold) {
|
562
658
|
this.cancelLongPress();
|
563
|
-
return;
|
564
659
|
}
|
565
660
|
|
566
|
-
|
567
|
-
if (this.longPressTimer && !this.isScrolling) {
|
568
|
-
e.oriEvent.preventDefault();
|
569
|
-
}
|
661
|
+
return false;
|
570
662
|
},
|
571
663
|
|
572
664
|
// 激活拖拽模式
|
573
665
|
activateDragMode(index) {
|
574
666
|
console.log('长按触发,进入编辑模式');
|
667
|
+
// 进入编辑模式
|
575
668
|
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
this.$emit('onEditStateChanged', true);
|
581
|
-
} else {
|
582
|
-
// 无论之前是否处于编辑模式,都设置拖拽状态
|
583
|
-
// 设置当前拖拽索引
|
584
|
-
this.draggingIndex = index;
|
585
|
-
this.isTouchDragging = true;
|
586
|
-
// 使用存储的固定宽高
|
587
|
-
this.draggedItemRect = {
|
588
|
-
width: this.fixedItemWidth,
|
589
|
-
height: this.fixedItemHeight,
|
590
|
-
};
|
591
|
-
|
592
|
-
// 获取当前元素位置用于计算偏移
|
593
|
-
const element = document.querySelectorAll('.grid-item')[index];
|
594
|
-
if (element) {
|
595
|
-
const rect = element.getBoundingClientRect();
|
596
|
-
// 计算触摸点相对于元素左上角的偏移
|
597
|
-
this.touchOffsetX = this.touchStartX - rect.left;
|
598
|
-
this.touchOffsetY = this.touchStartY - rect.top;
|
599
|
-
|
600
|
-
// 设置克隆元素的初始位置
|
601
|
-
this.dragCloneX = rect.left;
|
602
|
-
this.dragCloneY = rect.top;
|
603
|
-
|
604
|
-
// 显示拖拽克隆
|
605
|
-
this.isDragClone = true;
|
606
|
-
|
607
|
-
// 禁止页面滚动
|
608
|
-
document.body.style.overflow = 'hidden';
|
609
|
-
document.body.style.touchAction = 'none';
|
610
|
-
|
611
|
-
// 添加全局触摸事件处理,确保捕获所有触摸事件
|
612
|
-
document.addEventListener('touchmove', this.onGlobalTouchMove, {
|
613
|
-
passive: false,
|
614
|
-
capture: true,
|
615
|
-
});
|
616
|
-
document.addEventListener('touchend', this.onTouchEnd, {
|
617
|
-
passive: false,
|
618
|
-
capture: true,
|
619
|
-
});
|
669
|
+
if(this.data.isEditing){
|
670
|
+
// 设置当前拖拽索引
|
671
|
+
this.draggingIndex = index;
|
672
|
+
this.isTouchDragging = true;
|
620
673
|
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
674
|
+
// 此时添加阻止默认行为的事件监听,但只影响拖拽状态下的行为
|
675
|
+
document.addEventListener('touchmove', this.onTouchMove, {
|
676
|
+
passive: false, // 只有在拖拽模式下才阻止默认行为
|
677
|
+
});
|
678
|
+
document.addEventListener('touchend', this.onTouchEnd, {
|
679
|
+
passive: true,
|
680
|
+
});
|
626
681
|
|
627
|
-
|
628
|
-
|
629
|
-
|
682
|
+
// 使用存储的固定宽高
|
683
|
+
this.draggedItemRect = {
|
684
|
+
width: this.fixedItemWidth,
|
685
|
+
height: this.fixedItemHeight,
|
686
|
+
};
|
687
|
+
|
688
|
+
// 获取当前元素位置用于计算偏移
|
689
|
+
const element = document.querySelectorAll('.grid-item')[index];
|
690
|
+
if (element) {
|
691
|
+
const rect = element.getBoundingClientRect();
|
692
|
+
// 计算触摸点相对于元素左上角的偏移
|
693
|
+
this.touchOffsetX = this.touchStartX - rect.left;
|
694
|
+
this.touchOffsetY = this.touchStartY - rect.top;
|
695
|
+
|
696
|
+
// 设置克隆元素的初始位置
|
697
|
+
this.dragCloneX = rect.left;
|
698
|
+
this.dragCloneY = rect.top;
|
699
|
+
|
700
|
+
// 显示拖拽克隆
|
701
|
+
this.isDragClone = true;
|
702
|
+
|
703
|
+
// 立即执行一次定位以确保初始位置准确
|
704
|
+
requestAnimationFrame(() => {
|
705
|
+
this.dragCloneX = this.touchStartX - this.touchOffsetX;
|
706
|
+
this.dragCloneY = this.touchStartY - this.touchOffsetY;
|
707
|
+
});
|
630
708
|
|
709
|
+
console.log('拖拽克隆已创建');
|
710
|
+
}
|
711
|
+
}
|
712
|
+
this.data.isEditing = true;
|
713
|
+
this.$emit('onEditStateChanged', true);
|
631
714
|
this.longPressTimer = null;
|
632
715
|
},
|
633
716
|
|
@@ -638,45 +721,24 @@ export default {
|
|
638
721
|
this.longPressTimer = null;
|
639
722
|
}
|
640
723
|
this.longPressStartIndex = null;
|
641
|
-
|
642
|
-
// 确保不再阻止滚动
|
643
|
-
if (this.isScrolling) {
|
644
|
-
// 如果已经确定为滚动意图,应该立即恢复系统默认滚动行为
|
645
|
-
document.body.style.overflow = '';
|
646
|
-
document.body.style.touchAction = '';
|
647
|
-
}
|
648
|
-
},
|
649
|
-
|
650
|
-
// 全局触摸移动处理 - 捕获阶段拦截所有触摸移动
|
651
|
-
onGlobalTouchMove(e) {
|
652
|
-
if (this.isTouchDragging) {
|
653
|
-
// 在捕获阶段强制阻止默认行为,防止页面滚动
|
654
|
-
e.preventDefault();
|
655
|
-
e.stopPropagation();
|
656
|
-
|
657
|
-
// 继续处理拖拽逻辑
|
658
|
-
this.handleTouchMove(e);
|
659
|
-
}
|
660
724
|
},
|
661
725
|
|
662
726
|
// 触摸移动处理
|
663
727
|
onTouchMove(e) {
|
664
728
|
if (!this.isTouchDragging) return;
|
665
729
|
|
666
|
-
//
|
667
|
-
if (this.isTouchDragging) {
|
668
|
-
e.oriEvent
|
669
|
-
|
730
|
+
// 确保在拖拽模式下阻止所有事件传播
|
731
|
+
if (this.isTouchDragging && this.data.isEditing) {
|
732
|
+
if (e.oriEvent) {
|
733
|
+
e.oriEvent.preventDefault();
|
734
|
+
e.oriEvent.stopPropagation();
|
735
|
+
e.oriEvent.stopImmediatePropagation(); // 添加这行以确保事件不会继续传播
|
736
|
+
}
|
737
|
+
e.preventDefault && e.preventDefault();
|
738
|
+
e.stopPropagation && e.stopPropagation();
|
739
|
+
e.stopImmediatePropagation && e.stopImmediatePropagation();
|
670
740
|
}
|
671
741
|
|
672
|
-
this.handleTouchMove(e);
|
673
|
-
},
|
674
|
-
|
675
|
-
// 抽取通用的触摸移动处理逻辑
|
676
|
-
handleTouchMove(e) {
|
677
|
-
console.log(this.isTouchDragging, 'this.isTouchDragging111111');
|
678
|
-
if (!this.isTouchDragging) return;
|
679
|
-
|
680
742
|
const touch = e.changedTouches[0];
|
681
743
|
|
682
744
|
// 更新目标位置而非直接设置
|
@@ -731,9 +793,9 @@ export default {
|
|
731
793
|
);
|
732
794
|
const centerScore = 1 - distanceFromCenter;
|
733
795
|
|
734
|
-
//
|
735
|
-
//
|
736
|
-
if (centerScore > maxCenterScore && centerScore > 0.
|
796
|
+
// 只有当分数更高且高于阈值时才更新bestTarget
|
797
|
+
// 提高阈值到0.35,确保更稳定的交换判断
|
798
|
+
if (centerScore > maxCenterScore && centerScore > 0.35) {
|
737
799
|
maxCenterScore = centerScore;
|
738
800
|
bestTarget = itemIndex;
|
739
801
|
}
|
@@ -743,24 +805,36 @@ export default {
|
|
743
805
|
// 设置悬停高亮,仅当有合格的目标时才高亮
|
744
806
|
this.hoverIndex = bestTarget;
|
745
807
|
|
746
|
-
//
|
747
|
-
|
748
|
-
|
749
|
-
this.
|
750
|
-
|
751
|
-
|
752
|
-
this.
|
808
|
+
// 增加交换防抖动处理
|
809
|
+
// 如果bestTarget变化但不等于上次的目标,先记录一下
|
810
|
+
if (this.lastBestTarget !== bestTarget && bestTarget !== null) {
|
811
|
+
if (!this.targetChangeTime) {
|
812
|
+
this.targetChangeTime = Date.now();
|
813
|
+
this.pendingTarget = bestTarget;
|
814
|
+
} else if (Date.now() - this.targetChangeTime > 100) {
|
815
|
+
// 只有当目标持续100ms稳定不变时,才认为是有效目标
|
816
|
+
this.lastBestTarget = this.pendingTarget;
|
817
|
+
this.targetChangeTime = null;
|
818
|
+
this.pendingTarget = null;
|
819
|
+
}
|
820
|
+
} else if (bestTarget === null) {
|
821
|
+
// 如果没有目标,清除防抖状态
|
822
|
+
this.targetChangeTime = null;
|
823
|
+
this.pendingTarget = null;
|
753
824
|
}
|
754
825
|
|
755
826
|
// 使用已稳定的目标进行交换
|
756
827
|
bestTarget = this.lastBestTarget;
|
757
828
|
|
758
|
-
//
|
829
|
+
// 控制交换频率,改为250ms,大幅增加稳定性
|
759
830
|
const now = Date.now();
|
760
|
-
if (now - this.lastSwapTime <
|
831
|
+
if (now - this.lastSwapTime < 250 || bestTarget === null) return;
|
761
832
|
|
762
833
|
// 执行交换逻辑
|
763
834
|
if (bestTarget !== this.draggingIndex) {
|
835
|
+
console.log(`交换元素: ${this.draggingIndex} 和 ${bestTarget}`);
|
836
|
+
|
837
|
+
// 使用ES6解构直接交换数组元素
|
764
838
|
[
|
765
839
|
this.processedList[this.draggingIndex],
|
766
840
|
this.processedList[bestTarget],
|
@@ -768,16 +842,13 @@ export default {
|
|
768
842
|
this.processedList[bestTarget],
|
769
843
|
this.processedList[this.draggingIndex],
|
770
844
|
];
|
845
|
+
|
846
|
+
// 同时更新原始数据,保持同步
|
771
847
|
[this.data.list[this.draggingIndex], this.data.list[bestTarget]] = [
|
772
848
|
this.data.list[bestTarget],
|
773
849
|
this.data.list[this.draggingIndex],
|
774
850
|
];
|
775
851
|
|
776
|
-
console.log(this.data.list, 'cdj------this.data.list');
|
777
|
-
|
778
|
-
// 更新processedList以触发视图更新和动画效果
|
779
|
-
this.processedList = this.generateDataList();
|
780
|
-
|
781
852
|
// 更新拖拽索引
|
782
853
|
this.draggingIndex = bestTarget;
|
783
854
|
this.lastSwapTime = now;
|
@@ -787,7 +858,7 @@ export default {
|
|
787
858
|
// 新增:平滑更新拖拽位置的方法
|
788
859
|
updateDragPosition() {
|
789
860
|
// 计算当前位置到目标位置的平滑过渡
|
790
|
-
const easing = 0.
|
861
|
+
const easing = 0.6; // 增加缓动系数,让拖拽更跟手
|
791
862
|
|
792
863
|
this.dragCloneX +=
|
793
864
|
(this.targetTouchX - this.touchOffsetX - this.dragCloneX) * easing;
|
@@ -812,74 +883,58 @@ export default {
|
|
812
883
|
|
813
884
|
// 元素本身的触摸结束
|
814
885
|
endTouch(e) {
|
815
|
-
console.log('
|
886
|
+
console.log('触摸结束');
|
816
887
|
this.$emit('onDragEventEnd', e);
|
817
888
|
// 如果只是短按(还在计时),就取消长按
|
818
889
|
this.cancelLongPress();
|
819
890
|
|
820
891
|
// 如果正在拖拽中,确保清理状态
|
821
892
|
if (this.isTouchDragging) {
|
822
|
-
// 取消动画帧
|
823
|
-
if (this.animationFrameId) {
|
824
|
-
cancelAnimationFrame(this.animationFrameId);
|
825
|
-
this.animationFrameId = null;
|
826
|
-
}
|
827
|
-
|
828
893
|
// 移除全局事件监听
|
829
|
-
document.removeEventListener('touchmove', this.
|
894
|
+
document.removeEventListener('touchmove', this.onTouchMove, {
|
830
895
|
passive: false,
|
831
|
-
capture: true,
|
832
896
|
});
|
833
897
|
document.removeEventListener('touchend', this.onTouchEnd, {
|
834
898
|
passive: false,
|
835
|
-
capture: true,
|
836
899
|
});
|
837
900
|
|
838
|
-
//
|
839
|
-
|
840
|
-
|
901
|
+
// 取消动画帧
|
902
|
+
if (this.animationFrameId) {
|
903
|
+
cancelAnimationFrame(this.animationFrameId);
|
904
|
+
this.animationFrameId = null;
|
905
|
+
}
|
841
906
|
|
842
907
|
this.isTouchDragging = false;
|
843
908
|
this.isDragClone = false;
|
844
909
|
this.hoverIndex = null;
|
845
910
|
this.draggingIndex = null;
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
851
|
-
|
911
|
+
this.lastBestTarget = null;
|
912
|
+
this.targetChangeTime = null;
|
913
|
+
this.pendingTarget = null;
|
914
|
+
this.touchStartX = 0;
|
915
|
+
this.touchStartY = 0;
|
916
|
+
this.targetTouchX = 0;
|
917
|
+
this.targetTouchY = 0;
|
852
918
|
}
|
853
919
|
},
|
854
920
|
|
855
921
|
// 触摸结束处理 - 全局事件
|
856
922
|
onTouchEnd(e) {
|
857
|
-
console.log('
|
858
|
-
|
859
|
-
if (this.isTouchDragging) {
|
860
|
-
// 确保阻止默认行为
|
861
|
-
e.preventDefault();
|
862
|
-
e.stopPropagation();
|
863
|
-
}
|
864
|
-
// 取消动画帧
|
865
|
-
if (this.animationFrameId) {
|
866
|
-
cancelAnimationFrame(this.animationFrameId);
|
867
|
-
this.animationFrameId = null;
|
868
|
-
}
|
923
|
+
console.log('全局触摸结束');
|
869
924
|
|
870
925
|
// 移除全局事件监听
|
871
|
-
document.removeEventListener('touchmove', this.
|
926
|
+
document.removeEventListener('touchmove', this.onTouchMove, {
|
872
927
|
passive: false,
|
873
|
-
capture: true,
|
874
928
|
});
|
875
929
|
document.removeEventListener('touchend', this.onTouchEnd, {
|
876
930
|
passive: false,
|
877
|
-
capture: true,
|
878
931
|
});
|
879
932
|
|
880
|
-
//
|
881
|
-
|
882
|
-
|
933
|
+
// 取消动画帧
|
934
|
+
if (this.animationFrameId) {
|
935
|
+
cancelAnimationFrame(this.animationFrameId);
|
936
|
+
this.animationFrameId = null;
|
937
|
+
}
|
883
938
|
|
884
939
|
// 重置拖拽状态
|
885
940
|
this.isTouchDragging = false;
|
@@ -897,6 +952,7 @@ export default {
|
|
897
952
|
if (gridBoxes && gridBoxes.length > 0) {
|
898
953
|
const box = gridBoxes[0];
|
899
954
|
this.gridBoxWidth = box.offsetWidth;
|
955
|
+
console.log('Grid box宽度:', this.gridBoxWidth);
|
900
956
|
|
901
957
|
// 更新text样式,确保不超出
|
902
958
|
const textElements = document.querySelectorAll('.itemText');
|
@@ -909,14 +965,15 @@ export default {
|
|
909
965
|
|
910
966
|
initLottieAnimations() {
|
911
967
|
this.$nextTick(() => {
|
968
|
+
// 清理之前的动画实例
|
912
969
|
Object.keys(this.lottieAnimations).forEach((key) => {
|
913
970
|
const anim = this.lottieAnimations[key];
|
914
971
|
if (anim && typeof anim.destroy === 'function') {
|
915
|
-
console.log(
|
972
|
+
console.log(`销毁旧动画实例: ${key}`);
|
916
973
|
try {
|
917
974
|
anim.removeEventListener('complete');
|
918
975
|
} catch (e) {
|
919
|
-
console.warn(
|
976
|
+
console.warn(`移除事件监听器失败: ${e.message}`);
|
920
977
|
}
|
921
978
|
anim.destroy();
|
922
979
|
}
|
@@ -927,155 +984,166 @@ export default {
|
|
927
984
|
this.lottieAnimations = {};
|
928
985
|
|
929
986
|
console.log(
|
930
|
-
'
|
987
|
+
'开始初始化Lottie动画,列表项数量:',
|
931
988
|
this.processedList.length
|
932
989
|
);
|
933
990
|
|
934
|
-
//
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
991
|
+
// 延迟一点时间确保DOM完全更新并且之前的动画已被销毁
|
992
|
+
setTimeout(() => {
|
993
|
+
// 为每个列表项初始化动画
|
994
|
+
this.processedList.forEach((item) => {
|
995
|
+
const itemId = String(item.itemView.id);
|
996
|
+
console.log('处理项目ID:', itemId);
|
997
|
+
console.log('loadingAnimView:', item.loadingAnimView);
|
998
|
+
console.log('bgAnimView:', item.bgAnimView);
|
999
|
+
console.log('frontAnimView:', item.frontAnimView);
|
1000
|
+
|
1001
|
+
// 加载背景动画
|
939
1002
|
this.loadLottieAnimation(
|
940
1003
|
item.bgAnimView,
|
941
1004
|
`bgAnim-${itemId}`,
|
942
1005
|
`bg-${itemId}`,
|
943
1006
|
item
|
944
1007
|
);
|
945
|
-
}
|
946
1008
|
|
947
|
-
|
948
|
-
if (item.loadingAnimView && item.loadingAnimView.animUrl) {
|
1009
|
+
// 加载加载中动画
|
949
1010
|
this.loadLottieAnimation(
|
950
1011
|
item.loadingAnimView,
|
951
1012
|
`loadingAnim-${itemId}`,
|
952
1013
|
`loading-${itemId}`,
|
953
1014
|
item
|
954
1015
|
);
|
955
|
-
}
|
956
1016
|
|
957
|
-
|
958
|
-
if (item.frontAnimView && item.frontAnimView.animUrl) {
|
1017
|
+
// 加载前景动画
|
959
1018
|
this.loadLottieAnimation(
|
960
1019
|
item.frontAnimView,
|
961
1020
|
`frontAnim-${itemId}`,
|
962
1021
|
`front-${itemId}`,
|
963
1022
|
item
|
964
1023
|
);
|
965
|
-
}
|
966
|
-
});
|
1024
|
+
});
|
1025
|
+
}, 50); // 小延迟确保DOM已更新和旧动画已销毁
|
967
1026
|
});
|
968
1027
|
},
|
969
1028
|
|
970
1029
|
// 抽取公共的loadLottieAnimation方法
|
971
|
-
loadLottieAnimation(animView, refName, animKey, item
|
1030
|
+
loadLottieAnimation(animView, refName, animKey, item) {
|
972
1031
|
if (!animView || !animView.animUrl) {
|
1032
|
+
console.log(`[${animKey}] 跳过加载:animUrl不存在`);
|
973
1033
|
return;
|
974
1034
|
}
|
975
1035
|
|
1036
|
+
console.log(
|
1037
|
+
`[${animKey}] 尝试初始化${refName}, animUrl:`,
|
1038
|
+
animView.animUrl
|
1039
|
+
);
|
1040
|
+
|
976
1041
|
const animEl = this.$refs[refName];
|
977
1042
|
if (!animEl || !animEl[0]) {
|
1043
|
+
console.warn(`[${animKey}] 未找到${refName}的DOM引用`);
|
978
1044
|
return;
|
979
1045
|
}
|
980
1046
|
|
981
1047
|
try {
|
982
|
-
//
|
1048
|
+
// 如果已经有实例,先暂停它
|
983
1049
|
if (this.lottieAnimations[animKey]) {
|
984
|
-
this.lottieAnimations[animKey].
|
985
|
-
delete this.lottieAnimations[animKey];
|
1050
|
+
this.lottieAnimations[animKey].pause();
|
986
1051
|
}
|
987
1052
|
|
988
|
-
//
|
1053
|
+
// 确保容器可见且透明度重置
|
1054
|
+
animEl[0].style.display = '';
|
1055
|
+
animEl[0].style.opacity = '1';
|
1056
|
+
animEl[0].style.transition = 'opacity 0.2s';
|
1057
|
+
|
989
1058
|
if (
|
990
1059
|
typeof animView.visibility !== 'number' ||
|
991
1060
|
animView.visibility !== 0
|
992
1061
|
) {
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
|
997
|
-
|
998
|
-
|
999
|
-
const anim = lottie.loadAnimation({
|
1000
|
-
container: animEl[0],
|
1001
|
-
renderer: 'svg',
|
1002
|
-
loop: Boolean(item.animRepeatCount) || false,
|
1003
|
-
autoplay: true,
|
1004
|
-
path: animView.animUrl,
|
1005
|
-
rendererSettings: {
|
1006
|
-
preserveAspectRatio: 'xMidYMid slice',
|
1007
|
-
},
|
1008
|
-
});
|
1009
|
-
|
1010
|
-
// 保存动画实例
|
1011
|
-
this.lottieAnimations[animKey] = anim;
|
1012
|
-
|
1013
|
-
// 添加动画事件监听
|
1014
|
-
anim.addEventListener('complete', () => {
|
1015
|
-
// 检查是否需要销毁动画(非循环动画完成后销毁)
|
1016
|
-
if (!item.animRepeatCount) {
|
1017
|
-
// 销毁前再次检查实例是否存在
|
1018
|
-
if (this.lottieAnimations[animKey] === anim) {
|
1019
|
-
// 移除所有事件监听器
|
1020
|
-
anim.removeEventListener('complete');
|
1021
|
-
// 销毁动画实例
|
1022
|
-
anim.destroy();
|
1023
|
-
delete this.lottieAnimations[animKey];
|
1024
|
-
|
1025
|
-
// 查找并清空对应的动画容器,彻底移除所有内容
|
1026
|
-
this.$nextTick(() => {
|
1027
|
-
const animContainer =
|
1028
|
-
this.$refs[refName] && this.$refs[refName][0];
|
1029
|
-
if (animContainer) {
|
1030
|
-
// 隐藏容器
|
1031
|
-
animContainer.style.display = 'none';
|
1032
|
-
// 清空容器内容,确保不显示最后一帧
|
1033
|
-
animContainer.innerHTML = '';
|
1034
|
-
// 确保不会阻止触摸事件
|
1035
|
-
animContainer.style.pointerEvents = 'none';
|
1036
|
-
}
|
1062
|
+
// 延迟一帧再销毁旧实例
|
1063
|
+
requestAnimationFrame(() => {
|
1064
|
+
if (this.lottieAnimations[animKey]) {
|
1065
|
+
this.lottieAnimations[animKey].destroy();
|
1066
|
+
delete this.lottieAnimations[animKey];
|
1067
|
+
}
|
1037
1068
|
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1056
|
-
|
1057
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
1062
|
-
|
1063
|
-
|
1064
|
-
|
1065
|
-
|
1066
|
-
|
1067
|
-
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1069
|
+
// 创建新的动画实例
|
1070
|
+
console.log(`[${animKey}] 创建新的动画实例`);
|
1071
|
+
const anim = lottie.loadAnimation({
|
1072
|
+
container: animEl[0],
|
1073
|
+
renderer: 'svg',
|
1074
|
+
loop: Boolean(item.animRepeatCount) || false,
|
1075
|
+
autoplay: true,
|
1076
|
+
path: animView.animUrl,
|
1077
|
+
rendererSettings: {
|
1078
|
+
preserveAspectRatio: 'xMidYMid slice',
|
1079
|
+
progressiveLoad: true, // 启用渐进式加载
|
1080
|
+
hideOnTransparent: true, // 优化透明度处理
|
1081
|
+
},
|
1082
|
+
});
|
1083
|
+
|
1084
|
+
this.lottieAnimations[animKey] = anim;
|
1085
|
+
anim.addEventListener('complete', () => {
|
1086
|
+
// 检查是否需要销毁动画(非循环动画完成后销毁)
|
1087
|
+
if (!item.animRepeatCount) {
|
1088
|
+
// 销毁前再次检查实例是否存在
|
1089
|
+
if (this.lottieAnimations[animKey] === anim) {
|
1090
|
+
anim.removeEventListener('complete');
|
1091
|
+
anim.destroy();
|
1092
|
+
delete this.lottieAnimations[animKey];
|
1093
|
+
// 将对应数据中的animUrl清空
|
1094
|
+
const itemId = String(item.itemView.id);
|
1095
|
+
const index = this.processedList.findIndex(
|
1096
|
+
(i) => i.itemView.id == itemId
|
1097
|
+
);
|
1098
|
+
if (index !== -1) {
|
1099
|
+
// 根据动画类型设置对应的animUrl为空
|
1100
|
+
if (animKey.startsWith('bg-')) {
|
1101
|
+
this.$set(
|
1102
|
+
this.processedList[index].bgAnimView,
|
1103
|
+
'animUrl',
|
1104
|
+
''
|
1105
|
+
);
|
1106
|
+
this.$set(
|
1107
|
+
this.data.list[index].bgAnimView,
|
1108
|
+
'animUrl',
|
1109
|
+
''
|
1110
|
+
);
|
1111
|
+
} else if (animKey.startsWith('loading-')) {
|
1112
|
+
this.$set(
|
1113
|
+
this.processedList[index].loadingAnimView,
|
1114
|
+
'animUrl',
|
1115
|
+
''
|
1116
|
+
);
|
1117
|
+
this.$set(
|
1118
|
+
this.data.list[index].loadingAnimView,
|
1119
|
+
'animUrl',
|
1120
|
+
''
|
1121
|
+
);
|
1122
|
+
} else if (animKey.startsWith('front-')) {
|
1123
|
+
this.$set(
|
1124
|
+
this.processedList[index].frontAnimView,
|
1125
|
+
'animUrl',
|
1126
|
+
''
|
1127
|
+
);
|
1128
|
+
this.$set(
|
1129
|
+
this.data.list[index].frontAnimView,
|
1130
|
+
'animUrl',
|
1131
|
+
''
|
1132
|
+
);
|
1133
|
+
}
|
1134
|
+
console.log(`[${animKey}] 已清空动画URL数据`);
|
1135
|
+
item.addEventListener('touchstart', (e) => this.startLongPress(e, index), { passive: false });
|
1136
|
+
item.addEventListener('touchmove', this.checkLongPressMove, { passive: false });
|
1137
|
+
item.addEventListener('touchend', this.endTouch, { passive: false });
|
1138
|
+
item.addEventListener('touchcancel', this.endTouch, { passive: false });
|
1071
1139
|
}
|
1072
|
-
}
|
1140
|
+
}
|
1073
1141
|
}
|
1074
|
-
}
|
1142
|
+
});
|
1075
1143
|
});
|
1076
1144
|
}
|
1077
1145
|
} catch (error) {
|
1078
|
-
console.error(
|
1146
|
+
console.error(`[${animKey}] 初始化失败:`, error);
|
1079
1147
|
}
|
1080
1148
|
},
|
1081
1149
|
|
@@ -1090,35 +1158,66 @@ export default {
|
|
1090
1158
|
}
|
1091
1159
|
});
|
1092
1160
|
},
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1161
|
+
|
1162
|
+
// 暂未使用,但保留用于将来扩展
|
1163
|
+
leftTopIconClick(e) {
|
1164
|
+
e.preventDefault();
|
1165
|
+
e.stopPropagation();
|
1166
|
+
// 这里可以添加将来的处理逻辑
|
1167
|
+
return false;
|
1100
1168
|
},
|
1101
1169
|
},
|
1102
1170
|
watch: {
|
1171
|
+
// 监听初始数据变化
|
1172
|
+
initialItems: {
|
1173
|
+
handler(newItems) {
|
1174
|
+
if (!this.isTouchDragging) {
|
1175
|
+
this.items = [...newItems];
|
1176
|
+
}
|
1177
|
+
},
|
1178
|
+
deep: true,
|
1179
|
+
},
|
1103
1180
|
// 监听列表数据变化,自动更新处理后的数据
|
1104
1181
|
'data.list': {
|
1105
1182
|
handler(newList, oldList) {
|
1106
1183
|
this.processedList = this.generateDataList();
|
1107
|
-
console.log(newList, this.processedList, 'cdj------数据变化');
|
1108
1184
|
this.$nextTick(() => {
|
1109
|
-
|
1110
|
-
this
|
1185
|
+
// 再次使用 nextTick 确保 DOM 完全更新
|
1186
|
+
this.$nextTick(() => {
|
1187
|
+
const gridItems = document.querySelectorAll('.grid-item');
|
1188
|
+
gridItems.forEach((item, index) => {
|
1189
|
+
const hasEvents = item._vei;
|
1190
|
+
if (!hasEvents) {
|
1191
|
+
item.addEventListener('touchstart', (e) => this.startLongPress(e, index), { passive: false });
|
1192
|
+
item.addEventListener('touchmove', this.checkLongPressMove, { passive: false });
|
1193
|
+
item.addEventListener('touchend', this.endTouch, { passive: false });
|
1194
|
+
item.addEventListener('touchcancel', this.endTouch, { passive: false });
|
1195
|
+
}
|
1196
|
+
});
|
1197
|
+
this.initLottieAnimations();
|
1198
|
+
});
|
1111
1199
|
});
|
1112
1200
|
},
|
1113
1201
|
deep: true,
|
1114
1202
|
},
|
1203
|
+
// 监听所有动画URL变化,确保能及时更新动画
|
1204
|
+
processedList: {
|
1205
|
+
handler(newList, oldList) {
|
1206
|
+
if (!newList || !newList.length || this.isTouchDragging) return;
|
1207
|
+
// 避免在拖拽过程中重新初始化动画
|
1208
|
+
if (this.draggingIndex !== null) return;
|
1209
|
+
// 检查每个项目的动画URL是否有变化
|
1210
|
+
},
|
1211
|
+
deep: true,
|
1212
|
+
},
|
1115
1213
|
},
|
1116
1214
|
mounted() {
|
1117
|
-
console.log(this.data, '
|
1215
|
+
console.log(this.data, 'djdjdjdjdjdjdj');
|
1118
1216
|
// 初始获取grid-box宽度
|
1119
1217
|
this.updateGridBoxWidth();
|
1120
1218
|
// 获取并存储固定宽高
|
1121
1219
|
this.updateFixedItemSize();
|
1220
|
+
|
1122
1221
|
// 监听窗口大小变化,更新grid-box宽度
|
1123
1222
|
window.addEventListener('resize', () => {
|
1124
1223
|
this.updateGridBoxWidth();
|
@@ -1137,19 +1236,6 @@ export default {
|
|
1137
1236
|
document.removeEventListener('touchend', this.onTouchEnd, {
|
1138
1237
|
passive: false,
|
1139
1238
|
});
|
1140
|
-
// 确保全局事件监听也被清理
|
1141
|
-
document.removeEventListener('touchmove', this.onGlobalTouchMove, {
|
1142
|
-
passive: false,
|
1143
|
-
capture: true,
|
1144
|
-
});
|
1145
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
1146
|
-
passive: false,
|
1147
|
-
capture: true,
|
1148
|
-
});
|
1149
|
-
// 恢复页面滚动状态
|
1150
|
-
document.body.style.overflow = '';
|
1151
|
-
document.body.style.touchAction = '';
|
1152
|
-
|
1153
1239
|
window.removeEventListener('resize', () => {
|
1154
1240
|
this.updateGridBoxWidth();
|
1155
1241
|
this.updateFixedItemSize();
|
@@ -1165,27 +1251,19 @@ export default {
|
|
1165
1251
|
Object.keys(this.lottieAnimations).forEach((key) => {
|
1166
1252
|
const anim = this.lottieAnimations[key];
|
1167
1253
|
if (anim && typeof anim.destroy === 'function') {
|
1254
|
+
console.log(`组件销毁时清理动画: ${key}`);
|
1168
1255
|
try {
|
1169
1256
|
anim.removeEventListener('complete');
|
1257
|
+
anim.removeEventListener('DOMLoaded');
|
1258
|
+
anim.removeEventListener('data_ready');
|
1259
|
+
anim.removeEventListener('loaded_images');
|
1260
|
+
anim.removeEventListener('loopComplete');
|
1170
1261
|
} catch (e) {
|
1171
1262
|
console.warn(`移除事件监听器失败: ${e.message}`);
|
1172
1263
|
}
|
1173
1264
|
anim.destroy();
|
1174
1265
|
}
|
1175
1266
|
delete this.lottieAnimations[key];
|
1176
|
-
|
1177
|
-
// 尝试清理对应的DOM容器
|
1178
|
-
const parts = key.split('-');
|
1179
|
-
if (parts.length === 2) {
|
1180
|
-
const animType = parts[0]; // bg, loading, front
|
1181
|
-
const itemId = parts[1];
|
1182
|
-
const refName = `${animType}Anim-${itemId}`;
|
1183
|
-
const animContainer = this.$refs[refName] && this.$refs[refName][0];
|
1184
|
-
if (animContainer) {
|
1185
|
-
animContainer.innerHTML = '';
|
1186
|
-
animContainer.style.display = 'none';
|
1187
|
-
}
|
1188
|
-
}
|
1189
1267
|
});
|
1190
1268
|
|
1191
1269
|
// 确保动画实例对象被清空
|
@@ -1211,6 +1289,10 @@ button {
|
|
1211
1289
|
position: relative;
|
1212
1290
|
}
|
1213
1291
|
|
1292
|
+
.items-container {
|
1293
|
+
width: 100%;
|
1294
|
+
}
|
1295
|
+
|
1214
1296
|
.grid-items-wrapper {
|
1215
1297
|
width: 100%;
|
1216
1298
|
position: relative;
|
@@ -1220,15 +1302,13 @@ button {
|
|
1220
1302
|
display: grid;
|
1221
1303
|
grid-template-columns: repeat(var(--grid-cols), 1fr);
|
1222
1304
|
grid-gap: var(--grid-gap);
|
1223
|
-
width: calc(100% -
|
1305
|
+
width: calc(100% - 74px);
|
1224
1306
|
}
|
1225
1307
|
|
1226
1308
|
/* 优化排序过渡效果 */
|
1227
1309
|
.grid-fade-move {
|
1228
|
-
transition: transform 0.
|
1310
|
+
transition: transform 0.2s cubic-bezier(0.2, 0, 0.2, 1);
|
1229
1311
|
will-change: transform;
|
1230
|
-
position: relative;
|
1231
|
-
z-index: 2;
|
1232
1312
|
}
|
1233
1313
|
|
1234
1314
|
.grid-item {
|
@@ -1268,9 +1348,7 @@ button {
|
|
1268
1348
|
top: 0;
|
1269
1349
|
pointer-events: none;
|
1270
1350
|
}
|
1271
|
-
|
1272
|
-
pointer-events: none;
|
1273
|
-
}
|
1351
|
+
|
1274
1352
|
.bg-anim,
|
1275
1353
|
.front-anim {
|
1276
1354
|
z-index: 1;
|