@dolphinweex/weex-harmony 0.1.27 → 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.
@@ -4,140 +4,147 @@
|
|
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
|
-
>
|
7
|
+
<div class="items-container">
|
8
|
+
<div class="grid-items-wrapper">
|
9
|
+
<transition-group name="grid-fade" tag="div" class="grid-inner-wrapper">
|
28
10
|
<div
|
29
|
-
v-
|
30
|
-
|
31
|
-
|
32
|
-
:
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
item.
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
:
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
class="grid-box"
|
47
|
-
:class="{ shaking: data.isEditing }"
|
48
|
-
@click.stop="(e) => itemViewClick(e, item, index)"
|
49
|
-
|
11
|
+
v-for="(item, index) in data.list"
|
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
|
<!-- 拖拽克隆元素 -->
|
110
117
|
<div v-if="isDragClone" class="drag-clone" :style="dragCloneStyle">
|
111
118
|
<div
|
112
119
|
class="grid-box"
|
113
|
-
:style="applyNormalAttribute(
|
120
|
+
:style="applyNormalAttribute(data.list[draggingIndex].itemView)"
|
114
121
|
>
|
115
122
|
<div class="flexBox">
|
116
123
|
<img
|
117
|
-
:src="
|
124
|
+
:src="data.list[draggingIndex].iconImage.url"
|
118
125
|
:style="
|
119
|
-
applyNormalAttribute(
|
126
|
+
applyNormalAttribute(data.list[draggingIndex].iconImage)
|
120
127
|
"
|
121
128
|
/>
|
122
129
|
<div @click.stop.prevent>
|
123
130
|
<img
|
124
131
|
class="isSelected"
|
125
132
|
:src="
|
126
|
-
|
127
|
-
?
|
128
|
-
:
|
133
|
+
data.list[draggingIndex].itemView.isSelected
|
134
|
+
? data.list[draggingIndex].selectIcon.selectedUrl
|
135
|
+
: data.list[draggingIndex].selectIcon.url
|
129
136
|
"
|
130
137
|
:style="
|
131
|
-
applyNormalAttribute(
|
138
|
+
applyNormalAttribute(data.list[draggingIndex].selectIcon)
|
132
139
|
"
|
133
140
|
/>
|
134
141
|
</div>
|
135
142
|
</div>
|
136
143
|
<div
|
137
144
|
class="itemText"
|
138
|
-
:style="[applyNormalAttribute(
|
145
|
+
:style="[applyNormalAttribute(data.list[draggingIndex].text)]"
|
139
146
|
>
|
140
|
-
{{
|
147
|
+
{{ data.list[draggingIndex].text.text }}
|
141
148
|
</div>
|
142
149
|
</div>
|
143
150
|
</div>
|
@@ -166,10 +173,9 @@ export default {
|
|
166
173
|
return {
|
167
174
|
targetTouchX: 0,
|
168
175
|
targetTouchY: 0,
|
169
|
-
processedList: [],
|
170
176
|
selected: null,
|
171
177
|
longPressTimer: null,
|
172
|
-
longPressDuration:
|
178
|
+
longPressDuration: 350,
|
173
179
|
longPressStartIndex: null,
|
174
180
|
draggingIndex: null,
|
175
181
|
hoverIndex: null,
|
@@ -178,7 +184,6 @@ export default {
|
|
178
184
|
isAnimating: false,
|
179
185
|
touchStartX: 0,
|
180
186
|
touchStartY: 0,
|
181
|
-
touchStartTime: 0, // 记录触摸开始时间,用于计算滑动速度
|
182
187
|
draggedItemRect: null,
|
183
188
|
isDragClone: false,
|
184
189
|
dragCloneX: 0,
|
@@ -188,17 +193,11 @@ export default {
|
|
188
193
|
gridBoxWidth: 0,
|
189
194
|
lottieAnimations: {},
|
190
195
|
isScrolling: false,
|
191
|
-
scrollThreshold:
|
196
|
+
scrollThreshold: 5,
|
192
197
|
fixedItemWidth: 0,
|
193
198
|
fixedItemHeight: 0,
|
194
199
|
animationFrameId: null,
|
195
|
-
|
196
|
-
targetChangeTime: null,
|
197
|
-
pendingTarget: null,
|
198
|
-
weexListInnerEl: null,
|
199
|
-
weexRefreshEl: null,
|
200
|
-
originListInnerStyle: null,
|
201
|
-
originRefreshStyle: null,
|
200
|
+
isProcessingData: false,
|
202
201
|
};
|
203
202
|
},
|
204
203
|
computed: {
|
@@ -206,9 +205,7 @@ export default {
|
|
206
205
|
return {
|
207
206
|
'--grid-cols': this.data.spanCount || this.cols,
|
208
207
|
'--grid-gap': `${this.data.spaceSize || 8}px`,
|
209
|
-
'--grid-margin': `${
|
210
|
-
this.data.layoutConfig.marginStart + this.data.layoutConfig.marginEnd
|
211
|
-
}px`,
|
208
|
+
'--grid-margin': `${(this.data.layoutConfig.marginStart ) + (this.data.layoutConfig.marginEnd )}px`,
|
212
209
|
marginTop: this.data.layoutConfig.marginTop * scla,
|
213
210
|
marginBottom: this.data.layoutConfig.marginBottom * scla,
|
214
211
|
marginLeft: this.data.layoutConfig.marginStart * scla,
|
@@ -221,7 +218,7 @@ export default {
|
|
221
218
|
return { display: 'none' };
|
222
219
|
}
|
223
220
|
|
224
|
-
const item = this.
|
221
|
+
const item = this.data.list[this.draggingIndex];
|
225
222
|
let itemView = {};
|
226
223
|
if (item) {
|
227
224
|
itemView = this.applyNormalAttribute(item.itemView);
|
@@ -246,65 +243,107 @@ export default {
|
|
246
243
|
updateListItem(data) {
|
247
244
|
console.log('updateListItem收到数据:', JSON.stringify(data));
|
248
245
|
const id = data.itemView.id;
|
249
|
-
const index = this.
|
250
|
-
(item) => item.itemView.id == id
|
251
|
-
);
|
252
|
-
const dataIndex = this.data.list.findIndex(
|
246
|
+
const index = this.data.list.findIndex(
|
253
247
|
(item) => item.itemView.id == id
|
254
248
|
);
|
255
|
-
|
256
|
-
if(dataIndex !== -1) {
|
257
|
-
// 使用Vue的$set确保响应式更新
|
258
|
-
this.$set(this.data.list, dataIndex, JSON.parse(JSON.stringify(data)));
|
259
|
-
}
|
249
|
+
|
260
250
|
if (index === -1) {
|
261
251
|
return;
|
262
252
|
}
|
253
|
+
|
254
|
+
// 检查是否有动画URL的变化
|
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 || '';
|
263
258
|
const newBgAnimUrl = data.bgAnimView.animUrl || '';
|
264
259
|
const newLoadingAnimUrl = data.loadingAnimView.animUrl || '';
|
265
260
|
const newFrontAnimUrl = data.frontAnimView.animUrl || '';
|
266
|
-
// 同时更新processedList和原始data.list数据
|
267
|
-
this.processedList[index] = data;
|
268
|
-
this.processedList = [...this.processedList];
|
269
261
|
|
262
|
+
// 合并全局数据到新数据
|
263
|
+
const mergedData = this.mergeItemData(data);
|
264
|
+
|
265
|
+
// 更新数据
|
266
|
+
this.$set(this.data.list, index, mergedData);
|
270
267
|
|
271
|
-
// 无论是否有URL变化,都处理动画更新
|
272
|
-
// 这样确保任何visibility或其他属性变化也能被处理
|
273
268
|
this.$nextTick(() => {
|
274
|
-
// 清理旧的动画实例
|
275
269
|
const itemId = String(data.itemView.id);
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
`bgAnim-${itemId}`,
|
281
|
-
`bg-${itemId}`,
|
282
|
-
data,
|
283
|
-
dataIndex
|
284
|
-
);
|
270
|
+
|
271
|
+
// 处理背景动画
|
272
|
+
if (oldBgAnimUrl !== newBgAnimUrl && newBgAnimUrl) {
|
273
|
+
this.createLottieAnimation('bg', itemId, mergedData.bgAnimView, mergedData);
|
285
274
|
}
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
`loading-${itemId}`,
|
291
|
-
data,
|
292
|
-
dataIndex
|
293
|
-
);
|
275
|
+
|
276
|
+
// 处理加载动画
|
277
|
+
if (oldLoadingAnimUrl !== newLoadingAnimUrl && newLoadingAnimUrl) {
|
278
|
+
this.createLottieAnimation('loading', itemId, mergedData.loadingAnimView, mergedData);
|
294
279
|
}
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
`front-${itemId}`,
|
300
|
-
data,
|
301
|
-
dataIndex
|
302
|
-
);
|
280
|
+
|
281
|
+
// 处理前景动画
|
282
|
+
if (oldFrontAnimUrl !== newFrontAnimUrl && newFrontAnimUrl) {
|
283
|
+
this.createLottieAnimation('front', itemId, mergedData.frontAnimView, mergedData);
|
303
284
|
}
|
304
285
|
});
|
305
286
|
},
|
287
|
+
|
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
|
+
}
|
298
|
+
|
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
|
+
}
|
344
|
+
},
|
306
345
|
getListData(callback) {
|
307
|
-
let data = Array.from(this.
|
346
|
+
let data = Array.from(this.data.list);
|
308
347
|
data = data.map((item) => ({
|
309
348
|
...item,
|
310
349
|
itemView: {
|
@@ -315,6 +354,7 @@ export default {
|
|
315
354
|
callback({ listData: data });
|
316
355
|
},
|
317
356
|
rightIconClick(e) {
|
357
|
+
console.log('rightIconClick执行,阻止事件冒泡');
|
318
358
|
e.preventDefault();
|
319
359
|
e.stopPropagation();
|
320
360
|
this.$emit('onClickRightIcon', e);
|
@@ -328,8 +368,10 @@ export default {
|
|
328
368
|
},
|
329
369
|
selectIconClick(e, item, index) {
|
330
370
|
e.preventDefault();
|
331
|
-
e.stopPropagation();
|
332
|
-
item.
|
371
|
+
e.stopPropagation();
|
372
|
+
if (item.selectIcon.visibility) {
|
373
|
+
item.itemView.isSelected = !item.itemView.isSelected;
|
374
|
+
}
|
333
375
|
const target = {
|
334
376
|
...item.itemView,
|
335
377
|
id: String(item.itemView.id),
|
@@ -342,19 +384,21 @@ export default {
|
|
342
384
|
},
|
343
385
|
|
344
386
|
itemViewClick(e, item, index) {
|
345
|
-
// 如果事件已经被处理或被阻止默认行为,则不继续执行
|
346
387
|
if (e.defaultPrevented) {
|
347
388
|
return;
|
348
389
|
}
|
349
|
-
|
350
|
-
|
390
|
+
|
391
|
+
console.log('itemViewClick');
|
351
392
|
// 确保取消任何长按计时器
|
352
393
|
this.cancelLongPress();
|
353
394
|
if (this.data.isEditing) {
|
354
|
-
item.
|
355
|
-
|
395
|
+
if (item.selectIcon.visibility) {
|
396
|
+
item.itemView.isSelected = !item.itemView.isSelected;
|
397
|
+
}
|
356
398
|
return;
|
357
399
|
}
|
400
|
+
e.preventDefault();
|
401
|
+
e.stopPropagation();
|
358
402
|
const target = {
|
359
403
|
...item.itemView,
|
360
404
|
id: String(item.itemView.id),
|
@@ -365,8 +409,13 @@ export default {
|
|
365
409
|
},
|
366
410
|
|
367
411
|
actionClick(e, item, index) {
|
412
|
+
console.log('actionClick执行,阻止事件冒泡');
|
368
413
|
e.preventDefault();
|
369
414
|
e.stopPropagation(); // 阻止事件冒泡到itemViewClick
|
415
|
+
|
416
|
+
// 在触发事件前,先清理所有其他项目的动画实例
|
417
|
+
this.cleanupOtherAnimations(item.itemView.id);
|
418
|
+
|
370
419
|
const target = {
|
371
420
|
...item.itemView,
|
372
421
|
id: String(item.itemView.id),
|
@@ -377,40 +426,108 @@ export default {
|
|
377
426
|
return false;
|
378
427
|
},
|
379
428
|
|
380
|
-
|
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
|
+
const anim = this.lottieAnimations[key];
|
436
|
+
|
437
|
+
// 不要立即销毁,而是先停止动画
|
438
|
+
anim.pause();
|
439
|
+
|
440
|
+
// 使用 setTimeout 延迟销毁,给新动画一个启动的时间
|
441
|
+
setTimeout(() => {
|
442
|
+
try {
|
443
|
+
if (this.lottieAnimations[key] === anim) {
|
444
|
+
anim.removeEventListener('complete');
|
445
|
+
anim.destroy();
|
446
|
+
delete this.lottieAnimations[key];
|
447
|
+
|
448
|
+
// 提取项目ID和动画类型
|
449
|
+
const parts = key.split('-');
|
450
|
+
if (parts.length === 2) {
|
451
|
+
const animType = parts[0];
|
452
|
+
const itemId = parts[1];
|
453
|
+
|
454
|
+
// 延迟隐藏对应的动画容器
|
455
|
+
this.$nextTick(() => {
|
456
|
+
const refName = `${animType}Anim-${itemId}`;
|
457
|
+
const animContainer =
|
458
|
+
this.$refs[refName] && this.$refs[refName][0];
|
459
|
+
if (animContainer) {
|
460
|
+
// 使用 opacity 过渡而不是直接设置 display: none
|
461
|
+
animContainer.style.opacity = '0';
|
462
|
+
animContainer.style.transition = 'opacity 0.2s';
|
463
|
+
setTimeout(() => {
|
464
|
+
animContainer.style.display = 'none';
|
465
|
+
}, 200);
|
466
|
+
}
|
467
|
+
});
|
468
|
+
}
|
469
|
+
}
|
470
|
+
} catch (e) {
|
471
|
+
console.warn(`清理动画失败: ${e.message}`);
|
472
|
+
}
|
473
|
+
}, 50);
|
474
|
+
}
|
475
|
+
}
|
476
|
+
});
|
477
|
+
},
|
478
|
+
|
381
479
|
generateDataList() {
|
382
480
|
if (!this.data || !this.data.list || !this.data.globalData) {
|
383
|
-
return
|
481
|
+
return;
|
384
482
|
}
|
385
483
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
'text',
|
393
|
-
'actionButton',
|
394
|
-
'rightIcon',
|
395
|
-
'leftTopIcon',
|
396
|
-
'iconImage',
|
397
|
-
'selectIcon',
|
398
|
-
'loadingAnimView',
|
399
|
-
'bgAnimView',
|
400
|
-
'frontAnimView',
|
401
|
-
];
|
484
|
+
// 防止死循环
|
485
|
+
if (this.isProcessingData) {
|
486
|
+
return;
|
487
|
+
}
|
488
|
+
|
489
|
+
this.isProcessingData = true;
|
402
490
|
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
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;
|
407
501
|
});
|
502
|
+
}
|
503
|
+
},
|
408
504
|
|
409
|
-
|
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);
|
410
523
|
});
|
524
|
+
|
525
|
+
return processedItem;
|
411
526
|
},
|
527
|
+
|
412
528
|
getItemStyle(item, type) {
|
413
529
|
const result = this.mergeDate(item[type] || {}, type);
|
530
|
+
console.log(type, this.applyNormalAttribute(result), 'ggg');
|
414
531
|
return this.applyNormalAttribute(result);
|
415
532
|
},
|
416
533
|
mergeDate(target, type) {
|
@@ -438,18 +555,14 @@ export default {
|
|
438
555
|
borderRadius: `${(data.cornerRadius || 0) * scla}px`,
|
439
556
|
opacity: data.alpha || 1,
|
440
557
|
};
|
441
|
-
// 特殊处理visibility和display
|
442
558
|
if (typeof data.visibility === 'number') {
|
443
559
|
if (data.visibility === 0) {
|
444
|
-
// 如果visibility为0,则完全隐藏元素
|
445
560
|
style.display = 'none';
|
446
561
|
} else {
|
447
|
-
// 否则保持原有display属性,并设置visibility
|
448
562
|
style.display = data.display || '';
|
449
563
|
style.visibility = data.visibility === 1 ? 'visible' : 'hidden';
|
450
564
|
}
|
451
565
|
} else {
|
452
|
-
// 如果未设置visibility,保持原有display
|
453
566
|
style.display = data.display || '';
|
454
567
|
}
|
455
568
|
|
@@ -476,44 +589,26 @@ export default {
|
|
476
589
|
},
|
477
590
|
// 开始长按
|
478
591
|
startLongPress(e, index) {
|
479
|
-
console.log(e, 'cdj-------startLongPress');
|
480
592
|
if (!this.data.isEditable) {
|
481
593
|
return;
|
482
594
|
}
|
483
|
-
|
484
|
-
|
485
|
-
//
|
486
|
-
|
487
|
-
// 清除任何现有计时器
|
595
|
+
this.$emit('onDragEventStart', e);
|
596
|
+
|
597
|
+
// 只在编辑模式下阻止事件冒泡和默认行为
|
488
598
|
this.cancelLongPress();
|
489
|
-
|
490
599
|
// 记录起始触摸位置和索引
|
491
600
|
const touch = e.changedTouches[0];
|
492
601
|
this.touchStartX = touch.pageX;
|
493
602
|
this.touchStartY = touch.pageY;
|
603
|
+
console.log('开始长按ddddddddd', touch, touch.pageY, touch.pageX);
|
494
604
|
this.longPressStartIndex = index;
|
495
605
|
this.isScrolling = false; // 重置滚动状态
|
496
606
|
|
497
|
-
//
|
498
|
-
this.touchStartTime = Date.now();
|
499
|
-
|
500
|
-
if (this.isTouchDragging) {
|
501
|
-
this.$emit('drag-end', {
|
502
|
-
items: [...this.itemsList],
|
503
|
-
});
|
504
|
-
}
|
505
|
-
|
506
|
-
// 通知外部触摸开始事件
|
507
|
-
this.$emit('onDragEventStart', e);
|
508
|
-
|
509
|
-
// 设置新的长按计时器 - 延长一点时间避免误触发
|
607
|
+
// 设置新的长按计时器
|
510
608
|
this.longPressTimer = setTimeout(() => {
|
511
609
|
// 只有在没有滚动的情况下才激活拖拽模式
|
512
610
|
if (!this.isScrolling) {
|
513
|
-
console.log('长按时间到,激活拖拽模式');
|
514
611
|
this.activateDragMode(index);
|
515
|
-
} else {
|
516
|
-
console.log('检测到滚动,取消拖拽激活');
|
517
612
|
}
|
518
613
|
}, this.longPressDuration);
|
519
614
|
},
|
@@ -525,109 +620,83 @@ export default {
|
|
525
620
|
}
|
526
621
|
|
527
622
|
const touch = e.changedTouches[0];
|
623
|
+
const moveX = Math.abs(touch.pageX - this.touchStartX);
|
624
|
+
const moveY = Math.abs(touch.pageY - this.touchStartY);
|
625
|
+
|
626
|
+
// 如果水平移动大于垂直移动,则阻止事件传播
|
627
|
+
if (moveX > moveY &&!this.isTouchDragging && this.data.isEditing) {
|
628
|
+
if (e.oriEvent) {
|
629
|
+
e.oriEvent.preventDefault();
|
630
|
+
e.oriEvent.stopPropagation();
|
631
|
+
e.oriEvent.stopImmediatePropagation();
|
632
|
+
e.preventDefault && e.preventDefault();
|
633
|
+
e.stopPropagation && e.stopPropagation();
|
634
|
+
e.stopImmediatePropagation && e.stopImmediatePropagation();
|
635
|
+
}
|
636
|
+
}
|
637
|
+
|
528
638
|
// 如果已经在拖拽模式,交给onTouchMove处理
|
529
639
|
if (this.isTouchDragging) {
|
530
640
|
this.onTouchMove(e);
|
531
641
|
return;
|
532
642
|
}
|
533
643
|
|
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 });
|
644
|
+
// 检测是否在滚动
|
645
|
+
if (moveY > this.scrollThreshold * 3) {
|
646
|
+
// 增加滚动阈值以更容易识别滚动意图
|
555
647
|
this.isScrolling = true;
|
556
648
|
this.cancelLongPress();
|
557
649
|
return;
|
558
650
|
}
|
559
651
|
|
560
|
-
// 如果移动超过阈值,取消长按
|
652
|
+
// 如果移动超过阈值,取消长按
|
561
653
|
if (moveX > this.scrollThreshold || moveY > this.scrollThreshold) {
|
562
654
|
this.cancelLongPress();
|
563
|
-
return;
|
564
655
|
}
|
565
656
|
|
566
|
-
|
567
|
-
if (this.longPressTimer && !this.isScrolling) {
|
568
|
-
e.oriEvent.preventDefault();
|
569
|
-
}
|
657
|
+
return false;
|
570
658
|
},
|
571
659
|
|
572
660
|
// 激活拖拽模式
|
573
661
|
activateDragMode(index) {
|
574
662
|
console.log('长按触发,进入编辑模式');
|
663
|
+
// 进入编辑模式
|
575
664
|
|
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
|
-
});
|
665
|
+
if(this.data.isEditing){
|
666
|
+
// 设置当前拖拽索引
|
667
|
+
this.draggingIndex = index;
|
668
|
+
this.isTouchDragging = true;
|
620
669
|
|
621
|
-
// 立即执行一次定位以确保初始位置准确
|
622
|
-
requestAnimationFrame(() => {
|
623
|
-
this.dragCloneX = this.touchStartX - this.touchOffsetX;
|
624
|
-
this.dragCloneY = this.touchStartY - this.touchOffsetY;
|
625
|
-
});
|
626
670
|
|
627
|
-
|
628
|
-
|
629
|
-
|
671
|
+
// 使用存储的固定宽高
|
672
|
+
this.draggedItemRect = {
|
673
|
+
width: this.fixedItemWidth,
|
674
|
+
height: this.fixedItemHeight,
|
675
|
+
};
|
630
676
|
|
677
|
+
// 获取当前元素位置用于计算偏移
|
678
|
+
const element = document.querySelectorAll('.grid-item')[index];
|
679
|
+
if (element) {
|
680
|
+
const rect = element.getBoundingClientRect();
|
681
|
+
// 计算触摸点相对于元素左上角的偏移
|
682
|
+
this.touchOffsetX = this.touchStartX - rect.left;
|
683
|
+
this.touchOffsetY = this.touchStartY - rect.top;
|
684
|
+
|
685
|
+
// 设置克隆元素的初始位置
|
686
|
+
this.dragCloneX = rect.left;
|
687
|
+
this.dragCloneY = rect.top;
|
688
|
+
this.isDragClone = true;
|
689
|
+
|
690
|
+
requestAnimationFrame(() => {
|
691
|
+
this.dragCloneX = this.touchStartX - this.touchOffsetX;
|
692
|
+
this.dragCloneY = this.touchStartY - this.touchOffsetY;
|
693
|
+
});
|
694
|
+
|
695
|
+
console.log('拖拽克隆已创建');
|
696
|
+
}
|
697
|
+
}
|
698
|
+
this.data.isEditing = true;
|
699
|
+
this.$emit('onEditStateChanged', true);
|
631
700
|
this.longPressTimer = null;
|
632
701
|
},
|
633
702
|
|
@@ -638,45 +707,24 @@ export default {
|
|
638
707
|
this.longPressTimer = null;
|
639
708
|
}
|
640
709
|
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
710
|
},
|
661
711
|
|
662
712
|
// 触摸移动处理
|
663
713
|
onTouchMove(e) {
|
664
714
|
if (!this.isTouchDragging) return;
|
665
715
|
|
666
|
-
//
|
667
|
-
if (this.isTouchDragging) {
|
668
|
-
e.oriEvent
|
669
|
-
|
716
|
+
// 确保在拖拽模式下阻止所有事件传播
|
717
|
+
if (this.isTouchDragging && this.data.isEditing) {
|
718
|
+
if (e.oriEvent) {
|
719
|
+
e.oriEvent.preventDefault();
|
720
|
+
e.oriEvent.stopPropagation();
|
721
|
+
e.oriEvent.stopImmediatePropagation();
|
722
|
+
}
|
723
|
+
e.preventDefault && e.preventDefault();
|
724
|
+
e.stopPropagation && e.stopPropagation();
|
725
|
+
e.stopImmediatePropagation && e.stopImmediatePropagation();
|
670
726
|
}
|
671
727
|
|
672
|
-
this.handleTouchMove(e);
|
673
|
-
},
|
674
|
-
|
675
|
-
// 抽取通用的触摸移动处理逻辑
|
676
|
-
handleTouchMove(e) {
|
677
|
-
console.log(this.isTouchDragging, 'this.isTouchDragging111111');
|
678
|
-
if (!this.isTouchDragging) return;
|
679
|
-
|
680
728
|
const touch = e.changedTouches[0];
|
681
729
|
|
682
730
|
// 更新目标位置而非直接设置
|
@@ -688,113 +736,72 @@ export default {
|
|
688
736
|
this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
|
689
737
|
}
|
690
738
|
|
691
|
-
|
692
|
-
|
693
|
-
|
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;
|
694
748
|
|
695
749
|
// 获取所有网格元素
|
696
750
|
const gridItems = Array.from(document.querySelectorAll('.grid-item'));
|
751
|
+
let targetIndex = null;
|
697
752
|
|
698
|
-
|
699
|
-
let maxCenterScore = 0; // 用于记录最佳的中心区域得分
|
700
|
-
|
701
|
-
// ✅ 优化:精确坐标检测,偏向中心区域的元素
|
753
|
+
// 寻找触摸点所在的目标元素
|
702
754
|
for (const item of gridItems) {
|
703
755
|
const itemIndex = parseInt(item.getAttribute('data-index'), 10);
|
704
756
|
if (itemIndex === this.draggingIndex) continue;
|
705
757
|
|
706
758
|
const rect = item.getBoundingClientRect();
|
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;
|
707
765
|
|
708
|
-
//
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
touchY <= rect.bottom;
|
718
|
-
|
719
|
-
// 只有当触摸点足够深入时才考虑此元素作为交换目标
|
720
|
-
if (isDeepEnough) {
|
721
|
-
// 计算触摸点到元素中心的距离比例(越接近中心,值越大)
|
722
|
-
const centerX = (rect.left + rect.right) / 2;
|
723
|
-
const centerY = (rect.top + rect.bottom) / 2;
|
724
|
-
const distanceX = Math.abs(touchX - centerX) / (rect.width / 2);
|
725
|
-
const distanceY = Math.abs(touchY - centerY) / (rect.height / 2);
|
726
|
-
|
727
|
-
// 0表示在中心,1表示在边缘
|
728
|
-
const distanceFromCenter = Math.max(
|
729
|
-
0,
|
730
|
-
Math.min(1, (distanceX + distanceY) / 2)
|
731
|
-
);
|
732
|
-
const centerScore = 1 - distanceFromCenter;
|
733
|
-
|
734
|
-
// 放宽元素中心区域的判定,让交换更容易触发
|
735
|
-
// 降低阈值,让交换更敏感
|
736
|
-
if (centerScore > maxCenterScore && centerScore > 0.2) {
|
737
|
-
maxCenterScore = centerScore;
|
738
|
-
bestTarget = itemIndex;
|
739
|
-
}
|
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; // 找到第一个符合条件的就退出
|
740
775
|
}
|
741
776
|
}
|
742
777
|
|
743
|
-
//
|
744
|
-
this.hoverIndex =
|
745
|
-
|
746
|
-
// 简化交换防抖动处理,让交换更直接响应
|
747
|
-
if (bestTarget !== null) {
|
748
|
-
// 直接使用当前的最佳目标
|
749
|
-
this.lastBestTarget = bestTarget;
|
750
|
-
} else {
|
751
|
-
// 如果没有目标,清除lastBestTarget
|
752
|
-
this.lastBestTarget = null;
|
753
|
-
}
|
754
|
-
|
755
|
-
// 使用已稳定的目标进行交换
|
756
|
-
bestTarget = this.lastBestTarget;
|
778
|
+
// 设置悬停高亮
|
779
|
+
this.hoverIndex = targetIndex;
|
757
780
|
|
758
|
-
//
|
759
|
-
|
760
|
-
if (now - this.lastSwapTime < 150 || bestTarget === null) return;
|
761
|
-
|
762
|
-
// 执行交换逻辑
|
763
|
-
if (bestTarget !== this.draggingIndex) {
|
781
|
+
// 执行交换
|
782
|
+
if (targetIndex !== null && targetIndex !== this.draggingIndex) {
|
764
783
|
[
|
765
|
-
this.
|
766
|
-
this.
|
784
|
+
this.data.list[this.draggingIndex],
|
785
|
+
this.data.list[targetIndex],
|
767
786
|
] = [
|
768
|
-
this.
|
769
|
-
this.processedList[this.draggingIndex],
|
770
|
-
];
|
771
|
-
[this.data.list[this.draggingIndex], this.data.list[bestTarget]] = [
|
772
|
-
this.data.list[bestTarget],
|
787
|
+
this.data.list[targetIndex],
|
773
788
|
this.data.list[this.draggingIndex],
|
774
789
|
];
|
790
|
+
console.log(`交换: ${this.draggingIndex} ↔ ${targetIndex}`);
|
775
791
|
|
776
|
-
console.log(this.data.list, 'cdj------this.data.list');
|
777
|
-
|
778
|
-
// 更新processedList以触发视图更新和动画效果
|
779
|
-
this.processedList = this.generateDataList();
|
780
|
-
|
781
792
|
// 更新拖拽索引
|
782
|
-
this.draggingIndex =
|
793
|
+
this.draggingIndex = targetIndex;
|
783
794
|
this.lastSwapTime = now;
|
784
795
|
}
|
785
796
|
},
|
786
797
|
|
787
|
-
//
|
798
|
+
// 平滑更新拖拽位置的方法
|
788
799
|
updateDragPosition() {
|
789
|
-
//
|
790
|
-
const easing = 0.8; // 大幅提高缓动系数,让拖拽更紧跟手指
|
791
|
-
|
800
|
+
const easing = 0.6; // 增加缓动系数,让拖拽更跟手
|
792
801
|
this.dragCloneX +=
|
793
802
|
(this.targetTouchX - this.touchOffsetX - this.dragCloneX) * easing;
|
794
803
|
this.dragCloneY +=
|
795
804
|
(this.targetTouchY - this.touchOffsetY - this.dragCloneY) * easing;
|
796
|
-
|
797
|
-
// 如果距离目标很近,直接设为目标位置以避免微小抖动
|
798
805
|
if (
|
799
806
|
Math.abs(this.dragCloneX - (this.targetTouchX - this.touchOffsetX)) <
|
800
807
|
0.5
|
@@ -805,18 +812,15 @@ export default {
|
|
805
812
|
0.5
|
806
813
|
)
|
807
814
|
this.dragCloneY = this.targetTouchY - this.touchOffsetY;
|
808
|
-
|
809
|
-
// 继续下一帧动画
|
810
815
|
this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
|
811
816
|
},
|
812
817
|
|
813
818
|
// 元素本身的触摸结束
|
814
819
|
endTouch(e) {
|
815
|
-
console.log('
|
820
|
+
console.log('触摸结束');
|
816
821
|
this.$emit('onDragEventEnd', e);
|
817
822
|
// 如果只是短按(还在计时),就取消长按
|
818
823
|
this.cancelLongPress();
|
819
|
-
|
820
824
|
// 如果正在拖拽中,确保清理状态
|
821
825
|
if (this.isTouchDragging) {
|
822
826
|
// 取消动画帧
|
@@ -825,62 +829,22 @@ export default {
|
|
825
829
|
this.animationFrameId = null;
|
826
830
|
}
|
827
831
|
|
828
|
-
// 移除全局事件监听
|
829
|
-
document.removeEventListener('touchmove', this.onGlobalTouchMove, {
|
830
|
-
passive: false,
|
831
|
-
capture: true,
|
832
|
-
});
|
833
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
834
|
-
passive: false,
|
835
|
-
capture: true,
|
836
|
-
});
|
837
|
-
|
838
|
-
// 恢复页面滚动
|
839
|
-
document.body.style.overflow = '';
|
840
|
-
document.body.style.touchAction = '';
|
841
|
-
|
842
832
|
this.isTouchDragging = false;
|
843
833
|
this.isDragClone = false;
|
844
834
|
this.hoverIndex = null;
|
845
835
|
this.draggingIndex = null;
|
846
|
-
|
847
|
-
// 如果不再拖拽,恢复touch-action(但保持refresh隐藏,因为仍在编辑状态)
|
848
|
-
const elements = this.findParentElements();
|
849
|
-
if (elements.listInner) {
|
850
|
-
elements.listInner.style.touchAction = this.originListInnerStyle;
|
851
|
-
}
|
852
836
|
}
|
853
837
|
},
|
854
838
|
|
855
839
|
// 触摸结束处理 - 全局事件
|
856
840
|
onTouchEnd(e) {
|
857
|
-
console.log('
|
858
|
-
|
859
|
-
if (this.isTouchDragging) {
|
860
|
-
// 确保阻止默认行为
|
861
|
-
e.preventDefault();
|
862
|
-
e.stopPropagation();
|
863
|
-
}
|
841
|
+
console.log('全局触摸结束');
|
864
842
|
// 取消动画帧
|
865
843
|
if (this.animationFrameId) {
|
866
844
|
cancelAnimationFrame(this.animationFrameId);
|
867
845
|
this.animationFrameId = null;
|
868
846
|
}
|
869
847
|
|
870
|
-
// 移除全局事件监听
|
871
|
-
document.removeEventListener('touchmove', this.onGlobalTouchMove, {
|
872
|
-
passive: false,
|
873
|
-
capture: true,
|
874
|
-
});
|
875
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
876
|
-
passive: false,
|
877
|
-
capture: true,
|
878
|
-
});
|
879
|
-
|
880
|
-
// 恢复页面滚动
|
881
|
-
document.body.style.overflow = '';
|
882
|
-
document.body.style.touchAction = '';
|
883
|
-
|
884
848
|
// 重置拖拽状态
|
885
849
|
this.isTouchDragging = false;
|
886
850
|
this.isDragClone = false;
|
@@ -897,6 +861,7 @@ export default {
|
|
897
861
|
if (gridBoxes && gridBoxes.length > 0) {
|
898
862
|
const box = gridBoxes[0];
|
899
863
|
this.gridBoxWidth = box.offsetWidth;
|
864
|
+
console.log('Grid box宽度:', this.gridBoxWidth);
|
900
865
|
|
901
866
|
// 更新text样式,确保不超出
|
902
867
|
const textElements = document.querySelectorAll('.itemText');
|
@@ -909,14 +874,15 @@ export default {
|
|
909
874
|
|
910
875
|
initLottieAnimations() {
|
911
876
|
this.$nextTick(() => {
|
877
|
+
// 清理之前的动画实例
|
912
878
|
Object.keys(this.lottieAnimations).forEach((key) => {
|
913
879
|
const anim = this.lottieAnimations[key];
|
914
880
|
if (anim && typeof anim.destroy === 'function') {
|
915
|
-
console.log(
|
881
|
+
console.log(`销毁旧动画实例: ${key}`);
|
916
882
|
try {
|
917
883
|
anim.removeEventListener('complete');
|
918
884
|
} catch (e) {
|
919
|
-
console.warn(
|
885
|
+
console.warn(`移除事件监听器失败: ${e.message}`);
|
920
886
|
}
|
921
887
|
anim.destroy();
|
922
888
|
}
|
@@ -927,156 +893,26 @@ export default {
|
|
927
893
|
this.lottieAnimations = {};
|
928
894
|
|
929
895
|
console.log(
|
930
|
-
'
|
931
|
-
this.
|
896
|
+
'开始初始化Lottie动画,列表项数量:',
|
897
|
+
this.data.list.length
|
932
898
|
);
|
933
899
|
|
934
|
-
//
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
941
|
-
`bgAnim-${itemId}`,
|
942
|
-
`bg-${itemId}`,
|
943
|
-
item
|
944
|
-
);
|
945
|
-
}
|
900
|
+
// 延迟一点时间确保DOM完全更新并且之前的动画已被销毁
|
901
|
+
setTimeout(() => {
|
902
|
+
// 为每个列表项初始化动画
|
903
|
+
this.data.list.forEach((item) => {
|
904
|
+
const itemId = String(item.itemView.id);
|
905
|
+
// 加载背景动画
|
906
|
+
this.createLottieAnimation('bg', itemId, item.bgAnimView, item);
|
946
907
|
|
947
|
-
|
948
|
-
|
949
|
-
this.loadLottieAnimation(
|
950
|
-
item.loadingAnimView,
|
951
|
-
`loadingAnim-${itemId}`,
|
952
|
-
`loading-${itemId}`,
|
953
|
-
item
|
954
|
-
);
|
955
|
-
}
|
908
|
+
// 加载加载中动画
|
909
|
+
this.createLottieAnimation('loading', itemId, item.loadingAnimView, item);
|
956
910
|
|
957
|
-
|
958
|
-
|
959
|
-
this.loadLottieAnimation(
|
960
|
-
item.frontAnimView,
|
961
|
-
`frontAnim-${itemId}`,
|
962
|
-
`front-${itemId}`,
|
963
|
-
item
|
964
|
-
);
|
965
|
-
}
|
966
|
-
});
|
967
|
-
});
|
968
|
-
},
|
969
|
-
|
970
|
-
// 抽取公共的loadLottieAnimation方法
|
971
|
-
loadLottieAnimation(animView, refName, animKey, item, dataIndex) {
|
972
|
-
if (!animView || !animView.animUrl) {
|
973
|
-
return;
|
974
|
-
}
|
975
|
-
|
976
|
-
const animEl = this.$refs[refName];
|
977
|
-
if (!animEl || !animEl[0]) {
|
978
|
-
return;
|
979
|
-
}
|
980
|
-
|
981
|
-
try {
|
982
|
-
// 如果已经有实例,先销毁它
|
983
|
-
if (this.lottieAnimations[animKey]) {
|
984
|
-
this.lottieAnimations[animKey].destroy();
|
985
|
-
delete this.lottieAnimations[animKey];
|
986
|
-
}
|
987
|
-
|
988
|
-
// 确保animView.visibility不为0才初始化动画
|
989
|
-
if (
|
990
|
-
typeof animView.visibility !== 'number' ||
|
991
|
-
animView.visibility !== 0
|
992
|
-
) {
|
993
|
-
if (this.lottieAnimations[animKey]) {
|
994
|
-
this.lottieAnimations[animKey].destroy();
|
995
|
-
delete this.lottieAnimations[animKey];
|
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
|
-
}
|
1037
|
-
|
1038
|
-
if (animKey.startsWith('bg-') && !item.animRepeatCount) {
|
1039
|
-
this.data.list[dataIndex].bgAnimView = {
|
1040
|
-
...item.bgAnimView,
|
1041
|
-
animUrl: '',
|
1042
|
-
visibility: 0,
|
1043
|
-
};
|
1044
|
-
this.processedList[dataIndex].bgAnimView = {
|
1045
|
-
...item.bgAnimView,
|
1046
|
-
animUrl: '',
|
1047
|
-
visibility: 0,
|
1048
|
-
};
|
1049
|
-
} else if (animKey.startsWith('loading-') && !item.animRepeatCount) {
|
1050
|
-
this.data.list[dataIndex].loadingAnimView = {
|
1051
|
-
...item.loadingAnimView,
|
1052
|
-
animUrl: '',
|
1053
|
-
visibility: 0,
|
1054
|
-
};
|
1055
|
-
this.processedList[dataIndex].loadingAnimView = {
|
1056
|
-
...item.loadingAnimView,
|
1057
|
-
animUrl: '',
|
1058
|
-
visibility: 0,
|
1059
|
-
};
|
1060
|
-
} else if (animKey.startsWith('front-') && !item.animRepeatCount) {
|
1061
|
-
this.data.list[dataIndex].frontAnimView = {
|
1062
|
-
...item.frontAnimView,
|
1063
|
-
animUrl: '',
|
1064
|
-
visibility: 0,
|
1065
|
-
};
|
1066
|
-
this.processedList[dataIndex].frontAnimView = {
|
1067
|
-
...item.frontAnimView,
|
1068
|
-
animUrl: '',
|
1069
|
-
visibility: 0,
|
1070
|
-
};
|
1071
|
-
}
|
1072
|
-
});
|
1073
|
-
}
|
1074
|
-
}
|
911
|
+
// 加载前景动画
|
912
|
+
this.createLottieAnimation('front', itemId, item.frontAnimView, item);
|
1075
913
|
});
|
1076
|
-
}
|
1077
|
-
}
|
1078
|
-
console.error('加载Lottie动画失败:', error);
|
1079
|
-
}
|
914
|
+
}, 50); // 小延迟确保DOM已更新和旧动画已销毁
|
915
|
+
});
|
1080
916
|
},
|
1081
917
|
|
1082
918
|
// 添加新方法用于获取和存储固定宽高
|
@@ -1090,110 +926,62 @@ export default {
|
|
1090
926
|
}
|
1091
927
|
});
|
1092
928
|
},
|
1093
|
-
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
929
|
+
|
930
|
+
// 暂未使用,但保留用于将来扩展
|
931
|
+
leftTopIconClick(e) {
|
932
|
+
e.preventDefault();
|
933
|
+
e.stopPropagation();
|
934
|
+
// 这里可以添加将来的处理逻辑
|
935
|
+
return false;
|
1100
936
|
},
|
1101
937
|
},
|
1102
938
|
watch: {
|
939
|
+
'data.isEditing':{
|
940
|
+
handler(newVal){
|
941
|
+
if(!newVal){
|
942
|
+
this.$set(this.data, 'list', this.data.list);
|
943
|
+
}
|
944
|
+
},
|
945
|
+
deep:true
|
946
|
+
},
|
1103
947
|
// 监听列表数据变化,自动更新处理后的数据
|
1104
948
|
'data.list': {
|
1105
949
|
handler(newList, oldList) {
|
1106
|
-
|
1107
|
-
|
950
|
+
// 防止死循环
|
951
|
+
if (this.isProcessingData) {
|
952
|
+
return;
|
953
|
+
}
|
954
|
+
|
955
|
+
console.log('检测到data.list变化');
|
956
|
+
this.generateDataList();
|
1108
957
|
this.$nextTick(() => {
|
1109
|
-
this.
|
1110
|
-
this.updateFixedItemSize();
|
958
|
+
this.initLottieAnimations();
|
1111
959
|
});
|
1112
960
|
},
|
1113
961
|
deep: true,
|
1114
962
|
},
|
1115
963
|
},
|
1116
964
|
mounted() {
|
1117
|
-
console.log(this.data, '
|
965
|
+
console.log(this.data, 'djdjdjdjdjdjdj');
|
1118
966
|
// 初始获取grid-box宽度
|
1119
967
|
this.updateGridBoxWidth();
|
1120
968
|
// 获取并存储固定宽高
|
1121
969
|
this.updateFixedItemSize();
|
1122
|
-
// 监听窗口大小变化,更新grid-box宽度
|
1123
|
-
window.addEventListener('resize', () => {
|
1124
|
-
this.updateGridBoxWidth();
|
1125
|
-
this.updateFixedItemSize();
|
1126
|
-
});
|
1127
|
-
|
1128
970
|
// 初始化Lottie动画
|
1129
971
|
this.initLottieAnimations();
|
1130
972
|
},
|
1131
973
|
beforeDestroy() {
|
1132
974
|
// 确保清理所有事件监听器
|
1133
975
|
this.cancelLongPress();
|
1134
|
-
document.removeEventListener('touchmove', this.onTouchMove, {
|
1135
|
-
passive: false,
|
1136
|
-
});
|
1137
|
-
document.removeEventListener('touchend', this.onTouchEnd, {
|
1138
|
-
passive: false,
|
1139
|
-
});
|
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
|
-
window.removeEventListener('resize', () => {
|
1154
|
-
this.updateGridBoxWidth();
|
1155
|
-
this.updateFixedItemSize();
|
1156
|
-
});
|
1157
|
-
|
1158
976
|
// 清理动画帧
|
1159
977
|
if (this.animationFrameId) {
|
1160
978
|
cancelAnimationFrame(this.animationFrameId);
|
1161
979
|
this.animationFrameId = null;
|
1162
980
|
}
|
1163
|
-
|
1164
|
-
// 清理所有Lottie动画实例
|
1165
|
-
Object.keys(this.lottieAnimations).forEach((key) => {
|
1166
|
-
const anim = this.lottieAnimations[key];
|
1167
|
-
if (anim && typeof anim.destroy === 'function') {
|
1168
|
-
try {
|
1169
|
-
anim.removeEventListener('complete');
|
1170
|
-
} catch (e) {
|
1171
|
-
console.warn(`移除事件监听器失败: ${e.message}`);
|
1172
|
-
}
|
1173
|
-
anim.destroy();
|
1174
|
-
}
|
1175
|
-
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
|
-
});
|
1190
|
-
|
1191
|
-
// 确保动画实例对象被清空
|
1192
|
-
this.lottieAnimations = {};
|
1193
981
|
},
|
1194
982
|
created() {
|
1195
983
|
// 初始化处理后的数据列表
|
1196
|
-
this.
|
984
|
+
this.generateDataList();
|
1197
985
|
},
|
1198
986
|
};
|
1199
987
|
</script>
|
@@ -1211,6 +999,10 @@ button {
|
|
1211
999
|
position: relative;
|
1212
1000
|
}
|
1213
1001
|
|
1002
|
+
.items-container {
|
1003
|
+
width: 100%;
|
1004
|
+
}
|
1005
|
+
|
1214
1006
|
.grid-items-wrapper {
|
1215
1007
|
width: 100%;
|
1216
1008
|
position: relative;
|
@@ -1225,10 +1017,8 @@ button {
|
|
1225
1017
|
|
1226
1018
|
/* 优化排序过渡效果 */
|
1227
1019
|
.grid-fade-move {
|
1228
|
-
transition: transform 0.
|
1020
|
+
transition: transform 0.2s cubic-bezier(0.2, 0, 0.2, 1);
|
1229
1021
|
will-change: transform;
|
1230
|
-
position: relative;
|
1231
|
-
z-index: 2;
|
1232
1022
|
}
|
1233
1023
|
|
1234
1024
|
.grid-item {
|
@@ -1268,9 +1058,7 @@ button {
|
|
1268
1058
|
top: 0;
|
1269
1059
|
pointer-events: none;
|
1270
1060
|
}
|
1271
|
-
|
1272
|
-
pointer-events: none;
|
1273
|
-
}
|
1061
|
+
|
1274
1062
|
.bg-anim,
|
1275
1063
|
.front-anim {
|
1276
1064
|
z-index: 1;
|