@dolphinweex/weex-harmony 0.1.48 → 0.1.50
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.
@@ -27,64 +27,56 @@
|
|
27
27
|
@touchcancel="endTouch"
|
28
28
|
>
|
29
29
|
<div
|
30
|
-
v-if="
|
31
|
-
item.bgAnimView && item.bgAnimView.animUrl && !data.isEditing
|
32
|
-
"
|
30
|
+
v-if="hasAnimation(item, 'bgAnimView')"
|
33
31
|
class="lottie-container bg-anim"
|
34
|
-
:style="applyNormalAttribute(item.bgAnimView)"
|
32
|
+
:style="applyNormalAttribute(item.bgAnimView, 'bgAnimView')"
|
35
33
|
:ref="`bgAnim-${item.itemView.id}`"
|
36
34
|
></div>
|
37
35
|
<div
|
38
|
-
v-if="
|
39
|
-
item.frontAnimView &&
|
40
|
-
item.frontAnimView.animUrl &&
|
41
|
-
!data.isEditing
|
42
|
-
"
|
36
|
+
v-if="hasAnimation(item, 'frontAnimView')"
|
43
37
|
class="lottie-container front-anim"
|
44
|
-
:style="applyNormalAttribute(item.frontAnimView)"
|
38
|
+
:style="applyNormalAttribute(item.frontAnimView, 'frontAnimView')"
|
45
39
|
:ref="`frontAnim-${item.itemView.id}`"
|
46
40
|
></div>
|
47
41
|
<div
|
48
|
-
:style="applyNormalAttribute(item.itemView)"
|
42
|
+
:style="applyNormalAttribute(item.itemView, 'itemView')"
|
49
43
|
class="grid-box"
|
50
44
|
:class="{ shaking: data.isEditing }"
|
51
45
|
@click.stop.prevent="(e) => itemViewClick(e, item, index)"
|
52
46
|
>
|
53
47
|
<div class="flexBox">
|
54
48
|
<img
|
55
|
-
:src="item
|
56
|
-
:style="applyNormalAttribute(item.iconImage)"
|
49
|
+
:src="getUrl(item, 'iconImage')"
|
50
|
+
:style="applyNormalAttribute(item.iconImage, 'iconImage')"
|
57
51
|
/>
|
58
52
|
<div>
|
59
53
|
<div @click.stop.prevent v-if="!data.isEditing">
|
60
54
|
<button
|
61
55
|
class="actionButton"
|
62
56
|
@click.stop.prevent="(e) => actionClick(e, item, index)"
|
63
|
-
:style="applyNormalAttribute(item.actionButton)"
|
57
|
+
:style="applyNormalAttribute(item.actionButton, 'actionButton')"
|
64
58
|
>
|
65
|
-
{{ item
|
59
|
+
{{ getText(item, 'actionButton') }}
|
66
60
|
</button>
|
67
61
|
<div
|
68
|
-
v-if="
|
69
|
-
item.loadingAnimView && item.loadingAnimView.animUrl
|
70
|
-
"
|
62
|
+
v-if="getUrl(item, 'loadingAnimView', 'animUrl')"
|
71
63
|
class="loading-anim"
|
72
|
-
:style="applyNormalAttribute(item.loadingAnimView)"
|
64
|
+
:style="applyNormalAttribute(item.loadingAnimView, 'loadingAnimView')"
|
73
65
|
:ref="`loadingAnim-${item.itemView.id}`"
|
74
66
|
></div>
|
75
67
|
</div>
|
76
68
|
<div @click.stop.prevent v-if="!data.isEditing">
|
77
69
|
<img
|
78
|
-
:src="item
|
79
|
-
:style="applyNormalAttribute(item.rightIcon)"
|
70
|
+
:src="getUrl(item, 'rightIcon')"
|
71
|
+
:style="applyNormalAttribute(item.rightIcon, 'rightIcon')"
|
80
72
|
@click.stop.prevent="(e) => rightIconClick(e)"
|
81
73
|
/>
|
82
74
|
</div>
|
83
75
|
<div @click.stop.prevent v-if="!data.isEditing">
|
84
76
|
<img
|
85
77
|
class="leftTopIcon"
|
86
|
-
:src="item
|
87
|
-
:style="applyNormalAttribute(item.leftTopIcon)"
|
78
|
+
:src="getUrl(item, 'leftTopIcon')"
|
79
|
+
:style="applyNormalAttribute(item.leftTopIcon, 'leftTopIcon')"
|
88
80
|
@click.stop.prevent
|
89
81
|
/>
|
90
82
|
</div>
|
@@ -96,16 +88,16 @@
|
|
96
88
|
"
|
97
89
|
:src="
|
98
90
|
item.itemView.isSelected
|
99
|
-
? item
|
100
|
-
: item
|
91
|
+
? getUrl(item, 'selectIcon', 'selectedUrl')
|
92
|
+
: getUrl(item, 'selectIcon')
|
101
93
|
"
|
102
|
-
:style="applyNormalAttribute(item.selectIcon)"
|
94
|
+
:style="applyNormalAttribute(item.selectIcon, 'selectIcon')"
|
103
95
|
/>
|
104
96
|
</div>
|
105
97
|
</div>
|
106
98
|
</div>
|
107
|
-
<div class="itemText" :style="[applyNormalAttribute(item.text)]">
|
108
|
-
{{ item
|
99
|
+
<div class="itemText" :style="[applyNormalAttribute(item.text, 'text')]">
|
100
|
+
{{ getText(item, 'text') }}
|
109
101
|
</div>
|
110
102
|
</div>
|
111
103
|
</div>
|
@@ -120,6 +112,27 @@
|
|
120
112
|
<script>
|
121
113
|
import lottie from 'lottie-web';
|
122
114
|
const scla = 2;
|
115
|
+
|
116
|
+
// 常量配置
|
117
|
+
const REQUIRED_TYPES = [
|
118
|
+
'itemView',
|
119
|
+
'text',
|
120
|
+
'actionButton',
|
121
|
+
'rightIcon',
|
122
|
+
'leftTopIcon',
|
123
|
+
'iconImage',
|
124
|
+
'selectIcon',
|
125
|
+
'loadingAnimView',
|
126
|
+
'bgAnimView',
|
127
|
+
'frontAnimView',
|
128
|
+
];
|
129
|
+
|
130
|
+
const ANIMATION_TYPES = [
|
131
|
+
{ type: 'bg', view: 'bgAnimView' },
|
132
|
+
{ type: 'loading', view: 'loadingAnimView' },
|
133
|
+
{ type: 'front', view: 'frontAnimView' }
|
134
|
+
];
|
135
|
+
|
123
136
|
export default {
|
124
137
|
name: 'EditableGrid',
|
125
138
|
props: {
|
@@ -147,6 +160,10 @@ export default {
|
|
147
160
|
isTouchDragging: false,
|
148
161
|
lastSwapTime: 0,
|
149
162
|
isAnimating: false,
|
163
|
+
lastSwapPair: null, // 记录上次交换的元素对
|
164
|
+
stablePositionTime: 0, // 稳定位置时间
|
165
|
+
lastTouchX: 0, // 上次触摸X坐标
|
166
|
+
lastTouchY: 0, // 上次触摸Y坐标
|
150
167
|
touchStartX: 0,
|
151
168
|
touchStartY: 0,
|
152
169
|
draggedItemRect: null,
|
@@ -163,6 +180,15 @@ export default {
|
|
163
180
|
fixedItemHeight: 0,
|
164
181
|
animationFrameId: null,
|
165
182
|
dragCloneElement: null, // 拖拽克隆DOM元素
|
183
|
+
// 性能优化缓存
|
184
|
+
gridItemsCache: [],
|
185
|
+
lastCacheTime: 0,
|
186
|
+
cacheValidDuration: 1000, // 缓存有效期1秒
|
187
|
+
listChangeDebounced: null,
|
188
|
+
updatePositionThrottled: null,
|
189
|
+
// 避免频繁DOM查询
|
190
|
+
containerElement: null,
|
191
|
+
isUpdatingPosition: false,
|
166
192
|
};
|
167
193
|
},
|
168
194
|
computed: {
|
@@ -181,6 +207,263 @@ export default {
|
|
181
207
|
},
|
182
208
|
},
|
183
209
|
methods: {
|
210
|
+
// 添加工具方法
|
211
|
+
isNil(value) {
|
212
|
+
return value === null || value === undefined;
|
213
|
+
},
|
214
|
+
|
215
|
+
// 获取属性值,支持回退到全局数据和默认值
|
216
|
+
getValue(itemData, globalData, property, defaultValue = null) {
|
217
|
+
// 安全地获取属性值,避免访问undefined对象的属性
|
218
|
+
let itemValue;
|
219
|
+
let globalValue;
|
220
|
+
|
221
|
+
try {
|
222
|
+
itemValue = itemData && typeof itemData === 'object' ? itemData[property] : undefined;
|
223
|
+
} catch (e) {
|
224
|
+
itemValue = undefined;
|
225
|
+
}
|
226
|
+
|
227
|
+
try {
|
228
|
+
globalValue = globalData && typeof globalData === 'object' ? globalData[property] : undefined;
|
229
|
+
} catch (e) {
|
230
|
+
globalValue = undefined;
|
231
|
+
}
|
232
|
+
|
233
|
+
if (!this.isNil(itemValue)) {
|
234
|
+
return itemValue;
|
235
|
+
}
|
236
|
+
if (!this.isNil(globalValue)) {
|
237
|
+
return globalValue;
|
238
|
+
}
|
239
|
+
return defaultValue;
|
240
|
+
},
|
241
|
+
|
242
|
+
// 简化的URL获取方法
|
243
|
+
getUrl(item, type, property = 'url') {
|
244
|
+
return this.getValue(item[type], this.data.globalData[type], property, '');
|
245
|
+
},
|
246
|
+
|
247
|
+
// 简化的文本获取方法
|
248
|
+
getText(item, type, property = 'text') {
|
249
|
+
return this.getValue(item[type], this.data.globalData[type], property, '');
|
250
|
+
},
|
251
|
+
|
252
|
+
// 简化的动画URL检查方法
|
253
|
+
hasAnimation(item, type) {
|
254
|
+
return this.getValue(item[type], this.data.globalData[type], 'animUrl', '') && !this.data.isEditing;
|
255
|
+
},
|
256
|
+
|
257
|
+
// 性能优化工具方法
|
258
|
+
debounce(func, wait) {
|
259
|
+
let timeout;
|
260
|
+
return function executedFunction(...args) {
|
261
|
+
const later = () => {
|
262
|
+
clearTimeout(timeout);
|
263
|
+
func(...args);
|
264
|
+
};
|
265
|
+
clearTimeout(timeout);
|
266
|
+
timeout = setTimeout(later, wait);
|
267
|
+
};
|
268
|
+
},
|
269
|
+
|
270
|
+
throttle(func, limit) {
|
271
|
+
let inThrottle;
|
272
|
+
return function executedFunction(...args) {
|
273
|
+
if (!inThrottle) {
|
274
|
+
func.apply(this, args);
|
275
|
+
inThrottle = true;
|
276
|
+
setTimeout(() => inThrottle = false, limit);
|
277
|
+
}
|
278
|
+
};
|
279
|
+
},
|
280
|
+
|
281
|
+
// 缓存DOM查询结果
|
282
|
+
getCachedGridItems() {
|
283
|
+
const now = Date.now();
|
284
|
+
if (now - this.lastCacheTime > this.cacheValidDuration || this.gridItemsCache.length === 0) {
|
285
|
+
this.gridItemsCache = Array.from(document.querySelectorAll('.grid-item'));
|
286
|
+
this.lastCacheTime = now;
|
287
|
+
}
|
288
|
+
return this.gridItemsCache;
|
289
|
+
},
|
290
|
+
|
291
|
+
// 清除DOM缓存
|
292
|
+
clearGridItemsCache() {
|
293
|
+
this.gridItemsCache = [];
|
294
|
+
this.lastCacheTime = 0;
|
295
|
+
},
|
296
|
+
|
297
|
+
// 优化的位置更新方法
|
298
|
+
optimizedUpdateDragPosition() {
|
299
|
+
if (this.isUpdatingPosition) return;
|
300
|
+
|
301
|
+
this.isUpdatingPosition = true;
|
302
|
+
|
303
|
+
const easing = 0.6;
|
304
|
+
const targetX = this.targetTouchX - this.touchOffsetX + window.pageXOffset;
|
305
|
+
const targetY = this.targetTouchY - this.touchOffsetY + window.pageYOffset;
|
306
|
+
|
307
|
+
this.dragCloneX += (targetX - this.dragCloneX) * easing;
|
308
|
+
this.dragCloneY += (targetY - this.dragCloneY) * easing;
|
309
|
+
|
310
|
+
if (Math.abs(this.dragCloneX - targetX) < 0.5) {
|
311
|
+
this.dragCloneX = targetX;
|
312
|
+
}
|
313
|
+
if (Math.abs(this.dragCloneY - targetY) < 0.5) {
|
314
|
+
this.dragCloneY = targetY;
|
315
|
+
}
|
316
|
+
|
317
|
+
this.updateDragClonePosition();
|
318
|
+
|
319
|
+
this.isUpdatingPosition = false;
|
320
|
+
|
321
|
+
if (this.isTouchDragging) {
|
322
|
+
this.animationFrameId = requestAnimationFrame(() => this.optimizedUpdateDragPosition());
|
323
|
+
}
|
324
|
+
},
|
325
|
+
|
326
|
+
// 批量更新样式,减少重绘
|
327
|
+
batchUpdateStyles(elements, styles) {
|
328
|
+
if (!elements || elements.length === 0) return;
|
329
|
+
|
330
|
+
// 使用documentFragment来减少DOM操作
|
331
|
+
elements.forEach(element => {
|
332
|
+
Object.keys(styles).forEach(property => {
|
333
|
+
element.style[property] = styles[property];
|
334
|
+
});
|
335
|
+
});
|
336
|
+
},
|
337
|
+
|
338
|
+
// 确保项目数据结构完整性
|
339
|
+
ensureItemStructure(item) {
|
340
|
+
REQUIRED_TYPES.forEach((type) => {
|
341
|
+
if (!item[type] || typeof item[type] !== 'object') {
|
342
|
+
item[type] = {};
|
343
|
+
}
|
344
|
+
});
|
345
|
+
|
346
|
+
return item;
|
347
|
+
},
|
348
|
+
|
349
|
+
// 抽取的动画初始化公共方法
|
350
|
+
initItemAnimations(item, itemData = null) {
|
351
|
+
const itemId = String(item.itemView.id);
|
352
|
+
const dataSource = itemData || item; // 支持传入不同的数据源
|
353
|
+
|
354
|
+
ANIMATION_TYPES.forEach(({ type, view }) => {
|
355
|
+
const animUrl = this.getValue(
|
356
|
+
dataSource[view],
|
357
|
+
this.data.globalData[view],
|
358
|
+
'animUrl',
|
359
|
+
''
|
360
|
+
);
|
361
|
+
if (animUrl) {
|
362
|
+
this.createLottieAnimation(type, itemId, dataSource[view], dataSource);
|
363
|
+
}
|
364
|
+
});
|
365
|
+
},
|
366
|
+
|
367
|
+
// 统一的样式应用方法,支持缩放控制
|
368
|
+
applyOptimizedAttribute(itemData = {}, type = '', useScale = true) {
|
369
|
+
const globalData = this.data.globalData[type] || {};
|
370
|
+
const scale = useScale ? scla : 1; // 根据参数决定是否应用缩放
|
371
|
+
|
372
|
+
const style = {
|
373
|
+
color: this.getValue(itemData, globalData, 'textColor', '#000000'),
|
374
|
+
fontWeight: this.getValue(itemData, globalData, 'fontWeight', 'normal'),
|
375
|
+
fontFamily: this.getValue(itemData, globalData, 'fontFamilyPath', 'inherit'),
|
376
|
+
fontSize: `${(this.getValue(itemData, globalData, 'textSize', 16)) * scale}px`,
|
377
|
+
// 布局相关样式
|
378
|
+
backgroundColor: this.getValue(itemData, globalData, 'backgroundColor', 'transparent'),
|
379
|
+
borderRadius: `${(this.getValue(itemData, globalData, 'cornerRadius', 0)) * scale}px`,
|
380
|
+
opacity: this.getValue(itemData, globalData, 'alpha', 1),
|
381
|
+
};
|
382
|
+
|
383
|
+
// 处理width
|
384
|
+
const width = this.getValue(itemData, globalData, 'width');
|
385
|
+
if (typeof width === 'number' && width > 0) {
|
386
|
+
style.width = `${width * scale}px`;
|
387
|
+
} else if (width === -1) {
|
388
|
+
style.width = '100%';
|
389
|
+
} else if (width && typeof width === 'string') {
|
390
|
+
style.width = width;
|
391
|
+
}
|
392
|
+
|
393
|
+
// 处理height
|
394
|
+
const height = this.getValue(itemData, globalData, 'height');
|
395
|
+
if (typeof height === 'number' && height > 0) {
|
396
|
+
style.height = `${height * scale}px`;
|
397
|
+
} else if (height && typeof height === 'string') {
|
398
|
+
style.height = height;
|
399
|
+
}
|
400
|
+
|
401
|
+
// 处理visibility和display
|
402
|
+
const visibility = this.getValue(itemData, globalData, 'visibility');
|
403
|
+
if (typeof visibility === 'number') {
|
404
|
+
if (visibility === 0) {
|
405
|
+
style.display = 'none';
|
406
|
+
} else {
|
407
|
+
style.display = this.getValue(itemData, globalData, 'display', '');
|
408
|
+
style.visibility = visibility === 1 ? 'visible' : 'hidden';
|
409
|
+
}
|
410
|
+
} else {
|
411
|
+
style.display = this.getValue(itemData, globalData, 'display', '');
|
412
|
+
}
|
413
|
+
|
414
|
+
// 处理边框
|
415
|
+
const borderColor = this.data.isEditing
|
416
|
+
? this.getValue(itemData, globalData, 'selectedBorderColor', '')
|
417
|
+
: this.getValue(itemData, globalData, 'normalBorderColor', '');
|
418
|
+
const borderWidth = this.data.isEditing
|
419
|
+
? this.getValue(itemData, globalData, 'selectedBorderWidth', 0)
|
420
|
+
: this.getValue(itemData, globalData, 'normalBorderWidth', 0);
|
421
|
+
|
422
|
+
style.borderColor = borderColor;
|
423
|
+
style.borderWidth = `${borderWidth * scale}px`;
|
424
|
+
|
425
|
+
// 处理padding
|
426
|
+
const padding = this.getValue(itemData, globalData, 'padding');
|
427
|
+
if (padding && Array.isArray(padding)) {
|
428
|
+
style.paddingTop = `${(padding[1] || 0) * scale}px`;
|
429
|
+
style.paddingLeft = `${(padding[0] || 0) * scale}px`;
|
430
|
+
style.paddingBottom = `${(padding[3] || 0) * scale}px`;
|
431
|
+
style.paddingRight = `${(padding[2] || 0) * scale}px`;
|
432
|
+
}
|
433
|
+
|
434
|
+
// 处理margin
|
435
|
+
const margin = this.getValue(itemData, globalData, 'margin');
|
436
|
+
if (margin && Array.isArray(margin)) {
|
437
|
+
style.marginTop = `${(margin[1] || 0) * scale}px`;
|
438
|
+
style.marginLeft = `${(margin[0] || 0) * scale}px`;
|
439
|
+
style.marginBottom = `${(margin[3] || 0) * scale}px`;
|
440
|
+
style.marginRight = `${(margin[2] || 0) * scale}px`;
|
441
|
+
}
|
442
|
+
|
443
|
+
return style;
|
444
|
+
},
|
445
|
+
|
446
|
+
|
447
|
+
|
448
|
+
|
449
|
+
|
450
|
+
// 优化内存使用
|
451
|
+
optimizeMemoryUsage() {
|
452
|
+
// 清理无用的动画实例
|
453
|
+
Object.keys(this.lottieAnimations).forEach(key => {
|
454
|
+
const anim = this.lottieAnimations[key];
|
455
|
+
if (anim && anim.isLoaded === false) {
|
456
|
+
anim.destroy();
|
457
|
+
delete this.lottieAnimations[key];
|
458
|
+
}
|
459
|
+
});
|
460
|
+
|
461
|
+
// 清理DOM缓存
|
462
|
+
if (this.gridItemsCache.length > 50) { // 如果缓存过多,清理
|
463
|
+
this.clearGridItemsCache();
|
464
|
+
}
|
465
|
+
},
|
466
|
+
|
184
467
|
// 获取元素在页面中的绝对位置(考虑所有transform影响)
|
185
468
|
getAbsolutePosition(element) {
|
186
469
|
let x = 0;
|
@@ -217,7 +500,7 @@ export default {
|
|
217
500
|
this.dragCloneElement.className = 'drag-clone-dynamic';
|
218
501
|
|
219
502
|
// 设置基本样式(使用不带缩放的样式方法)
|
220
|
-
const itemViewStyle = this.
|
503
|
+
const itemViewStyle = this.applyOptimizedAttribute(item.itemView, 'itemView', false);
|
221
504
|
Object.assign(this.dragCloneElement.style, {
|
222
505
|
position: 'absolute',
|
223
506
|
zIndex: '9999',
|
@@ -237,11 +520,11 @@ export default {
|
|
237
520
|
justifyContent: 'space-between',
|
238
521
|
});
|
239
522
|
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
523
|
+
// 创建内容HTML(不使用缩放)
|
524
|
+
const iconImageStyle = this.applyOptimizedAttribute(item.iconImage, 'iconImage', false);
|
525
|
+
const selectIconStyle = this.applyOptimizedAttribute(item.selectIcon, 'selectIcon', false);
|
526
|
+
const textStyle = this.applyOptimizedAttribute(item.text, 'text', false);
|
527
|
+
const actionButtonStyle = this.applyOptimizedAttribute(item.actionButton, 'actionButton', false);
|
245
528
|
|
246
529
|
// 将样式对象转换为CSS字符串
|
247
530
|
const stylesToString = (styleObj) => {
|
@@ -252,7 +535,7 @@ export default {
|
|
252
535
|
|
253
536
|
// 确保内部grid-box也使用固定尺寸(使用不带缩放的样式方法)
|
254
537
|
const gridBoxStyle = {
|
255
|
-
...this.
|
538
|
+
...this.applyOptimizedAttribute(item.itemView, 'itemView', false),
|
256
539
|
width: `${this.fixedItemWidth }px`,
|
257
540
|
height: `${this.fixedItemHeight }px`
|
258
541
|
};
|
@@ -260,17 +543,19 @@ export default {
|
|
260
543
|
this.dragCloneElement.innerHTML = `
|
261
544
|
<div class="grid-box" style="${stylesToString(gridBoxStyle)} ;display: flex; justify-content: space-between;flex-direction: column">
|
262
545
|
<div class="flexBox" style="display: flex; justify-content: space-between; width: 100%; position: relative; flex-direction: row;">
|
263
|
-
<img src="${item
|
546
|
+
<img src="${this.getUrl(item, 'iconImage')}" style="${stylesToString(iconImageStyle)}" />
|
264
547
|
<div style="pointer-events: none;">
|
265
548
|
<img
|
266
549
|
class="isSelected"
|
267
|
-
src="${item.itemView.isSelected
|
550
|
+
src="${item.itemView.isSelected
|
551
|
+
? this.getUrl(item, 'selectIcon', 'selectedUrl')
|
552
|
+
: this.getUrl(item, 'selectIcon')}"
|
268
553
|
style="${stylesToString(selectIconStyle)}"
|
269
554
|
/>
|
270
555
|
</div>
|
271
556
|
</div>
|
272
557
|
<div class="itemText" style="${stylesToString(textStyle)}; display: block; width: 80%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
273
|
-
${item
|
558
|
+
${this.getText(item, 'text')}
|
274
559
|
</div>
|
275
560
|
</div>
|
276
561
|
`;
|
@@ -298,64 +583,11 @@ export default {
|
|
298
583
|
this.dragCloneElement.style.transform = `translate(${this.dragCloneX}px, ${this.dragCloneY}px)`;
|
299
584
|
},
|
300
585
|
|
301
|
-
//
|
302
|
-
|
303
|
-
|
304
|
-
color: data.textColor || '#000000',
|
305
|
-
fontWeight: data.fontWeight || 'normal',
|
306
|
-
fontFamily: data.fontFamilyPath || 'inherit',
|
307
|
-
fontSize: `${data.textSize || 16}px`, // 不乘以scla
|
308
|
-
// 布局相关样式
|
309
|
-
width:
|
310
|
-
typeof data.width === 'number' && data.width > 0
|
311
|
-
? `${data.width}px` // 不乘以scla
|
312
|
-
: data.width === -1
|
313
|
-
? '%'
|
314
|
-
: data.width || '',
|
315
|
-
height:
|
316
|
-
typeof data.height === 'number'
|
317
|
-
? `${data.height}px` // 不乘以scla
|
318
|
-
: data.height || '',
|
319
|
-
backgroundColor: data.backgroundColor || 'transparent',
|
320
|
-
borderRadius: `${data.cornerRadius || 0}px`, // 不乘以scla
|
321
|
-
opacity: data.alpha || 1,
|
322
|
-
};
|
323
|
-
|
324
|
-
if (typeof data.visibility === 'number') {
|
325
|
-
if (data.visibility === 0) {
|
326
|
-
style.display = 'none';
|
327
|
-
} else {
|
328
|
-
style.display = data.display || '';
|
329
|
-
style.visibility = data.visibility === 1 ? 'visible' : 'hidden';
|
330
|
-
}
|
331
|
-
} else {
|
332
|
-
style.display = data.display || '';
|
333
|
-
}
|
334
|
-
|
335
|
-
style.borderColor = this.data.isEditing
|
336
|
-
? data.selectedBorderColor || ''
|
337
|
-
: data.normalBorderColor || '';
|
338
|
-
style.borderWidth = this.data.isEditing
|
339
|
-
? `${data.selectedBorderWidth || 0}px` // 不乘以scla
|
340
|
-
: `${data.normalBorderWidth || 0}px`; // 不乘以scla
|
341
|
-
|
342
|
-
if (data.padding) {
|
343
|
-
style.paddingTop = `${data.padding[1] || 0}px`; // 不乘以scla
|
344
|
-
style.paddingLeft = `${data.padding[0] || 0}px`; // 不乘以scla
|
345
|
-
style.paddingBottom = `${data.padding[3] || 0}px`; // 不乘以scla
|
346
|
-
style.paddingRight = `${data.padding[2] || 0}px`; // 不乘以scla
|
347
|
-
}
|
348
|
-
if (data.margin) {
|
349
|
-
style.marginTop = `${data.margin[1] || 0}px`; // 不乘以scla
|
350
|
-
style.marginLeft = `${data.margin[0] || 0}px`; // 不乘以scla
|
351
|
-
style.marginBottom = `${data.margin[3] || 0}px`; // 不乘以scla
|
352
|
-
style.marginRight = `${data.margin[2] || 0}px`; // 不乘以scla
|
353
|
-
}
|
354
|
-
|
355
|
-
return style;
|
586
|
+
// 普通渲染的样式应用方法(使用缩放)
|
587
|
+
applyNormalAttribute(itemData = {}, type = '') {
|
588
|
+
return this.applyOptimizedAttribute(itemData, type, true);
|
356
589
|
},
|
357
590
|
updateListItem(data) {
|
358
|
-
console.log('updateListItem收到数据:', JSON.stringify(data));
|
359
591
|
const id = data.itemView.id;
|
360
592
|
const index = this.data.list.findIndex((item) => item.itemView.id == id);
|
361
593
|
|
@@ -363,38 +595,16 @@ export default {
|
|
363
595
|
return;
|
364
596
|
}
|
365
597
|
|
366
|
-
//
|
367
|
-
|
598
|
+
// 确保数据结构完整性
|
599
|
+
this.ensureItemStructure(data);
|
368
600
|
|
369
|
-
|
601
|
+
// 直接更新数据,不再需要合并
|
602
|
+
this.$set(this.data.list, index, data);
|
370
603
|
this.$nextTick(() => {
|
371
604
|
const itemId = String(data.itemView.id);
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
itemId,
|
376
|
-
mergedData.bgAnimView,
|
377
|
-
mergedData
|
378
|
-
);
|
379
|
-
}
|
380
|
-
|
381
|
-
if (mergedData.loadingAnimView.animUrl) {
|
382
|
-
this.createLottieAnimation(
|
383
|
-
'loading',
|
384
|
-
itemId,
|
385
|
-
mergedData.loadingAnimView,
|
386
|
-
mergedData
|
387
|
-
);
|
388
|
-
}
|
389
|
-
|
390
|
-
if (mergedData.frontAnimView.animUrl) {
|
391
|
-
this.createLottieAnimation(
|
392
|
-
'front',
|
393
|
-
itemId,
|
394
|
-
mergedData.frontAnimView,
|
395
|
-
mergedData
|
396
|
-
);
|
397
|
-
}
|
605
|
+
|
606
|
+
// 使用抽取的公共方法初始化动画
|
607
|
+
this.initItemAnimations(data, data);
|
398
608
|
});
|
399
609
|
},
|
400
610
|
createLottieAnimation(type, itemId, animView, itemData) {
|
@@ -488,17 +698,31 @@ export default {
|
|
488
698
|
},
|
489
699
|
getListData(callback) {
|
490
700
|
let data = Array.from(this.data.list);
|
491
|
-
data = data.map((item) =>
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
701
|
+
data = data.map((item) => {
|
702
|
+
// 确保每个项目都有完整的数据结构
|
703
|
+
const completeItem = { ...item };
|
704
|
+
this.ensureItemStructure(completeItem);
|
705
|
+
|
706
|
+
// 为了外部使用,合并全局数据到每个类型
|
707
|
+
REQUIRED_TYPES.forEach((type) => {
|
708
|
+
const globalData = this.data.globalData && this.data.globalData[type] || {};
|
709
|
+
completeItem[type] = {
|
710
|
+
...globalData,
|
711
|
+
...completeItem[type]
|
712
|
+
};
|
713
|
+
});
|
714
|
+
|
715
|
+
return {
|
716
|
+
...completeItem,
|
717
|
+
itemView: {
|
718
|
+
...completeItem.itemView,
|
719
|
+
id: String(completeItem.itemView.id),
|
720
|
+
},
|
721
|
+
};
|
722
|
+
});
|
498
723
|
callback({ listData: data });
|
499
724
|
},
|
500
725
|
rightIconClick(e) {
|
501
|
-
console.log('rightIconClick执行,阻止事件冒泡');
|
502
726
|
e.preventDefault();
|
503
727
|
e.stopPropagation();
|
504
728
|
this.$emit('onClickRightIcon', e);
|
@@ -554,7 +778,6 @@ export default {
|
|
554
778
|
},
|
555
779
|
|
556
780
|
actionClick(e, item, index) {
|
557
|
-
console.log('actionClick执行,阻止事件冒泡');
|
558
781
|
e.preventDefault();
|
559
782
|
e.stopPropagation(); // 阻止事件冒泡到itemViewClick
|
560
783
|
|
@@ -572,8 +795,6 @@ export default {
|
|
572
795
|
},
|
573
796
|
|
574
797
|
cleanupOtherAnimations(currentItemId) {
|
575
|
-
console.log('清理其他项目的动画实例,当前项目ID:', currentItemId);
|
576
|
-
|
577
798
|
Object.keys(this.lottieAnimations).forEach((key) => {
|
578
799
|
if (!key.includes(`-${currentItemId}`)) {
|
579
800
|
if (this.lottieAnimations[key]) {
|
@@ -621,103 +842,10 @@ export default {
|
|
621
842
|
});
|
622
843
|
},
|
623
844
|
|
624
|
-
generateDataList() {
|
625
|
-
if (!this.data || !this.data.list || !this.data.globalData) {
|
626
|
-
return;
|
627
|
-
}
|
628
|
-
// 直接处理 this.data.list,为每个项目合并全局数据
|
629
|
-
this.data.list.forEach((item, index) => {
|
630
|
-
const processedItem = this.mergeItemData(item);
|
631
|
-
this.$set(this.data.list, index, processedItem);
|
632
|
-
});
|
633
|
-
|
634
|
-
},
|
635
|
-
|
636
|
-
mergeItemData(item) {
|
637
|
-
const processedItem = {};
|
638
|
-
const types = [
|
639
|
-
'itemView',
|
640
|
-
'text',
|
641
|
-
'actionButton',
|
642
|
-
'rightIcon',
|
643
|
-
'leftTopIcon',
|
644
|
-
'iconImage',
|
645
|
-
'selectIcon',
|
646
|
-
'loadingAnimView',
|
647
|
-
'bgAnimView',
|
648
|
-
'frontAnimView',
|
649
|
-
];
|
650
|
-
|
651
|
-
types.forEach((type) => {
|
652
|
-
// 使用mergeDate合并全局数据和项目数据
|
653
|
-
processedItem[type] = this.mergeDate(item[type] || {}, type);
|
654
|
-
});
|
655
845
|
|
656
|
-
return processedItem;
|
657
|
-
},
|
658
846
|
|
659
|
-
getItemStyle(item, type) {
|
660
|
-
const result = this.mergeDate(item[type] || {}, type);
|
661
|
-
console.log(type, this.applyNormalAttribute(result), 'ggg');
|
662
|
-
return this.applyNormalAttribute(result);
|
663
|
-
},
|
664
|
-
mergeDate(target, type) {
|
665
|
-
const globalData = this.data.globalData[type] || {};
|
666
|
-
return Object.assign({}, globalData, target);
|
667
|
-
},
|
668
|
-
applyNormalAttribute(data = {}, type) {
|
669
|
-
let style = {
|
670
|
-
color: data.textColor || '#000000',
|
671
|
-
fontWeight: data.fontWeight || 'normal',
|
672
|
-
fontFamily: data.fontFamilyPath || 'inherit',
|
673
|
-
fontSize: `${(data.textSize || 16) * scla}px`, // 默认16px
|
674
|
-
// 布局相关样式
|
675
|
-
width:
|
676
|
-
typeof data.width === 'number' && data.width > 0
|
677
|
-
? `${data.width * scla}px`
|
678
|
-
: data.width === -1
|
679
|
-
? '%'
|
680
|
-
: data.width || '',
|
681
|
-
height:
|
682
|
-
typeof data.height === 'number'
|
683
|
-
? `${data.height * scla}px`
|
684
|
-
: data.height || '',
|
685
|
-
backgroundColor: data.backgroundColor || 'transparent',
|
686
|
-
borderRadius: `${(data.cornerRadius || 0) * scla}px`,
|
687
|
-
opacity: data.alpha || 1,
|
688
|
-
};
|
689
|
-
if (typeof data.visibility === 'number') {
|
690
|
-
if (data.visibility === 0) {
|
691
|
-
style.display = 'none';
|
692
|
-
} else {
|
693
|
-
style.display = data.display || '';
|
694
|
-
style.visibility = data.visibility === 1 ? 'visible' : 'hidden';
|
695
|
-
}
|
696
|
-
} else {
|
697
|
-
style.display = data.display || '';
|
698
|
-
}
|
699
847
|
|
700
|
-
style.borderColor = this.data.isEditing
|
701
|
-
? data.selectedBorderColor || ''
|
702
|
-
: data.normalBorderColor || '';
|
703
|
-
style.borderWidth = this.data.isEditing
|
704
|
-
? `${(data.selectedBorderWidth || 0) * scla}px`
|
705
|
-
: `${(data.normalBorderWidth || 0) * scla}px`;
|
706
|
-
if (data.padding) {
|
707
|
-
style.paddingTop = `${(data.padding[1] || 0) * scla}px`;
|
708
|
-
style.paddingLeft = `${(data.padding[0] || 0) * scla}px`;
|
709
|
-
style.paddingBottom = `${(data.padding[3] || 0) * scla}px`;
|
710
|
-
style.paddingRight = `${(data.padding[2] || 0) * scla}px`;
|
711
|
-
}
|
712
|
-
if (data.margin) {
|
713
|
-
style.marginTop = `${(data.margin[1] || 0) * scla}px`;
|
714
|
-
style.marginLeft = `${(data.margin[0] || 0) * scla}px`;
|
715
|
-
style.marginBottom = `${(data.margin[3] || 0) * scla}px`;
|
716
|
-
style.marginRight = `${(data.margin[2] || 0) * scla}px`;
|
717
|
-
}
|
718
848
|
|
719
|
-
return style;
|
720
|
-
},
|
721
849
|
// 开始长按
|
722
850
|
startLongPress(e, index) {
|
723
851
|
if (!this.data.isEditable) {
|
@@ -730,7 +858,6 @@ export default {
|
|
730
858
|
const touch = e.changedTouches[0];
|
731
859
|
this.touchStartX = touch.pageX;
|
732
860
|
this.touchStartY = touch.pageY;
|
733
|
-
console.log('开始长按ddddddddd', touch, touch.pageY, touch.pageX);
|
734
861
|
this.isScrolling = false; // 重置滚动状态
|
735
862
|
|
736
863
|
// 设置新的长按计时器
|
@@ -788,10 +915,14 @@ export default {
|
|
788
915
|
|
789
916
|
// 激活拖拽模式
|
790
917
|
activateDragMode(index) {
|
791
|
-
console.log('长按触发,进入编辑模式');
|
792
|
-
// 进入编辑模式
|
793
|
-
|
794
918
|
if (this.data.isEditing) {
|
919
|
+
// 重置交换状态
|
920
|
+
this.lastSwapPair = null;
|
921
|
+
this.stablePositionTime = 0;
|
922
|
+
this.isAnimating = false;
|
923
|
+
this.lastTouchX = this.touchStartX;
|
924
|
+
this.lastTouchY = this.touchStartY;
|
925
|
+
|
795
926
|
// 设置当前拖拽索引
|
796
927
|
this.draggingIndex = index;
|
797
928
|
this.isTouchDragging = true;
|
@@ -802,8 +933,9 @@ export default {
|
|
802
933
|
height: this.fixedItemHeight,
|
803
934
|
};
|
804
935
|
|
805
|
-
// 获取当前元素位置用于计算偏移
|
806
|
-
const
|
936
|
+
// 获取当前元素位置用于计算偏移 - 使用缓存的元素
|
937
|
+
const gridItems = this.getCachedGridItems();
|
938
|
+
const element = gridItems[index];
|
807
939
|
if (element) {
|
808
940
|
// 使用getBoundingClientRect获取视口中的位置
|
809
941
|
const rect = element.getBoundingClientRect();
|
@@ -830,7 +962,6 @@ export default {
|
|
830
962
|
this.updateDragClonePosition();
|
831
963
|
});
|
832
964
|
|
833
|
-
console.log('拖拽克隆已创建');
|
834
965
|
}
|
835
966
|
}
|
836
967
|
if(!this.data.isEditing){
|
@@ -872,21 +1003,49 @@ export default {
|
|
872
1003
|
|
873
1004
|
// 启动平滑动画(如果尚未启动)
|
874
1005
|
if (!this.animationFrameId) {
|
875
|
-
this.animationFrameId = requestAnimationFrame(this.
|
1006
|
+
this.animationFrameId = requestAnimationFrame(this.optimizedUpdateDragPosition);
|
876
1007
|
}
|
877
1008
|
|
878
|
-
//
|
879
|
-
this.
|
1009
|
+
// 使用节流优化交换逻辑,增加节流时间减少频繁调用
|
1010
|
+
if (!this.updatePositionThrottled) {
|
1011
|
+
this.updatePositionThrottled = this.throttle(this.handleSwapLogic.bind(this), 500);
|
1012
|
+
}
|
1013
|
+
|
1014
|
+
// 在动画过程中不执行交换逻辑
|
1015
|
+
if (!this.isAnimating) {
|
1016
|
+
this.updatePositionThrottled(touch.pageX, touch.pageY);
|
1017
|
+
}
|
880
1018
|
},
|
881
1019
|
|
882
1020
|
// 简化的交换处理逻辑
|
883
1021
|
handleSwapLogic(touchX, touchY) {
|
884
1022
|
// 控制交换频率,避免过于频繁的交换
|
885
1023
|
const now = Date.now();
|
886
|
-
if (now - this.lastSwapTime <
|
1024
|
+
if (now - this.lastSwapTime < 150) return; // 增加频率控制到150ms
|
1025
|
+
|
1026
|
+
// 检查位置稳定性 - 如果触摸点基本没有移动,认为是稳定状态
|
1027
|
+
const moveDistance = Math.sqrt(
|
1028
|
+
Math.pow(touchX - this.lastTouchX, 2) + Math.pow(touchY - this.lastTouchY, 2)
|
1029
|
+
);
|
1030
|
+
|
1031
|
+
if (moveDistance < 10) { // 移动距离小于10px认为是稳定的
|
1032
|
+
if (this.stablePositionTime === 0) {
|
1033
|
+
this.stablePositionTime = now;
|
1034
|
+
} else if (now - this.stablePositionTime > 300) {
|
1035
|
+
// 稳定超过300ms,跳过交换检测
|
1036
|
+
return;
|
1037
|
+
}
|
1038
|
+
} else {
|
1039
|
+
// 位置发生明显移动,重置稳定时间
|
1040
|
+
this.stablePositionTime = 0;
|
1041
|
+
}
|
1042
|
+
|
1043
|
+
// 更新上次触摸位置
|
1044
|
+
this.lastTouchX = touchX;
|
1045
|
+
this.lastTouchY = touchY;
|
887
1046
|
|
888
|
-
//
|
889
|
-
const gridItems =
|
1047
|
+
// 使用缓存的网格元素
|
1048
|
+
const gridItems = this.getCachedGridItems();
|
890
1049
|
let targetIndex = null;
|
891
1050
|
|
892
1051
|
// 寻找触摸点所在的目标元素
|
@@ -899,8 +1058,8 @@ export default {
|
|
899
1058
|
// 触摸点是否在元素的中心区域内
|
900
1059
|
const centerX = (rect.left + rect.right) / 2;
|
901
1060
|
const centerY = (rect.top + rect.bottom) / 2;
|
902
|
-
const halfWidth = rect.width * 0.
|
903
|
-
const halfHeight = rect.height * 0.
|
1061
|
+
const halfWidth = rect.width * 0.3; // 缩小判断区域到30%,减少误触
|
1062
|
+
const halfHeight = rect.height * 0.3;
|
904
1063
|
|
905
1064
|
// 检查触摸点是否在元素的中心区域内
|
906
1065
|
if (
|
@@ -919,46 +1078,46 @@ export default {
|
|
919
1078
|
|
920
1079
|
// 执行交换
|
921
1080
|
if (targetIndex !== null && targetIndex !== this.draggingIndex) {
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
//
|
1081
|
+
// 防止在动画过程中频繁交换
|
1082
|
+
if (this.isAnimating) return;
|
1083
|
+
|
1084
|
+
// 创建当前交换对的标识符
|
1085
|
+
const currentSwapPair = [this.draggingIndex, targetIndex].sort().join('-');
|
1086
|
+
|
1087
|
+
// 如果这是同一对元素的重复交换,需要更严格的时间控制
|
1088
|
+
if (this.lastSwapPair === currentSwapPair) {
|
1089
|
+
if (now - this.lastSwapTime < 1000) return; // 同一对元素需要等待1秒
|
1090
|
+
}
|
1091
|
+
|
1092
|
+
this.isAnimating = true;
|
1093
|
+
|
1094
|
+
// 使用Vue的$set来避免触发不必要的重新渲染
|
1095
|
+
const temp = this.data.list[this.draggingIndex];
|
1096
|
+
this.$set(this.data.list, this.draggingIndex, this.data.list[targetIndex]);
|
1097
|
+
this.$set(this.data.list, targetIndex, temp);
|
1098
|
+
|
1099
|
+
// 更新拖拽索引和交换记录
|
929
1100
|
this.draggingIndex = targetIndex;
|
930
1101
|
this.lastSwapTime = now;
|
1102
|
+
this.lastSwapPair = currentSwapPair;
|
1103
|
+
|
1104
|
+
// 重置稳定位置时间,因为刚刚发生了交换
|
1105
|
+
this.stablePositionTime = 0;
|
1106
|
+
|
1107
|
+
// 延迟清理缓存,等待DOM更新完成
|
1108
|
+
this.$nextTick(() => {
|
1109
|
+
setTimeout(() => {
|
1110
|
+
this.clearGridItemsCache();
|
1111
|
+
this.isAnimating = false;
|
1112
|
+
}, 100); // 增加延迟时间到100ms
|
1113
|
+
});
|
931
1114
|
}
|
932
1115
|
},
|
933
1116
|
|
934
|
-
|
935
|
-
updateDragPosition() {
|
936
|
-
const easing = 0.6;
|
937
|
-
// 计算目标位置(考虑页面滚动位置)
|
938
|
-
const targetX = this.targetTouchX - this.touchOffsetX + window.pageXOffset;
|
939
|
-
const targetY = this.targetTouchY - this.touchOffsetY + window.pageYOffset;
|
940
|
-
|
941
|
-
// 平滑插值
|
942
|
-
this.dragCloneX += (targetX - this.dragCloneX) * easing;
|
943
|
-
this.dragCloneY += (targetY - this.dragCloneY) * easing;
|
944
|
-
|
945
|
-
// 当接近目标位置时直接设置为目标位置
|
946
|
-
if (Math.abs(this.dragCloneX - targetX) < 0.5) {
|
947
|
-
this.dragCloneX = targetX;
|
948
|
-
}
|
949
|
-
if (Math.abs(this.dragCloneY - targetY) < 0.5) {
|
950
|
-
this.dragCloneY = targetY;
|
951
|
-
}
|
952
|
-
|
953
|
-
// 更新拖拽克隆元素位置
|
954
|
-
this.updateDragClonePosition();
|
955
|
-
|
956
|
-
this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
|
957
|
-
},
|
1117
|
+
|
958
1118
|
|
959
1119
|
// 元素本身的触摸结束
|
960
1120
|
endTouch(e) {
|
961
|
-
console.log('触摸结束');
|
962
1121
|
this.$emit('onDragEventEnd', e);
|
963
1122
|
// 如果只是短按(还在计时),就取消长按
|
964
1123
|
this.cancelLongPress();
|
@@ -973,20 +1132,32 @@ export default {
|
|
973
1132
|
this.isTouchDragging = false;
|
974
1133
|
this.isDragClone = false;
|
975
1134
|
this.hoverIndex = null;
|
1135
|
+
this.isUpdatingPosition = false;
|
1136
|
+
|
1137
|
+
// 重置交换状态
|
1138
|
+
this.lastSwapPair = null;
|
1139
|
+
this.stablePositionTime = 0;
|
1140
|
+
this.lastTouchX = 0;
|
1141
|
+
this.lastTouchY = 0;
|
976
1142
|
|
977
1143
|
// 销毁拖拽克隆元素
|
978
1144
|
this.destroyDragClone();
|
1145
|
+
|
1146
|
+
// 等待可能的交换动画完成后再清理
|
1147
|
+
setTimeout(() => {
|
1148
|
+
this.clearGridItemsCache();
|
1149
|
+
this.isAnimating = false; // 重置动画状态
|
1150
|
+
}, 100);
|
979
1151
|
|
980
1152
|
// 使用延时来重置拖拽索引,避免与点击事件冲突
|
981
1153
|
setTimeout(() => {
|
982
1154
|
this.draggingIndex = null;
|
983
|
-
},
|
1155
|
+
}, 150);
|
984
1156
|
}
|
985
1157
|
},
|
986
1158
|
|
987
1159
|
// 触摸结束处理 - 全局事件
|
988
1160
|
onTouchEnd(e) {
|
989
|
-
console.log('全局触摸结束');
|
990
1161
|
// 取消动画帧
|
991
1162
|
if (this.animationFrameId) {
|
992
1163
|
cancelAnimationFrame(this.animationFrameId);
|
@@ -997,13 +1168,23 @@ export default {
|
|
997
1168
|
this.isTouchDragging = false;
|
998
1169
|
this.isDragClone = false;
|
999
1170
|
this.hoverIndex = null;
|
1171
|
+
this.isUpdatingPosition = false;
|
1172
|
+
|
1173
|
+
// 重置交换状态
|
1174
|
+
this.lastSwapPair = null;
|
1175
|
+
this.stablePositionTime = 0;
|
1176
|
+
this.lastTouchX = 0;
|
1177
|
+
this.lastTouchY = 0;
|
1000
1178
|
|
1001
1179
|
// 销毁拖拽克隆元素
|
1002
1180
|
this.destroyDragClone();
|
1003
|
-
|
1004
|
-
//
|
1005
|
-
|
1006
|
-
|
1181
|
+
|
1182
|
+
// 延迟清理,确保所有动画和状态更新完成
|
1183
|
+
setTimeout(() => {
|
1184
|
+
this.clearGridItemsCache();
|
1185
|
+
this.isAnimating = false;
|
1186
|
+
this.draggingIndex = null;
|
1187
|
+
}, 100);
|
1007
1188
|
},
|
1008
1189
|
// 获取grid-box宽度的方法
|
1009
1190
|
updateGridBoxWidth() {
|
@@ -1012,7 +1193,6 @@ export default {
|
|
1012
1193
|
if (gridBoxes && gridBoxes.length > 0) {
|
1013
1194
|
const box = gridBoxes[0];
|
1014
1195
|
this.gridBoxWidth = box.offsetWidth;
|
1015
|
-
console.log('Grid box宽度:', this.gridBoxWidth);
|
1016
1196
|
|
1017
1197
|
// 更新text样式,确保不超出
|
1018
1198
|
const textElements = document.querySelectorAll('.itemText');
|
@@ -1029,7 +1209,6 @@ export default {
|
|
1029
1209
|
Object.keys(this.lottieAnimations).forEach((key) => {
|
1030
1210
|
const anim = this.lottieAnimations[key];
|
1031
1211
|
if (anim && typeof anim.destroy === 'function') {
|
1032
|
-
console.log(`销毁旧动画实例: ${key}`);
|
1033
1212
|
try {
|
1034
1213
|
anim.removeEventListener('complete');
|
1035
1214
|
} catch (e) {
|
@@ -1043,31 +1222,11 @@ export default {
|
|
1043
1222
|
// 重置动画实例对象
|
1044
1223
|
this.lottieAnimations = {};
|
1045
1224
|
|
1046
|
-
console.log('开始初始化Lottie动画,列表项数量:', this.data.list.length);
|
1047
|
-
|
1048
1225
|
// 延迟一点时间确保DOM完全更新并且之前的动画已被销毁
|
1049
1226
|
setTimeout(() => {
|
1050
1227
|
// 为每个列表项初始化动画
|
1051
1228
|
this.data.list.forEach((item) => {
|
1052
|
-
|
1053
|
-
// 加载背景动画
|
1054
|
-
this.createLottieAnimation('bg', itemId, item.bgAnimView, item);
|
1055
|
-
|
1056
|
-
// 加载加载中动画
|
1057
|
-
this.createLottieAnimation(
|
1058
|
-
'loading',
|
1059
|
-
itemId,
|
1060
|
-
item.loadingAnimView,
|
1061
|
-
item
|
1062
|
-
);
|
1063
|
-
|
1064
|
-
// 加载前景动画
|
1065
|
-
this.createLottieAnimation(
|
1066
|
-
'front',
|
1067
|
-
itemId,
|
1068
|
-
item.frontAnimView,
|
1069
|
-
item
|
1070
|
-
);
|
1229
|
+
this.initItemAnimations(item);
|
1071
1230
|
});
|
1072
1231
|
}, 50); // 小延迟确保DOM已更新和旧动画已销毁
|
1073
1232
|
});
|
@@ -1094,39 +1253,66 @@ export default {
|
|
1094
1253
|
},
|
1095
1254
|
},
|
1096
1255
|
watch: {
|
1097
|
-
|
1098
|
-
handler(newVal) {
|
1099
|
-
this.generateDataList();
|
1100
|
-
this.$nextTick(() => {
|
1101
|
-
this.initLottieAnimations();
|
1102
|
-
});
|
1103
|
-
},
|
1104
|
-
deep: true,
|
1105
|
-
},
|
1106
|
-
// 监听列表数据变化,自动更新处理后的数据
|
1256
|
+
// 监听列表数据变化,自动更新缓存
|
1107
1257
|
'data.list': {
|
1108
1258
|
handler(newList, oldList) {
|
1109
|
-
//
|
1110
|
-
if (
|
1111
|
-
|
1112
|
-
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1259
|
+
// 跳过拖拽期间的更新,提高性能
|
1260
|
+
if (this.isTouchDragging) {
|
1261
|
+
return;
|
1262
|
+
}
|
1263
|
+
// 更精确的变化检测
|
1264
|
+
const hasLengthChange = newList.length !== oldList.length;
|
1265
|
+
if (hasLengthChange) {
|
1266
|
+
|
1267
|
+
// 清除DOM缓存
|
1268
|
+
this.clearGridItemsCache();
|
1269
|
+
|
1270
|
+
// 使用防抖处理列表变化
|
1271
|
+
if (!this.listChangeDebounced) {
|
1272
|
+
this.listChangeDebounced = this.debounce(() => {
|
1273
|
+
this.$nextTick(() => {
|
1274
|
+
// 优化内存使用
|
1275
|
+
// 确保每个项目都有完整的数据结构
|
1276
|
+
if (newList && Array.isArray(newList)) {
|
1277
|
+
newList.forEach((item, index) => {
|
1278
|
+
this.ensureItemStructure(item);
|
1279
|
+
});
|
1280
|
+
}
|
1281
|
+
|
1282
|
+
this.optimizeMemoryUsage();
|
1283
|
+
});
|
1284
|
+
}, 100);
|
1285
|
+
}
|
1286
|
+
this.listChangeDebounced();
|
1116
1287
|
}
|
1117
1288
|
},
|
1118
1289
|
deep: true,
|
1119
1290
|
},
|
1120
1291
|
},
|
1121
|
-
|
1292
|
+
updated(){
|
1293
|
+
if(this.isTouchDragging){return}
|
1294
|
+
},
|
1122
1295
|
mounted() {
|
1123
|
-
|
1296
|
+
|
1297
|
+
// 确保初始数据结构完整性
|
1298
|
+
if (this.data && this.data.list && Array.isArray(this.data.list)) {
|
1299
|
+
this.data.list.forEach((item, index) => {
|
1300
|
+
this.ensureItemStructure(item);
|
1301
|
+
});
|
1302
|
+
}
|
1303
|
+
|
1304
|
+
// 缓存容器元素
|
1305
|
+
this.containerElement = this.$el;
|
1306
|
+
|
1124
1307
|
// 初始获取grid-box宽度
|
1125
1308
|
this.updateGridBoxWidth();
|
1126
1309
|
// 获取并存储固定宽高
|
1127
1310
|
this.updateFixedItemSize();
|
1128
1311
|
// 初始化Lottie动画
|
1129
1312
|
this.initLottieAnimations();
|
1313
|
+
|
1314
|
+
// 初始化节流函数
|
1315
|
+
this.updatePositionThrottled = this.throttle(this.handleSwapLogic.bind(this), 100);
|
1130
1316
|
},
|
1131
1317
|
beforeDestroy() {
|
1132
1318
|
// 确保清理所有事件监听器
|
@@ -1138,10 +1324,19 @@ export default {
|
|
1138
1324
|
}
|
1139
1325
|
// 清理拖拽克隆元素
|
1140
1326
|
this.destroyDragClone();
|
1327
|
+
|
1328
|
+
// 清理缓存
|
1329
|
+
this.clearGridItemsCache();
|
1330
|
+
|
1331
|
+
// 清理节流和防抖函数
|
1332
|
+
this.updatePositionThrottled = null;
|
1333
|
+
this.listChangeDebounced = null;
|
1334
|
+
|
1335
|
+
// 清理容器引用
|
1336
|
+
this.containerElement = null;
|
1141
1337
|
},
|
1142
1338
|
created() {
|
1143
|
-
//
|
1144
|
-
this.generateDataList();
|
1339
|
+
// 组件创建时无需预处理数据,使用时按需获取全局数据
|
1145
1340
|
},
|
1146
1341
|
};
|
1147
1342
|
</script>
|
@@ -1176,21 +1371,28 @@ button {
|
|
1176
1371
|
grid-template-columns: repeat(var(--grid-cols), 1fr);
|
1177
1372
|
grid-gap: var(--grid-gap);
|
1178
1373
|
width: calc(100% - var(--grid-margin));
|
1374
|
+
/* 性能优化 */
|
1375
|
+
contain: layout style paint;
|
1376
|
+
transform: translateZ(0);
|
1179
1377
|
}
|
1180
1378
|
|
1181
1379
|
/* 优化排序过渡效果 */
|
1182
1380
|
.grid-fade-move {
|
1183
|
-
transition: transform 0.
|
1381
|
+
transition: transform 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
1184
1382
|
will-change: transform;
|
1185
1383
|
}
|
1186
1384
|
|
1187
1385
|
.grid-item {
|
1188
1386
|
position: relative;
|
1189
1387
|
box-sizing: border-box;
|
1190
|
-
transition: all 0.
|
1388
|
+
transition: all 0.25s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
1191
1389
|
will-change: transform, opacity, box-shadow;
|
1192
1390
|
overflow: hidden;
|
1193
1391
|
z-index: 1;
|
1392
|
+
/* 性能优化 */
|
1393
|
+
transform: translateZ(0);
|
1394
|
+
backface-visibility: hidden;
|
1395
|
+
perspective: 1000px;
|
1194
1396
|
}
|
1195
1397
|
|
1196
1398
|
.grid-box {
|
@@ -1252,7 +1454,7 @@ button {
|
|
1252
1454
|
.hover-target {
|
1253
1455
|
z-index: 50;
|
1254
1456
|
transform: scale(1.05);
|
1255
|
-
transition: all 0.
|
1457
|
+
transition: all 0.2s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
1256
1458
|
position: relative;
|
1257
1459
|
}
|
1258
1460
|
|
@@ -1314,4 +1516,6 @@ button {
|
|
1314
1516
|
transform: rotateZ(2deg);
|
1315
1517
|
}
|
1316
1518
|
}
|
1519
|
+
|
1520
|
+
|
1317
1521
|
</style>
|