@dolphinweex/weex-harmony 0.1.28 → 0.1.29
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/package.json
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
<div class="grid-items-wrapper">
|
9
9
|
<transition-group name="grid-fade" tag="div" class="grid-inner-wrapper">
|
10
10
|
<div
|
11
|
-
v-for="(item, index) in
|
11
|
+
v-for="(item, index) in data.list"
|
12
12
|
:key="item.itemView.id"
|
13
13
|
class="grid-item"
|
14
14
|
:class="{
|
@@ -117,34 +117,34 @@
|
|
117
117
|
<div v-if="isDragClone" class="drag-clone" :style="dragCloneStyle">
|
118
118
|
<div
|
119
119
|
class="grid-box"
|
120
|
-
:style="applyNormalAttribute(
|
120
|
+
:style="applyNormalAttribute(data.list[draggingIndex].itemView)"
|
121
121
|
>
|
122
122
|
<div class="flexBox">
|
123
123
|
<img
|
124
|
-
:src="
|
124
|
+
:src="data.list[draggingIndex].iconImage.url"
|
125
125
|
:style="
|
126
|
-
applyNormalAttribute(
|
126
|
+
applyNormalAttribute(data.list[draggingIndex].iconImage)
|
127
127
|
"
|
128
128
|
/>
|
129
129
|
<div @click.stop.prevent>
|
130
130
|
<img
|
131
131
|
class="isSelected"
|
132
132
|
:src="
|
133
|
-
|
134
|
-
?
|
135
|
-
:
|
133
|
+
data.list[draggingIndex].itemView.isSelected
|
134
|
+
? data.list[draggingIndex].selectIcon.selectedUrl
|
135
|
+
: data.list[draggingIndex].selectIcon.url
|
136
136
|
"
|
137
137
|
:style="
|
138
|
-
applyNormalAttribute(
|
138
|
+
applyNormalAttribute(data.list[draggingIndex].selectIcon)
|
139
139
|
"
|
140
140
|
/>
|
141
141
|
</div>
|
142
142
|
</div>
|
143
143
|
<div
|
144
144
|
class="itemText"
|
145
|
-
:style="[applyNormalAttribute(
|
145
|
+
:style="[applyNormalAttribute(data.list[draggingIndex].text)]"
|
146
146
|
>
|
147
|
-
{{
|
147
|
+
{{ data.list[draggingIndex].text.text }}
|
148
148
|
</div>
|
149
149
|
</div>
|
150
150
|
</div>
|
@@ -173,7 +173,6 @@ export default {
|
|
173
173
|
return {
|
174
174
|
targetTouchX: 0,
|
175
175
|
targetTouchY: 0,
|
176
|
-
processedList: [],
|
177
176
|
selected: null,
|
178
177
|
longPressTimer: null,
|
179
178
|
longPressDuration: 350,
|
@@ -198,9 +197,7 @@ export default {
|
|
198
197
|
fixedItemWidth: 0,
|
199
198
|
fixedItemHeight: 0,
|
200
199
|
animationFrameId: null,
|
201
|
-
|
202
|
-
targetChangeTime: null,
|
203
|
-
pendingTarget: null,
|
200
|
+
isProcessingData: false,
|
204
201
|
};
|
205
202
|
},
|
206
203
|
computed: {
|
@@ -208,6 +205,7 @@ export default {
|
|
208
205
|
return {
|
209
206
|
'--grid-cols': this.data.spanCount || this.cols,
|
210
207
|
'--grid-gap': `${this.data.spaceSize || 8}px`,
|
208
|
+
'--grid-margin': `${(this.data.layoutConfig.marginStart ) + (this.data.layoutConfig.marginEnd )}px`,
|
211
209
|
marginTop: this.data.layoutConfig.marginTop * scla,
|
212
210
|
marginBottom: this.data.layoutConfig.marginBottom * scla,
|
213
211
|
marginLeft: this.data.layoutConfig.marginStart * scla,
|
@@ -220,7 +218,7 @@ export default {
|
|
220
218
|
return { display: 'none' };
|
221
219
|
}
|
222
220
|
|
223
|
-
const item = this.
|
221
|
+
const item = this.data.list[this.draggingIndex];
|
224
222
|
let itemView = {};
|
225
223
|
if (item) {
|
226
224
|
itemView = this.applyNormalAttribute(item.itemView);
|
@@ -245,10 +243,7 @@ export default {
|
|
245
243
|
updateListItem(data) {
|
246
244
|
console.log('updateListItem收到数据:', JSON.stringify(data));
|
247
245
|
const id = data.itemView.id;
|
248
|
-
const index = this.
|
249
|
-
(item) => item.itemView.id == id
|
250
|
-
);
|
251
|
-
const dataIndex = this.data.list.findIndex(
|
246
|
+
const index = this.data.list.findIndex(
|
252
247
|
(item) => item.itemView.id == id
|
253
248
|
);
|
254
249
|
|
@@ -257,89 +252,98 @@ export default {
|
|
257
252
|
}
|
258
253
|
|
259
254
|
// 检查是否有动画URL的变化
|
260
|
-
const oldBgAnimUrl = this.
|
261
|
-
const oldLoadingAnimUrl =
|
262
|
-
|
263
|
-
const oldFrontAnimUrl =
|
264
|
-
this.processedList[index].frontAnimView.animUrl || '';
|
265
|
-
|
255
|
+
const oldBgAnimUrl = this.data.list[index].bgAnimView.animUrl || '';
|
256
|
+
const oldLoadingAnimUrl = this.data.list[index].loadingAnimView.animUrl || '';
|
257
|
+
const oldFrontAnimUrl = this.data.list[index].frontAnimView.animUrl || '';
|
266
258
|
const newBgAnimUrl = data.bgAnimView.animUrl || '';
|
267
259
|
const newLoadingAnimUrl = data.loadingAnimView.animUrl || '';
|
268
260
|
const newFrontAnimUrl = data.frontAnimView.animUrl || '';
|
269
|
-
// 同时更新processedList和原始data.list数据
|
270
|
-
this.processedList[index] = data;
|
271
|
-
this.processedList = [...this.processedList];
|
272
|
-
|
273
|
-
// 更新原始数据以确保watch能触发
|
274
|
-
if (dataIndex !== -1) {
|
275
|
-
// 使用Vue的$set确保响应式更新
|
276
|
-
this.$set(this.data.list, dataIndex, JSON.parse(JSON.stringify(data)));
|
277
|
-
}
|
278
261
|
|
279
|
-
//
|
280
|
-
|
262
|
+
// 合并全局数据到新数据
|
263
|
+
const mergedData = this.mergeItemData(data);
|
264
|
+
|
265
|
+
// 更新数据
|
266
|
+
this.$set(this.data.list, index, mergedData);
|
281
267
|
|
282
|
-
// 等待下一帧DOM更新完成后再操作
|
283
268
|
this.$nextTick(() => {
|
284
|
-
// 清理旧的动画实例
|
285
269
|
const itemId = String(data.itemView.id);
|
286
270
|
|
287
|
-
//
|
288
|
-
|
289
|
-
|
290
|
-
|
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
|
-
});
|
271
|
+
// 处理背景动画
|
272
|
+
if (oldBgAnimUrl !== newBgAnimUrl && newBgAnimUrl) {
|
273
|
+
this.createLottieAnimation('bg', itemId, mergedData.bgAnimView, mergedData);
|
274
|
+
}
|
306
275
|
|
307
|
-
|
276
|
+
// 处理加载动画
|
277
|
+
if (oldLoadingAnimUrl !== newLoadingAnimUrl && newLoadingAnimUrl) {
|
278
|
+
this.createLottieAnimation('loading', itemId, mergedData.loadingAnimView, mergedData);
|
279
|
+
}
|
308
280
|
|
309
|
-
//
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
`bgAnim-${itemId}`,
|
316
|
-
`bg-${itemId}`,
|
317
|
-
data
|
318
|
-
);
|
319
|
-
}
|
281
|
+
// 处理前景动画
|
282
|
+
if (oldFrontAnimUrl !== newFrontAnimUrl && newFrontAnimUrl) {
|
283
|
+
this.createLottieAnimation('front', itemId, mergedData.frontAnimView, mergedData);
|
284
|
+
}
|
285
|
+
});
|
286
|
+
},
|
320
287
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
288
|
+
// 简化的动画创建方法
|
289
|
+
createLottieAnimation(type, itemId, animView, itemData) {
|
290
|
+
const animKey = `${type}-${itemId}`;
|
291
|
+
const refName = `${type}Anim-${itemId}`;
|
292
|
+
|
293
|
+
// 销毁旧动画
|
294
|
+
if (this.lottieAnimations[animKey]) {
|
295
|
+
this.lottieAnimations[animKey].destroy();
|
296
|
+
delete this.lottieAnimations[animKey];
|
297
|
+
}
|
329
298
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
}
|
299
|
+
// 检查DOM元素是否存在
|
300
|
+
const animEl = this.$refs[refName];
|
301
|
+
if (!animEl || !animEl[0] || !animView.animUrl) {
|
302
|
+
return;
|
303
|
+
}
|
304
|
+
|
305
|
+
// 检查可见性
|
306
|
+
if (typeof animView.visibility === 'number' && animView.visibility === 0) {
|
307
|
+
return;
|
308
|
+
}
|
309
|
+
|
310
|
+
try {
|
311
|
+
const anim = lottie.loadAnimation({
|
312
|
+
container: animEl[0],
|
313
|
+
renderer: 'svg',
|
314
|
+
loop: Boolean(itemData.animRepeatCount) || false,
|
315
|
+
autoplay: true,
|
316
|
+
path: animView.animUrl,
|
317
|
+
rendererSettings: {
|
318
|
+
preserveAspectRatio: 'xMidYMid slice',
|
319
|
+
progressiveLoad: true,
|
320
|
+
hideOnTransparent: true,
|
321
|
+
},
|
322
|
+
});
|
323
|
+
|
324
|
+
this.lottieAnimations[animKey] = anim;
|
325
|
+
|
326
|
+
// 非循环动画完成后清理
|
327
|
+
if (!itemData.animRepeatCount) {
|
328
|
+
anim.addEventListener('complete', () => {
|
329
|
+
if (this.lottieAnimations[animKey] === anim) {
|
330
|
+
anim.destroy();
|
331
|
+
delete this.lottieAnimations[animKey];
|
332
|
+
|
333
|
+
// 清空对应的animUrl数据
|
334
|
+
const index = this.data.list.findIndex(i => i.itemView.id == itemId);
|
335
|
+
if (index !== -1) {
|
336
|
+
this.$set(this.data.list[index][`${type}AnimView`], 'animUrl', '');
|
337
|
+
}
|
338
|
+
}
|
339
|
+
});
|
340
|
+
}
|
341
|
+
} catch (error) {
|
342
|
+
console.error(`创建${type}动画失败:`, error);
|
343
|
+
}
|
340
344
|
},
|
341
345
|
getListData(callback) {
|
342
|
-
let data = Array.from(this.
|
346
|
+
let data = Array.from(this.data.list);
|
343
347
|
data = data.map((item) => ({
|
344
348
|
...item,
|
345
349
|
itemView: {
|
@@ -364,7 +368,7 @@ export default {
|
|
364
368
|
},
|
365
369
|
selectIconClick(e, item, index) {
|
366
370
|
e.preventDefault();
|
367
|
-
e.stopPropagation();
|
371
|
+
e.stopPropagation();
|
368
372
|
if (item.selectIcon.visibility) {
|
369
373
|
item.itemView.isSelected = !item.itemView.isSelected;
|
370
374
|
}
|
@@ -380,16 +384,10 @@ export default {
|
|
380
384
|
},
|
381
385
|
|
382
386
|
itemViewClick(e, item, index) {
|
383
|
-
// 如果事件已经被处理或被阻止默认行为,则不继续执行
|
384
387
|
if (e.defaultPrevented) {
|
385
388
|
return;
|
386
389
|
}
|
387
|
-
|
388
|
-
...item.itemView,
|
389
|
-
id: String(item.itemView.id),
|
390
|
-
index,
|
391
|
-
...e,
|
392
|
-
};
|
390
|
+
|
393
391
|
console.log('itemViewClick');
|
394
392
|
// 确保取消任何长按计时器
|
395
393
|
this.cancelLongPress();
|
@@ -401,10 +399,13 @@ export default {
|
|
401
399
|
}
|
402
400
|
e.preventDefault();
|
403
401
|
e.stopPropagation();
|
404
|
-
|
402
|
+
const target = {
|
403
|
+
...item.itemView,
|
404
|
+
id: String(item.itemView.id),
|
405
|
+
index,
|
406
|
+
...e,
|
407
|
+
};
|
405
408
|
this.$emit('onClickItem', target);
|
406
|
-
this.data.isEditing&&item.selectIcon.visibility&&this.$emit('onSelectItems', target);
|
407
|
-
|
408
409
|
},
|
409
410
|
|
410
411
|
actionClick(e, item, index) {
|
@@ -425,14 +426,12 @@ export default {
|
|
425
426
|
return false;
|
426
427
|
},
|
427
428
|
|
428
|
-
// 新增方法:清理除指定项目外的所有动画实例
|
429
429
|
cleanupOtherAnimations(currentItemId) {
|
430
430
|
console.log('清理其他项目的动画实例,当前项目ID:', currentItemId);
|
431
431
|
|
432
432
|
Object.keys(this.lottieAnimations).forEach((key) => {
|
433
433
|
if (!key.includes(`-${currentItemId}`)) {
|
434
434
|
if (this.lottieAnimations[key]) {
|
435
|
-
console.log(`销毁其他项目的动画实例: ${key}`);
|
436
435
|
const anim = this.lottieAnimations[key];
|
437
436
|
|
438
437
|
// 不要立即销毁,而是先停止动画
|
@@ -477,37 +476,55 @@ export default {
|
|
477
476
|
});
|
478
477
|
},
|
479
478
|
|
480
|
-
// 生成处理后的数据列表
|
481
479
|
generateDataList() {
|
482
480
|
if (!this.data || !this.data.list || !this.data.globalData) {
|
483
|
-
return
|
481
|
+
return;
|
484
482
|
}
|
485
483
|
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
'text',
|
493
|
-
'actionButton',
|
494
|
-
'rightIcon',
|
495
|
-
'leftTopIcon',
|
496
|
-
'iconImage',
|
497
|
-
'selectIcon',
|
498
|
-
'loadingAnimView',
|
499
|
-
'bgAnimView',
|
500
|
-
'frontAnimView',
|
501
|
-
];
|
484
|
+
// 防止死循环
|
485
|
+
if (this.isProcessingData) {
|
486
|
+
return;
|
487
|
+
}
|
488
|
+
|
489
|
+
this.isProcessingData = true;
|
502
490
|
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
processedItem
|
491
|
+
try {
|
492
|
+
// 直接处理 this.data.list,为每个项目合并全局数据
|
493
|
+
this.data.list.forEach((item, index) => {
|
494
|
+
const processedItem = this.mergeItemData(item);
|
495
|
+
this.$set(this.data.list, index, processedItem);
|
496
|
+
});
|
497
|
+
} finally {
|
498
|
+
// 确保标志位被重置
|
499
|
+
this.$nextTick(() => {
|
500
|
+
this.isProcessingData = false;
|
507
501
|
});
|
508
|
-
|
502
|
+
}
|
503
|
+
},
|
504
|
+
|
505
|
+
mergeItemData(item) {
|
506
|
+
const processedItem = {};
|
507
|
+
const types = [
|
508
|
+
'itemView',
|
509
|
+
'text',
|
510
|
+
'actionButton',
|
511
|
+
'rightIcon',
|
512
|
+
'leftTopIcon',
|
513
|
+
'iconImage',
|
514
|
+
'selectIcon',
|
515
|
+
'loadingAnimView',
|
516
|
+
'bgAnimView',
|
517
|
+
'frontAnimView',
|
518
|
+
];
|
519
|
+
|
520
|
+
types.forEach((type) => {
|
521
|
+
// 使用mergeDate合并全局数据和项目数据
|
522
|
+
processedItem[type] = this.mergeDate(item[type] || {}, type);
|
509
523
|
});
|
524
|
+
|
525
|
+
return processedItem;
|
510
526
|
},
|
527
|
+
|
511
528
|
getItemStyle(item, type) {
|
512
529
|
const result = this.mergeDate(item[type] || {}, type);
|
513
530
|
console.log(type, this.applyNormalAttribute(result), 'ggg');
|
@@ -538,18 +555,14 @@ export default {
|
|
538
555
|
borderRadius: `${(data.cornerRadius || 0) * scla}px`,
|
539
556
|
opacity: data.alpha || 1,
|
540
557
|
};
|
541
|
-
// 特殊处理visibility和display
|
542
558
|
if (typeof data.visibility === 'number') {
|
543
559
|
if (data.visibility === 0) {
|
544
|
-
// 如果visibility为0,则完全隐藏元素
|
545
560
|
style.display = 'none';
|
546
561
|
} else {
|
547
|
-
// 否则保持原有display属性,并设置visibility
|
548
562
|
style.display = data.display || '';
|
549
563
|
style.visibility = data.visibility === 1 ? 'visible' : 'hidden';
|
550
564
|
}
|
551
565
|
} else {
|
552
|
-
// 如果未设置visibility,保持原有display
|
553
566
|
style.display = data.display || '';
|
554
567
|
}
|
555
568
|
|
@@ -582,18 +595,6 @@ export default {
|
|
582
595
|
this.$emit('onDragEventStart', e);
|
583
596
|
|
584
597
|
// 只在编辑模式下阻止事件冒泡和默认行为
|
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
|
-
|
596
|
-
// 清除任何现有计时器
|
597
598
|
this.cancelLongPress();
|
598
599
|
// 记录起始触摸位置和索引
|
599
600
|
const touch = e.changedTouches[0];
|
@@ -603,11 +604,6 @@ export default {
|
|
603
604
|
this.longPressStartIndex = index;
|
604
605
|
this.isScrolling = false; // 重置滚动状态
|
605
606
|
|
606
|
-
// 添加临时事件监听器,在长按期间阻止滚动
|
607
|
-
document.addEventListener('touchmove', this.onTouchMove, {
|
608
|
-
passive: false // 设置为false以允许阻止默认行为
|
609
|
-
});
|
610
|
-
|
611
607
|
// 设置新的长按计时器
|
612
608
|
this.longPressTimer = setTimeout(() => {
|
613
609
|
// 只有在没有滚动的情况下才激活拖拽模式
|
@@ -671,13 +667,6 @@ export default {
|
|
671
667
|
this.draggingIndex = index;
|
672
668
|
this.isTouchDragging = true;
|
673
669
|
|
674
|
-
// 此时添加阻止默认行为的事件监听,但只影响拖拽状态下的行为
|
675
|
-
document.addEventListener('touchmove', this.onTouchMove, {
|
676
|
-
passive: false, // 只有在拖拽模式下才阻止默认行为
|
677
|
-
});
|
678
|
-
document.addEventListener('touchend', this.onTouchEnd, {
|
679
|
-
passive: true,
|
680
|
-
});
|
681
670
|
|
682
671
|
// 使用存储的固定宽高
|
683
672
|
this.draggedItemRect = {
|
@@ -696,11 +685,8 @@ export default {
|
|
696
685
|
// 设置克隆元素的初始位置
|
697
686
|
this.dragCloneX = rect.left;
|
698
687
|
this.dragCloneY = rect.top;
|
699
|
-
|
700
|
-
// 显示拖拽克隆
|
701
688
|
this.isDragClone = true;
|
702
689
|
|
703
|
-
// 立即执行一次定位以确保初始位置准确
|
704
690
|
requestAnimationFrame(() => {
|
705
691
|
this.dragCloneX = this.touchStartX - this.touchOffsetX;
|
706
692
|
this.dragCloneY = this.touchStartY - this.touchOffsetY;
|
@@ -732,7 +718,7 @@ export default {
|
|
732
718
|
if (e.oriEvent) {
|
733
719
|
e.oriEvent.preventDefault();
|
734
720
|
e.oriEvent.stopPropagation();
|
735
|
-
e.oriEvent.stopImmediatePropagation();
|
721
|
+
e.oriEvent.stopImmediatePropagation();
|
736
722
|
}
|
737
723
|
e.preventDefault && e.preventDefault();
|
738
724
|
e.stopPropagation && e.stopPropagation();
|
@@ -750,122 +736,72 @@ export default {
|
|
750
736
|
this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
|
751
737
|
}
|
752
738
|
|
753
|
-
|
754
|
-
|
755
|
-
|
739
|
+
// 简化的交换逻辑
|
740
|
+
this.handleSwapLogic(touch.pageX, touch.pageY);
|
741
|
+
},
|
742
|
+
|
743
|
+
// 简化的交换处理逻辑
|
744
|
+
handleSwapLogic(touchX, touchY) {
|
745
|
+
// 控制交换频率,避免过于频繁的交换
|
746
|
+
const now = Date.now();
|
747
|
+
if (now - this.lastSwapTime < 300) return;
|
756
748
|
|
757
749
|
// 获取所有网格元素
|
758
750
|
const gridItems = Array.from(document.querySelectorAll('.grid-item'));
|
751
|
+
let targetIndex = null;
|
759
752
|
|
760
|
-
|
761
|
-
let maxCenterScore = 0; // 用于记录最佳的中心区域得分
|
762
|
-
|
763
|
-
// ✅ 优化:精确坐标检测,偏向中心区域的元素
|
753
|
+
// 寻找触摸点所在的目标元素
|
764
754
|
for (const item of gridItems) {
|
765
755
|
const itemIndex = parseInt(item.getAttribute('data-index'), 10);
|
766
756
|
if (itemIndex === this.draggingIndex) continue;
|
767
757
|
|
768
758
|
const rect = item.getBoundingClientRect();
|
769
|
-
|
770
|
-
//
|
771
|
-
const
|
772
|
-
const
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
const centerY = (rect.top + rect.bottom) / 2;
|
786
|
-
const distanceX = Math.abs(touchX - centerX) / (rect.width / 2);
|
787
|
-
const distanceY = Math.abs(touchY - centerY) / (rect.height / 2);
|
788
|
-
|
789
|
-
// 0表示在中心,1表示在边缘
|
790
|
-
const distanceFromCenter = Math.max(
|
791
|
-
0,
|
792
|
-
Math.min(1, (distanceX + distanceY) / 2)
|
793
|
-
);
|
794
|
-
const centerScore = 1 - distanceFromCenter;
|
795
|
-
|
796
|
-
// 只有当分数更高且高于阈值时才更新bestTarget
|
797
|
-
// 提高阈值到0.35,确保更稳定的交换判断
|
798
|
-
if (centerScore > maxCenterScore && centerScore > 0.35) {
|
799
|
-
maxCenterScore = centerScore;
|
800
|
-
bestTarget = itemIndex;
|
801
|
-
}
|
802
|
-
}
|
803
|
-
}
|
804
|
-
|
805
|
-
// 设置悬停高亮,仅当有合格的目标时才高亮
|
806
|
-
this.hoverIndex = bestTarget;
|
807
|
-
|
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;
|
759
|
+
|
760
|
+
// 触摸点是否在元素的中心区域内
|
761
|
+
const centerX = (rect.left + rect.right) / 2;
|
762
|
+
const centerY = (rect.top + rect.bottom) / 2;
|
763
|
+
const halfWidth = rect.width * 0.4; // 缩小判断区域到40%,避免边缘误触
|
764
|
+
const halfHeight = rect.height * 0.4;
|
765
|
+
|
766
|
+
// 检查触摸点是否在元素的中心区域内
|
767
|
+
if (
|
768
|
+
touchX >= centerX - halfWidth &&
|
769
|
+
touchX <= centerX + halfWidth &&
|
770
|
+
touchY >= centerY - halfHeight &&
|
771
|
+
touchY <= centerY + halfHeight
|
772
|
+
) {
|
773
|
+
targetIndex = itemIndex;
|
774
|
+
break; // 找到第一个符合条件的就退出
|
819
775
|
}
|
820
|
-
} else if (bestTarget === null) {
|
821
|
-
// 如果没有目标,清除防抖状态
|
822
|
-
this.targetChangeTime = null;
|
823
|
-
this.pendingTarget = null;
|
824
776
|
}
|
825
777
|
|
826
|
-
//
|
827
|
-
|
778
|
+
// 设置悬停高亮
|
779
|
+
this.hoverIndex = targetIndex;
|
828
780
|
|
829
|
-
//
|
830
|
-
|
831
|
-
if (now - this.lastSwapTime < 250 || bestTarget === null) return;
|
832
|
-
|
833
|
-
// 执行交换逻辑
|
834
|
-
if (bestTarget !== this.draggingIndex) {
|
835
|
-
console.log(`交换元素: ${this.draggingIndex} 和 ${bestTarget}`);
|
836
|
-
|
837
|
-
// 使用ES6解构直接交换数组元素
|
781
|
+
// 执行交换
|
782
|
+
if (targetIndex !== null && targetIndex !== this.draggingIndex) {
|
838
783
|
[
|
839
|
-
this.
|
840
|
-
this.
|
784
|
+
this.data.list[this.draggingIndex],
|
785
|
+
this.data.list[targetIndex],
|
841
786
|
] = [
|
842
|
-
this.
|
843
|
-
this.processedList[this.draggingIndex],
|
844
|
-
];
|
845
|
-
|
846
|
-
// 同时更新原始数据,保持同步
|
847
|
-
[this.data.list[this.draggingIndex], this.data.list[bestTarget]] = [
|
848
|
-
this.data.list[bestTarget],
|
787
|
+
this.data.list[targetIndex],
|
849
788
|
this.data.list[this.draggingIndex],
|
850
789
|
];
|
790
|
+
console.log(`交换: ${this.draggingIndex} ↔ ${targetIndex}`);
|
851
791
|
|
852
792
|
// 更新拖拽索引
|
853
|
-
this.draggingIndex =
|
793
|
+
this.draggingIndex = targetIndex;
|
854
794
|
this.lastSwapTime = now;
|
855
795
|
}
|
856
796
|
},
|
857
797
|
|
858
|
-
//
|
798
|
+
// 平滑更新拖拽位置的方法
|
859
799
|
updateDragPosition() {
|
860
|
-
// 计算当前位置到目标位置的平滑过渡
|
861
800
|
const easing = 0.6; // 增加缓动系数,让拖拽更跟手
|
862
|
-
|
863
801
|
this.dragCloneX +=
|
864
802
|
(this.targetTouchX - this.touchOffsetX - this.dragCloneX) * easing;
|
865
803
|
this.dragCloneY +=
|
866
804
|
(this.targetTouchY - this.touchOffsetY - this.dragCloneY) * easing;
|
867
|
-
|
868
|
-
// 如果距离目标很近,直接设为目标位置以避免微小抖动
|
869
805
|
if (
|
870
806
|
Math.abs(this.dragCloneX - (this.targetTouchX - this.touchOffsetX)) <
|
871
807
|
0.5
|
@@ -876,8 +812,6 @@ export default {
|
|
876
812
|
0.5
|
877
813
|
)
|
878
814
|
this.dragCloneY = this.targetTouchY - this.touchOffsetY;
|
879
|
-
|
880
|
-
// 继续下一帧动画
|
881
815
|
this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
|
882
816
|
},
|
883
817
|
|
@@ -887,17 +821,8 @@ export default {
|
|
887
821
|
this.$emit('onDragEventEnd', e);
|
888
822
|
// 如果只是短按(还在计时),就取消长按
|
889
823
|
this.cancelLongPress();
|
890
|
-
|
891
824
|
// 如果正在拖拽中,确保清理状态
|
892
825
|
if (this.isTouchDragging) {
|
893
|
-
// 移除全局事件监听
|
894
|
-
document.removeEventListener('touchmove', this.onTouchMove, {
|
895
|
-
passive: false,
|
896
|
-
});
|
897
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
898
|
-
passive: false,
|
899
|
-
});
|
900
|
-
|
901
826
|
// 取消动画帧
|
902
827
|
if (this.animationFrameId) {
|
903
828
|
cancelAnimationFrame(this.animationFrameId);
|
@@ -908,28 +833,12 @@ export default {
|
|
908
833
|
this.isDragClone = false;
|
909
834
|
this.hoverIndex = null;
|
910
835
|
this.draggingIndex = null;
|
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;
|
918
836
|
}
|
919
837
|
},
|
920
838
|
|
921
839
|
// 触摸结束处理 - 全局事件
|
922
840
|
onTouchEnd(e) {
|
923
841
|
console.log('全局触摸结束');
|
924
|
-
|
925
|
-
// 移除全局事件监听
|
926
|
-
document.removeEventListener('touchmove', this.onTouchMove, {
|
927
|
-
passive: false,
|
928
|
-
});
|
929
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
930
|
-
passive: false,
|
931
|
-
});
|
932
|
-
|
933
842
|
// 取消动画帧
|
934
843
|
if (this.animationFrameId) {
|
935
844
|
cancelAnimationFrame(this.animationFrameId);
|
@@ -985,168 +894,27 @@ export default {
|
|
985
894
|
|
986
895
|
console.log(
|
987
896
|
'开始初始化Lottie动画,列表项数量:',
|
988
|
-
this.
|
897
|
+
this.data.list.length
|
989
898
|
);
|
990
899
|
|
991
900
|
// 延迟一点时间确保DOM完全更新并且之前的动画已被销毁
|
992
901
|
setTimeout(() => {
|
993
902
|
// 为每个列表项初始化动画
|
994
|
-
this.
|
903
|
+
this.data.list.forEach((item) => {
|
995
904
|
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
905
|
// 加载背景动画
|
1002
|
-
this.
|
1003
|
-
item.bgAnimView,
|
1004
|
-
`bgAnim-${itemId}`,
|
1005
|
-
`bg-${itemId}`,
|
1006
|
-
item
|
1007
|
-
);
|
906
|
+
this.createLottieAnimation('bg', itemId, item.bgAnimView, item);
|
1008
907
|
|
1009
908
|
// 加载加载中动画
|
1010
|
-
this.
|
1011
|
-
item.loadingAnimView,
|
1012
|
-
`loadingAnim-${itemId}`,
|
1013
|
-
`loading-${itemId}`,
|
1014
|
-
item
|
1015
|
-
);
|
909
|
+
this.createLottieAnimation('loading', itemId, item.loadingAnimView, item);
|
1016
910
|
|
1017
911
|
// 加载前景动画
|
1018
|
-
this.
|
1019
|
-
item.frontAnimView,
|
1020
|
-
`frontAnim-${itemId}`,
|
1021
|
-
`front-${itemId}`,
|
1022
|
-
item
|
1023
|
-
);
|
912
|
+
this.createLottieAnimation('front', itemId, item.frontAnimView, item);
|
1024
913
|
});
|
1025
914
|
}, 50); // 小延迟确保DOM已更新和旧动画已销毁
|
1026
915
|
});
|
1027
916
|
},
|
1028
917
|
|
1029
|
-
// 抽取公共的loadLottieAnimation方法
|
1030
|
-
loadLottieAnimation(animView, refName, animKey, item) {
|
1031
|
-
if (!animView || !animView.animUrl) {
|
1032
|
-
console.log(`[${animKey}] 跳过加载:animUrl不存在`);
|
1033
|
-
return;
|
1034
|
-
}
|
1035
|
-
|
1036
|
-
console.log(
|
1037
|
-
`[${animKey}] 尝试初始化${refName}, animUrl:`,
|
1038
|
-
animView.animUrl
|
1039
|
-
);
|
1040
|
-
|
1041
|
-
const animEl = this.$refs[refName];
|
1042
|
-
if (!animEl || !animEl[0]) {
|
1043
|
-
console.warn(`[${animKey}] 未找到${refName}的DOM引用`);
|
1044
|
-
return;
|
1045
|
-
}
|
1046
|
-
|
1047
|
-
try {
|
1048
|
-
// 如果已经有实例,先暂停它
|
1049
|
-
if (this.lottieAnimations[animKey]) {
|
1050
|
-
this.lottieAnimations[animKey].pause();
|
1051
|
-
}
|
1052
|
-
|
1053
|
-
// 确保容器可见且透明度重置
|
1054
|
-
animEl[0].style.display = '';
|
1055
|
-
animEl[0].style.opacity = '1';
|
1056
|
-
animEl[0].style.transition = 'opacity 0.2s';
|
1057
|
-
|
1058
|
-
if (
|
1059
|
-
typeof animView.visibility !== 'number' ||
|
1060
|
-
animView.visibility !== 0
|
1061
|
-
) {
|
1062
|
-
// 延迟一帧再销毁旧实例
|
1063
|
-
requestAnimationFrame(() => {
|
1064
|
-
if (this.lottieAnimations[animKey]) {
|
1065
|
-
this.lottieAnimations[animKey].destroy();
|
1066
|
-
delete this.lottieAnimations[animKey];
|
1067
|
-
}
|
1068
|
-
|
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 });
|
1139
|
-
}
|
1140
|
-
}
|
1141
|
-
}
|
1142
|
-
});
|
1143
|
-
});
|
1144
|
-
}
|
1145
|
-
} catch (error) {
|
1146
|
-
console.error(`[${animKey}] 初始化失败:`, error);
|
1147
|
-
}
|
1148
|
-
},
|
1149
|
-
|
1150
918
|
// 添加新方法用于获取和存储固定宽高
|
1151
919
|
updateFixedItemSize() {
|
1152
920
|
this.$nextTick(() => {
|
@@ -1168,48 +936,30 @@ export default {
|
|
1168
936
|
},
|
1169
937
|
},
|
1170
938
|
watch: {
|
1171
|
-
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1176
|
-
}
|
1177
|
-
|
1178
|
-
deep: true,
|
939
|
+
'data.isEditing':{
|
940
|
+
handler(newVal){
|
941
|
+
if(!newVal){
|
942
|
+
this.$set(this.data, 'list', this.data.list);
|
943
|
+
}
|
944
|
+
},
|
945
|
+
deep:true
|
1179
946
|
},
|
1180
947
|
// 监听列表数据变化,自动更新处理后的数据
|
1181
948
|
'data.list': {
|
1182
949
|
handler(newList, oldList) {
|
1183
|
-
|
950
|
+
// 防止死循环
|
951
|
+
if (this.isProcessingData) {
|
952
|
+
return;
|
953
|
+
}
|
954
|
+
|
955
|
+
console.log('检测到data.list变化');
|
956
|
+
this.generateDataList();
|
1184
957
|
this.$nextTick(() => {
|
1185
|
-
|
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
|
-
});
|
958
|
+
this.initLottieAnimations();
|
1199
959
|
});
|
1200
960
|
},
|
1201
961
|
deep: true,
|
1202
962
|
},
|
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
|
-
},
|
1213
963
|
},
|
1214
964
|
mounted() {
|
1215
965
|
console.log(this.data, 'djdjdjdjdjdjdj');
|
@@ -1217,61 +967,21 @@ export default {
|
|
1217
967
|
this.updateGridBoxWidth();
|
1218
968
|
// 获取并存储固定宽高
|
1219
969
|
this.updateFixedItemSize();
|
1220
|
-
|
1221
|
-
// 监听窗口大小变化,更新grid-box宽度
|
1222
|
-
window.addEventListener('resize', () => {
|
1223
|
-
this.updateGridBoxWidth();
|
1224
|
-
this.updateFixedItemSize();
|
1225
|
-
});
|
1226
|
-
|
1227
970
|
// 初始化Lottie动画
|
1228
971
|
this.initLottieAnimations();
|
1229
972
|
},
|
1230
973
|
beforeDestroy() {
|
1231
974
|
// 确保清理所有事件监听器
|
1232
975
|
this.cancelLongPress();
|
1233
|
-
document.removeEventListener('touchmove', this.onTouchMove, {
|
1234
|
-
passive: false,
|
1235
|
-
});
|
1236
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
1237
|
-
passive: false,
|
1238
|
-
});
|
1239
|
-
window.removeEventListener('resize', () => {
|
1240
|
-
this.updateGridBoxWidth();
|
1241
|
-
this.updateFixedItemSize();
|
1242
|
-
});
|
1243
|
-
|
1244
976
|
// 清理动画帧
|
1245
977
|
if (this.animationFrameId) {
|
1246
978
|
cancelAnimationFrame(this.animationFrameId);
|
1247
979
|
this.animationFrameId = null;
|
1248
980
|
}
|
1249
|
-
|
1250
|
-
// 清理所有Lottie动画实例
|
1251
|
-
Object.keys(this.lottieAnimations).forEach((key) => {
|
1252
|
-
const anim = this.lottieAnimations[key];
|
1253
|
-
if (anim && typeof anim.destroy === 'function') {
|
1254
|
-
console.log(`组件销毁时清理动画: ${key}`);
|
1255
|
-
try {
|
1256
|
-
anim.removeEventListener('complete');
|
1257
|
-
anim.removeEventListener('DOMLoaded');
|
1258
|
-
anim.removeEventListener('data_ready');
|
1259
|
-
anim.removeEventListener('loaded_images');
|
1260
|
-
anim.removeEventListener('loopComplete');
|
1261
|
-
} catch (e) {
|
1262
|
-
console.warn(`移除事件监听器失败: ${e.message}`);
|
1263
|
-
}
|
1264
|
-
anim.destroy();
|
1265
|
-
}
|
1266
|
-
delete this.lottieAnimations[key];
|
1267
|
-
});
|
1268
|
-
|
1269
|
-
// 确保动画实例对象被清空
|
1270
|
-
this.lottieAnimations = {};
|
1271
981
|
},
|
1272
982
|
created() {
|
1273
983
|
// 初始化处理后的数据列表
|
1274
|
-
this.
|
984
|
+
this.generateDataList();
|
1275
985
|
},
|
1276
986
|
};
|
1277
987
|
</script>
|
@@ -1302,7 +1012,7 @@ button {
|
|
1302
1012
|
display: grid;
|
1303
1013
|
grid-template-columns: repeat(var(--grid-cols), 1fr);
|
1304
1014
|
grid-gap: var(--grid-gap);
|
1305
|
-
width: calc(100% -
|
1015
|
+
width: calc(100% - var(--grid-margin));
|
1306
1016
|
}
|
1307
1017
|
|
1308
1018
|
/* 优化排序过渡效果 */
|