@dolphinweex/weex-harmony 0.1.28 → 0.1.30

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dolphinweex/weex-harmony",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "description": "weex harmony adapter",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -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 processedList"
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(processedList[draggingIndex].itemView)"
120
+ :style="applyNormalAttribute(data.list[draggingIndex].itemView)"
121
121
  >
122
122
  <div class="flexBox">
123
123
  <img
124
- :src="processedList[draggingIndex].iconImage.url"
124
+ :src="data.list[draggingIndex].iconImage.url"
125
125
  :style="
126
- applyNormalAttribute(processedList[draggingIndex].iconImage)
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
- processedList[draggingIndex].itemView.isSelected
134
- ? processedList[draggingIndex].selectIcon.selectedUrl
135
- : processedList[draggingIndex].selectIcon.url
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(processedList[draggingIndex].selectIcon)
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(processedList[draggingIndex].text)]"
145
+ :style="[applyNormalAttribute(data.list[draggingIndex].text)]"
146
146
  >
147
- {{ processedList[draggingIndex].text.text }}
147
+ {{ data.list[draggingIndex].text.text }}
148
148
  </div>
149
149
  </div>
150
150
  </div>
@@ -152,6 +152,7 @@
152
152
  </template>
153
153
 
154
154
  <script>
155
+ import { weex_harmony_rebindGestureDelayed } from '@dolphinweex/harmony-webpack-adapter/registerGesture.js'; // 请替换为实际的loader文件路径
155
156
  import lottie from 'lottie-web';
156
157
  const scla = 2;
157
158
  export default {
@@ -173,7 +174,6 @@ export default {
173
174
  return {
174
175
  targetTouchX: 0,
175
176
  targetTouchY: 0,
176
- processedList: [],
177
177
  selected: null,
178
178
  longPressTimer: null,
179
179
  longPressDuration: 350,
@@ -198,9 +198,11 @@ export default {
198
198
  fixedItemWidth: 0,
199
199
  fixedItemHeight: 0,
200
200
  animationFrameId: null,
201
- lastBestTarget: null,
202
- targetChangeTime: null,
203
- pendingTarget: null,
201
+ isProcessingData: false,
202
+ // 手势事件配置 - 根据实际使用的手势事件来配置
203
+ gestureEventMap: [],
204
+ // 元素ID - 用于标识需要绑定手势的元素
205
+ elementId: 'grid-item'
204
206
  };
205
207
  },
206
208
  computed: {
@@ -208,6 +210,7 @@ export default {
208
210
  return {
209
211
  '--grid-cols': this.data.spanCount || this.cols,
210
212
  '--grid-gap': `${this.data.spaceSize || 8}px`,
213
+ '--grid-margin': `${(this.data.layoutConfig.marginStart ) + (this.data.layoutConfig.marginEnd )}px`,
211
214
  marginTop: this.data.layoutConfig.marginTop * scla,
212
215
  marginBottom: this.data.layoutConfig.marginBottom * scla,
213
216
  marginLeft: this.data.layoutConfig.marginStart * scla,
@@ -220,7 +223,7 @@ export default {
220
223
  return { display: 'none' };
221
224
  }
222
225
 
223
- const item = this.processedList[this.draggingIndex];
226
+ const item = this.data.list[this.draggingIndex];
224
227
  let itemView = {};
225
228
  if (item) {
226
229
  itemView = this.applyNormalAttribute(item.itemView);
@@ -245,10 +248,7 @@ export default {
245
248
  updateListItem(data) {
246
249
  console.log('updateListItem收到数据:', JSON.stringify(data));
247
250
  const id = data.itemView.id;
248
- const index = this.processedList.findIndex(
249
- (item) => item.itemView.id == id
250
- );
251
- const dataIndex = this.data.list.findIndex(
251
+ const index = this.data.list.findIndex(
252
252
  (item) => item.itemView.id == id
253
253
  );
254
254
 
@@ -257,89 +257,148 @@ export default {
257
257
  }
258
258
 
259
259
  // 检查是否有动画URL的变化
260
- const oldBgAnimUrl = this.processedList[index].bgAnimView.animUrl || '';
261
- const oldLoadingAnimUrl =
262
- this.processedList[index].loadingAnimView.animUrl || '';
263
- const oldFrontAnimUrl =
264
- this.processedList[index].frontAnimView.animUrl || '';
265
-
260
+ const oldBgAnimUrl = this.data.list[index].bgAnimView.animUrl || '';
261
+ const oldLoadingAnimUrl = this.data.list[index].loadingAnimView.animUrl || '';
262
+ const oldFrontAnimUrl = this.data.list[index].frontAnimView.animUrl || '';
266
263
  const newBgAnimUrl = data.bgAnimView.animUrl || '';
267
264
  const newLoadingAnimUrl = data.loadingAnimView.animUrl || '';
268
265
  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
266
 
279
- // 如果动画URL有变化,重新初始化相关的Lottie动画
280
- console.log('检测到动画URL变化,重新初始化动画');
267
+ // 合并全局数据到新数据
268
+ const mergedData = this.mergeItemData(data);
269
+
270
+ // 更新数据
271
+ this.$set(this.data.list, index, mergedData);
281
272
 
282
- // 等待下一帧DOM更新完成后再操作
283
273
  this.$nextTick(() => {
284
- // 清理旧的动画实例
285
274
  const itemId = String(data.itemView.id);
286
275
 
287
- // 清除所有相关的动画实例
288
- const animKeys = [
289
- `bg-${itemId}`,
290
- `loading-${itemId}`,
291
- `front-${itemId}`,
292
- ];
293
- animKeys.forEach((key) => {
294
- if (this.lottieAnimations[key]) {
295
- console.log(`销毁动画实例: ${key}`);
296
- const anim = this.lottieAnimations[key];
297
- try {
298
- anim.removeEventListener('complete');
299
- } catch (e) {
300
- console.warn(`移除事件监听器失败: ${e.message}`);
301
- }
302
- anim.destroy();
303
- delete this.lottieAnimations[key];
304
- }
305
- });
276
+ // 处理背景动画
277
+ if (oldBgAnimUrl !== newBgAnimUrl && newBgAnimUrl) {
278
+ this.createLottieAnimation('bg', itemId, mergedData.bgAnimView, mergedData);
279
+ }
306
280
 
307
- console.log('清理完成,开始重新初始化动画');
281
+ // 处理加载动画
282
+ if (oldLoadingAnimUrl !== newLoadingAnimUrl && newLoadingAnimUrl) {
283
+ this.createLottieAnimation('loading', itemId, mergedData.loadingAnimView, mergedData);
284
+ }
308
285
 
309
- // 延迟一点时间确保DOM完全更新
310
- setTimeout(() => {
311
- // 重新初始化背景动画
312
- if (oldBgAnimUrl !== newBgAnimUrl) {
313
- this.loadLottieAnimation(
314
- data.bgAnimView,
315
- `bgAnim-${itemId}`,
316
- `bg-${itemId}`,
317
- data
318
- );
319
- }
286
+ // 处理前景动画
287
+ if (oldFrontAnimUrl !== newFrontAnimUrl && newFrontAnimUrl) {
288
+ this.createLottieAnimation('front', itemId, mergedData.frontAnimView, mergedData);
289
+ }
290
+ // 重新绑定手势事件 - 解决DOM更新后手势失效问题
291
+ this.rebindGestureEvents();
292
+ });
293
+ },
294
+ // 重新绑定手势事件的方法
295
+ rebindGestureEvents() {
296
+ try {
297
+ // 延迟执行确保DOM完全更新
298
+ weex_harmony_rebindGestureDelayed(
299
+ this.$el, // 组件根元素
300
+ this.gestureEventMap, // 手势事件配置数组
301
+ this.elementId, // 元素类名ID
302
+ 100 // 延迟100ms
303
+ );
304
+ console.log('手势事件重新绑定完成');
305
+ } catch (error) {
306
+ console.error('重新绑定手势事件失败:', error);
307
+ }
308
+ },
309
+ // 初始化手势事件配置
310
+ initGestureConfig() {
311
+ // 根据你实际使用的手势事件来配置
312
+ this.gestureEventMap = [
313
+ {
314
+ name: "@touchstart",
315
+ handler: this.startLongPress,
316
+ params: ['event', 'index'],
317
+ isEvent: true,
318
+ modifier: []
319
+ },
320
+ {
321
+ name: "@touchmove",
322
+ handler: this.checkLongPressMove,
323
+ params: [],
324
+ isEvent: true,
325
+ modifier: []
326
+ },
327
+ {
328
+ name: "@touchend",
329
+ handler: this.endTouch,
330
+ params: [],
331
+ isEvent: true,
332
+ modifier: []
333
+ },
334
+ {
335
+ name: "@touchcancel",
336
+ handler: this.endTouch,
337
+ params: [],
338
+ isEvent: true,
339
+ modifier: []
340
+ }
341
+ ];
342
+ },
343
+ // 简化的动画创建方法
344
+ createLottieAnimation(type, itemId, animView, itemData) {
345
+ const animKey = `${type}-${itemId}`;
346
+ const refName = `${type}Anim-${itemId}`;
347
+
348
+ // 销毁旧动画
349
+ if (this.lottieAnimations[animKey]) {
350
+ this.lottieAnimations[animKey].destroy();
351
+ delete this.lottieAnimations[animKey];
352
+ }
320
353
 
321
- if (oldLoadingAnimUrl !== newLoadingAnimUrl) {
322
- this.loadLottieAnimation(
323
- data.loadingAnimView,
324
- `loadingAnim-${itemId}`,
325
- `loading-${itemId}`,
326
- data
327
- );
328
- }
354
+ // 检查DOM元素是否存在
355
+ const animEl = this.$refs[refName];
356
+ if (!animEl || !animEl[0] || !animView.animUrl) {
357
+ return;
358
+ }
329
359
 
330
- if (oldFrontAnimUrl !== newFrontAnimUrl) {
331
- this.loadLottieAnimation(
332
- data.frontAnimView,
333
- `frontAnim-${itemId}`,
334
- `front-${itemId}`,
335
- data
336
- );
337
- }
338
- }, 100); // 增加延迟确保DOM已更新和旧动画已完全销毁
339
- });
360
+ // 检查可见性
361
+ if (typeof animView.visibility === 'number' && animView.visibility === 0) {
362
+ return;
363
+ }
364
+
365
+ try {
366
+ const anim = lottie.loadAnimation({
367
+ container: animEl[0],
368
+ renderer: 'svg',
369
+ loop: Boolean(itemData.animRepeatCount) || false,
370
+ autoplay: true,
371
+ path: animView.animUrl,
372
+ rendererSettings: {
373
+ preserveAspectRatio: 'xMidYMid slice',
374
+ progressiveLoad: true,
375
+ hideOnTransparent: true,
376
+ },
377
+ });
378
+
379
+ this.lottieAnimations[animKey] = anim;
380
+
381
+ // 非循环动画完成后清理
382
+ if (!itemData.animRepeatCount) {
383
+ anim.addEventListener('complete', () => {
384
+ if (this.lottieAnimations[animKey] === anim) {
385
+ anim.destroy();
386
+ delete this.lottieAnimations[animKey];
387
+
388
+ // 清空对应的animUrl数据
389
+ const index = this.data.list.findIndex(i => i.itemView.id == itemId);
390
+ if (index !== -1) {
391
+ this.$set(this.data.list[index][`${type}AnimView`], 'animUrl', '');
392
+ }
393
+ }
394
+ });
395
+ }
396
+ } catch (error) {
397
+ console.error(`创建${type}动画失败:`, error);
398
+ }
340
399
  },
341
400
  getListData(callback) {
342
- let data = Array.from(this.processedList);
401
+ let data = Array.from(this.data.list);
343
402
  data = data.map((item) => ({
344
403
  ...item,
345
404
  itemView: {
@@ -364,7 +423,7 @@ export default {
364
423
  },
365
424
  selectIconClick(e, item, index) {
366
425
  e.preventDefault();
367
- e.stopPropagation(); // 阻止事件冒泡到itemViewClick
426
+ e.stopPropagation();
368
427
  if (item.selectIcon.visibility) {
369
428
  item.itemView.isSelected = !item.itemView.isSelected;
370
429
  }
@@ -380,16 +439,10 @@ export default {
380
439
  },
381
440
 
382
441
  itemViewClick(e, item, index) {
383
- // 如果事件已经被处理或被阻止默认行为,则不继续执行
384
442
  if (e.defaultPrevented) {
385
443
  return;
386
444
  }
387
- const target = {
388
- ...item.itemView,
389
- id: String(item.itemView.id),
390
- index,
391
- ...e,
392
- };
445
+
393
446
  console.log('itemViewClick');
394
447
  // 确保取消任何长按计时器
395
448
  this.cancelLongPress();
@@ -401,10 +454,13 @@ export default {
401
454
  }
402
455
  e.preventDefault();
403
456
  e.stopPropagation();
404
-
457
+ const target = {
458
+ ...item.itemView,
459
+ id: String(item.itemView.id),
460
+ index,
461
+ ...e,
462
+ };
405
463
  this.$emit('onClickItem', target);
406
- this.data.isEditing&&item.selectIcon.visibility&&this.$emit('onSelectItems', target);
407
-
408
464
  },
409
465
 
410
466
  actionClick(e, item, index) {
@@ -425,14 +481,12 @@ export default {
425
481
  return false;
426
482
  },
427
483
 
428
- // 新增方法:清理除指定项目外的所有动画实例
429
484
  cleanupOtherAnimations(currentItemId) {
430
485
  console.log('清理其他项目的动画实例,当前项目ID:', currentItemId);
431
486
 
432
487
  Object.keys(this.lottieAnimations).forEach((key) => {
433
488
  if (!key.includes(`-${currentItemId}`)) {
434
489
  if (this.lottieAnimations[key]) {
435
- console.log(`销毁其他项目的动画实例: ${key}`);
436
490
  const anim = this.lottieAnimations[key];
437
491
 
438
492
  // 不要立即销毁,而是先停止动画
@@ -477,37 +531,55 @@ export default {
477
531
  });
478
532
  },
479
533
 
480
- // 生成处理后的数据列表
481
534
  generateDataList() {
482
535
  if (!this.data || !this.data.list || !this.data.globalData) {
483
- return [];
536
+ return;
484
537
  }
485
538
 
486
- return this.data.list.map((item) => {
487
- const processedItem = {};
488
-
489
- // 获取所有可能的类型
490
- const types = [
491
- 'itemView',
492
- 'text',
493
- 'actionButton',
494
- 'rightIcon',
495
- 'leftTopIcon',
496
- 'iconImage',
497
- 'selectIcon',
498
- 'loadingAnimView',
499
- 'bgAnimView',
500
- 'frontAnimView',
501
- ];
539
+ // 防止死循环
540
+ if (this.isProcessingData) {
541
+ return;
542
+ }
543
+
544
+ this.isProcessingData = true;
502
545
 
503
- // 对每种类型进行处理
504
- types.forEach((type) => {
505
- // 使用mergeDate合并全局数据和项目数据
506
- processedItem[type] = this.mergeDate(item[type] || {}, type);
546
+ try {
547
+ // 直接处理 this.data.list,为每个项目合并全局数据
548
+ this.data.list.forEach((item, index) => {
549
+ const processedItem = this.mergeItemData(item);
550
+ this.$set(this.data.list, index, processedItem);
551
+ });
552
+ } finally {
553
+ // 确保标志位被重置
554
+ this.$nextTick(() => {
555
+ this.isProcessingData = false;
507
556
  });
508
- return processedItem;
557
+ }
558
+ },
559
+
560
+ mergeItemData(item) {
561
+ const processedItem = {};
562
+ const types = [
563
+ 'itemView',
564
+ 'text',
565
+ 'actionButton',
566
+ 'rightIcon',
567
+ 'leftTopIcon',
568
+ 'iconImage',
569
+ 'selectIcon',
570
+ 'loadingAnimView',
571
+ 'bgAnimView',
572
+ 'frontAnimView',
573
+ ];
574
+
575
+ types.forEach((type) => {
576
+ // 使用mergeDate合并全局数据和项目数据
577
+ processedItem[type] = this.mergeDate(item[type] || {}, type);
509
578
  });
579
+
580
+ return processedItem;
510
581
  },
582
+
511
583
  getItemStyle(item, type) {
512
584
  const result = this.mergeDate(item[type] || {}, type);
513
585
  console.log(type, this.applyNormalAttribute(result), 'ggg');
@@ -538,18 +610,14 @@ export default {
538
610
  borderRadius: `${(data.cornerRadius || 0) * scla}px`,
539
611
  opacity: data.alpha || 1,
540
612
  };
541
- // 特殊处理visibility和display
542
613
  if (typeof data.visibility === 'number') {
543
614
  if (data.visibility === 0) {
544
- // 如果visibility为0,则完全隐藏元素
545
615
  style.display = 'none';
546
616
  } else {
547
- // 否则保持原有display属性,并设置visibility
548
617
  style.display = data.display || '';
549
618
  style.visibility = data.visibility === 1 ? 'visible' : 'hidden';
550
619
  }
551
620
  } else {
552
- // 如果未设置visibility,保持原有display
553
621
  style.display = data.display || '';
554
622
  }
555
623
 
@@ -582,18 +650,6 @@ export default {
582
650
  this.$emit('onDragEventStart', e);
583
651
 
584
652
  // 只在编辑模式下阻止事件冒泡和默认行为
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
653
  this.cancelLongPress();
598
654
  // 记录起始触摸位置和索引
599
655
  const touch = e.changedTouches[0];
@@ -603,11 +659,6 @@ export default {
603
659
  this.longPressStartIndex = index;
604
660
  this.isScrolling = false; // 重置滚动状态
605
661
 
606
- // 添加临时事件监听器,在长按期间阻止滚动
607
- document.addEventListener('touchmove', this.onTouchMove, {
608
- passive: false // 设置为false以允许阻止默认行为
609
- });
610
-
611
662
  // 设置新的长按计时器
612
663
  this.longPressTimer = setTimeout(() => {
613
664
  // 只有在没有滚动的情况下才激活拖拽模式
@@ -671,13 +722,6 @@ export default {
671
722
  this.draggingIndex = index;
672
723
  this.isTouchDragging = true;
673
724
 
674
- // 此时添加阻止默认行为的事件监听,但只影响拖拽状态下的行为
675
- document.addEventListener('touchmove', this.onTouchMove, {
676
- passive: false, // 只有在拖拽模式下才阻止默认行为
677
- });
678
- document.addEventListener('touchend', this.onTouchEnd, {
679
- passive: true,
680
- });
681
725
 
682
726
  // 使用存储的固定宽高
683
727
  this.draggedItemRect = {
@@ -696,11 +740,8 @@ export default {
696
740
  // 设置克隆元素的初始位置
697
741
  this.dragCloneX = rect.left;
698
742
  this.dragCloneY = rect.top;
699
-
700
- // 显示拖拽克隆
701
743
  this.isDragClone = true;
702
744
 
703
- // 立即执行一次定位以确保初始位置准确
704
745
  requestAnimationFrame(() => {
705
746
  this.dragCloneX = this.touchStartX - this.touchOffsetX;
706
747
  this.dragCloneY = this.touchStartY - this.touchOffsetY;
@@ -732,7 +773,7 @@ export default {
732
773
  if (e.oriEvent) {
733
774
  e.oriEvent.preventDefault();
734
775
  e.oriEvent.stopPropagation();
735
- e.oriEvent.stopImmediatePropagation(); // 添加这行以确保事件不会继续传播
776
+ e.oriEvent.stopImmediatePropagation();
736
777
  }
737
778
  e.preventDefault && e.preventDefault();
738
779
  e.stopPropagation && e.stopPropagation();
@@ -750,122 +791,72 @@ export default {
750
791
  this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
751
792
  }
752
793
 
753
- console.log('开始移动ddddddd', touch, touch.pageY, touch.pageX);
754
- const touchX = touch.pageX;
755
- const touchY = touch.pageY;
794
+ // 简化的交换逻辑
795
+ this.handleSwapLogic(touch.pageX, touch.pageY);
796
+ },
797
+
798
+ // 简化的交换处理逻辑
799
+ handleSwapLogic(touchX, touchY) {
800
+ // 控制交换频率,避免过于频繁的交换
801
+ const now = Date.now();
802
+ if (now - this.lastSwapTime < 300) return;
756
803
 
757
804
  // 获取所有网格元素
758
805
  const gridItems = Array.from(document.querySelectorAll('.grid-item'));
806
+ let targetIndex = null;
759
807
 
760
- let bestTarget = null;
761
- let maxCenterScore = 0; // 用于记录最佳的中心区域得分
762
-
763
- // ✅ 优化:精确坐标检测,偏向中心区域的元素
808
+ // 寻找触摸点所在的目标元素
764
809
  for (const item of gridItems) {
765
810
  const itemIndex = parseInt(item.getAttribute('data-index'), 10);
766
811
  if (itemIndex === this.draggingIndex) continue;
767
812
 
768
813
  const rect = item.getBoundingClientRect();
769
-
770
- // 计算触摸点在元素内的深度,用于判断是否足够深入(放宽到整个元素区域)
771
- const insetThresholdX = rect.width;
772
- const insetThresholdY = rect.height;
773
-
774
- // 判断触摸点是否足够深入元素内部(只要在元素内部即可)
775
- const isDeepEnough =
776
- touchX >= rect.left &&
777
- touchX <= rect.right &&
778
- touchY >= rect.top &&
779
- touchY <= rect.bottom;
780
-
781
- // 只有当触摸点足够深入时才考虑此元素作为交换目标
782
- if (isDeepEnough) {
783
- // 计算触摸点到元素中心的距离比例(越接近中心,值越大)
784
- const centerX = (rect.left + rect.right) / 2;
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;
814
+
815
+ // 触摸点是否在元素的中心区域内
816
+ const centerX = (rect.left + rect.right) / 2;
817
+ const centerY = (rect.top + rect.bottom) / 2;
818
+ const halfWidth = rect.width * 0.4; // 缩小判断区域到40%,避免边缘误触
819
+ const halfHeight = rect.height * 0.4;
820
+
821
+ // 检查触摸点是否在元素的中心区域内
822
+ if (
823
+ touchX >= centerX - halfWidth &&
824
+ touchX <= centerX + halfWidth &&
825
+ touchY >= centerY - halfHeight &&
826
+ touchY <= centerY + halfHeight
827
+ ) {
828
+ targetIndex = itemIndex;
829
+ break; // 找到第一个符合条件的就退出
819
830
  }
820
- } else if (bestTarget === null) {
821
- // 如果没有目标,清除防抖状态
822
- this.targetChangeTime = null;
823
- this.pendingTarget = null;
824
831
  }
825
832
 
826
- // 使用已稳定的目标进行交换
827
- bestTarget = this.lastBestTarget;
828
-
829
- // 控制交换频率,改为250ms,大幅增加稳定性
830
- const now = Date.now();
831
- if (now - this.lastSwapTime < 250 || bestTarget === null) return;
832
-
833
- // 执行交换逻辑
834
- if (bestTarget !== this.draggingIndex) {
835
- console.log(`交换元素: ${this.draggingIndex} 和 ${bestTarget}`);
833
+ // 设置悬停高亮
834
+ this.hoverIndex = targetIndex;
836
835
 
837
- // 使用ES6解构直接交换数组元素
836
+ // 执行交换
837
+ if (targetIndex !== null && targetIndex !== this.draggingIndex) {
838
838
  [
839
- this.processedList[this.draggingIndex],
840
- this.processedList[bestTarget],
839
+ this.data.list[this.draggingIndex],
840
+ this.data.list[targetIndex],
841
841
  ] = [
842
- this.processedList[bestTarget],
843
- this.processedList[this.draggingIndex],
844
- ];
845
-
846
- // 同时更新原始数据,保持同步
847
- [this.data.list[this.draggingIndex], this.data.list[bestTarget]] = [
848
- this.data.list[bestTarget],
842
+ this.data.list[targetIndex],
849
843
  this.data.list[this.draggingIndex],
850
844
  ];
845
+ console.log(`交换: ${this.draggingIndex} ↔ ${targetIndex}`);
851
846
 
852
847
  // 更新拖拽索引
853
- this.draggingIndex = bestTarget;
848
+ this.draggingIndex = targetIndex;
854
849
  this.lastSwapTime = now;
855
850
  }
856
851
  },
857
852
 
858
- // 新增:平滑更新拖拽位置的方法
853
+ // 平滑更新拖拽位置的方法
859
854
  updateDragPosition() {
860
- // 计算当前位置到目标位置的平滑过渡
861
855
  const easing = 0.6; // 增加缓动系数,让拖拽更跟手
862
-
863
856
  this.dragCloneX +=
864
857
  (this.targetTouchX - this.touchOffsetX - this.dragCloneX) * easing;
865
858
  this.dragCloneY +=
866
859
  (this.targetTouchY - this.touchOffsetY - this.dragCloneY) * easing;
867
-
868
- // 如果距离目标很近,直接设为目标位置以避免微小抖动
869
860
  if (
870
861
  Math.abs(this.dragCloneX - (this.targetTouchX - this.touchOffsetX)) <
871
862
  0.5
@@ -876,8 +867,6 @@ export default {
876
867
  0.5
877
868
  )
878
869
  this.dragCloneY = this.targetTouchY - this.touchOffsetY;
879
-
880
- // 继续下一帧动画
881
870
  this.animationFrameId = requestAnimationFrame(this.updateDragPosition);
882
871
  },
883
872
 
@@ -887,17 +876,8 @@ export default {
887
876
  this.$emit('onDragEventEnd', e);
888
877
  // 如果只是短按(还在计时),就取消长按
889
878
  this.cancelLongPress();
890
-
891
879
  // 如果正在拖拽中,确保清理状态
892
880
  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
881
  // 取消动画帧
902
882
  if (this.animationFrameId) {
903
883
  cancelAnimationFrame(this.animationFrameId);
@@ -908,28 +888,12 @@ export default {
908
888
  this.isDragClone = false;
909
889
  this.hoverIndex = null;
910
890
  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
891
  }
919
892
  },
920
893
 
921
894
  // 触摸结束处理 - 全局事件
922
895
  onTouchEnd(e) {
923
896
  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
897
  // 取消动画帧
934
898
  if (this.animationFrameId) {
935
899
  cancelAnimationFrame(this.animationFrameId);
@@ -985,168 +949,27 @@ export default {
985
949
 
986
950
  console.log(
987
951
  '开始初始化Lottie动画,列表项数量:',
988
- this.processedList.length
952
+ this.data.list.length
989
953
  );
990
954
 
991
955
  // 延迟一点时间确保DOM完全更新并且之前的动画已被销毁
992
956
  setTimeout(() => {
993
957
  // 为每个列表项初始化动画
994
- this.processedList.forEach((item) => {
958
+ this.data.list.forEach((item) => {
995
959
  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
960
  // 加载背景动画
1002
- this.loadLottieAnimation(
1003
- item.bgAnimView,
1004
- `bgAnim-${itemId}`,
1005
- `bg-${itemId}`,
1006
- item
1007
- );
961
+ this.createLottieAnimation('bg', itemId, item.bgAnimView, item);
1008
962
 
1009
963
  // 加载加载中动画
1010
- this.loadLottieAnimation(
1011
- item.loadingAnimView,
1012
- `loadingAnim-${itemId}`,
1013
- `loading-${itemId}`,
1014
- item
1015
- );
964
+ this.createLottieAnimation('loading', itemId, item.loadingAnimView, item);
1016
965
 
1017
966
  // 加载前景动画
1018
- this.loadLottieAnimation(
1019
- item.frontAnimView,
1020
- `frontAnim-${itemId}`,
1021
- `front-${itemId}`,
1022
- item
1023
- );
967
+ this.createLottieAnimation('front', itemId, item.frontAnimView, item);
1024
968
  });
1025
969
  }, 50); // 小延迟确保DOM已更新和旧动画已销毁
1026
970
  });
1027
971
  },
1028
972
 
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
973
  // 添加新方法用于获取和存储固定宽高
1151
974
  updateFixedItemSize() {
1152
975
  this.$nextTick(() => {
@@ -1168,110 +991,55 @@ export default {
1168
991
  },
1169
992
  },
1170
993
  watch: {
1171
- // 监听初始数据变化
1172
- initialItems: {
1173
- handler(newItems) {
1174
- if (!this.isTouchDragging) {
1175
- this.items = [...newItems];
1176
- }
1177
- },
1178
- deep: true,
994
+ 'data.isEditing':{
995
+ handler(newVal){
996
+ if(!newVal){
997
+ this.$set(this.data, 'list', this.data.list);
998
+ }
999
+ },
1000
+ deep:true
1179
1001
  },
1180
1002
  // 监听列表数据变化,自动更新处理后的数据
1181
1003
  'data.list': {
1182
1004
  handler(newList, oldList) {
1183
- this.processedList = this.generateDataList();
1005
+ // 防止死循环
1006
+ if (this.isProcessingData) {
1007
+ return;
1008
+ }
1009
+
1010
+ console.log('检测到data.list变化');
1011
+ this.generateDataList();
1184
1012
  this.$nextTick(() => {
1185
- // 再次使用 nextTick 确保 DOM 完全更新
1186
- this.$nextTick(() => {
1187
- const gridItems = document.querySelectorAll('.grid-item');
1188
- gridItems.forEach((item, index) => {
1189
- const hasEvents = item._vei;
1190
- if (!hasEvents) {
1191
- item.addEventListener('touchstart', (e) => this.startLongPress(e, index), { passive: false });
1192
- item.addEventListener('touchmove', this.checkLongPressMove, { passive: false });
1193
- item.addEventListener('touchend', this.endTouch, { passive: false });
1194
- item.addEventListener('touchcancel', this.endTouch, { passive: false });
1195
- }
1196
- });
1197
- this.initLottieAnimations();
1198
- });
1013
+ this.initLottieAnimations();
1014
+ this.rebindGestureEvents();
1199
1015
  });
1200
1016
  },
1201
1017
  deep: true,
1202
1018
  },
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
1019
  },
1020
+
1214
1021
  mounted() {
1022
+ this.initGestureConfig()
1215
1023
  console.log(this.data, 'djdjdjdjdjdjdj');
1216
1024
  // 初始获取grid-box宽度
1217
1025
  this.updateGridBoxWidth();
1218
1026
  // 获取并存储固定宽高
1219
1027
  this.updateFixedItemSize();
1220
-
1221
- // 监听窗口大小变化,更新grid-box宽度
1222
- window.addEventListener('resize', () => {
1223
- this.updateGridBoxWidth();
1224
- this.updateFixedItemSize();
1225
- });
1226
-
1227
1028
  // 初始化Lottie动画
1228
1029
  this.initLottieAnimations();
1229
1030
  },
1230
1031
  beforeDestroy() {
1231
1032
  // 确保清理所有事件监听器
1232
1033
  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
1034
  // 清理动画帧
1245
1035
  if (this.animationFrameId) {
1246
1036
  cancelAnimationFrame(this.animationFrameId);
1247
1037
  this.animationFrameId = null;
1248
1038
  }
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
1039
  },
1272
1040
  created() {
1273
1041
  // 初始化处理后的数据列表
1274
- this.processedList = this.generateDataList();
1042
+ this.generateDataList();
1275
1043
  },
1276
1044
  };
1277
1045
  </script>
@@ -1302,7 +1070,7 @@ button {
1302
1070
  display: grid;
1303
1071
  grid-template-columns: repeat(var(--grid-cols), 1fr);
1304
1072
  grid-gap: var(--grid-gap);
1305
- width: calc(100% - 74px);
1073
+ width: calc(100% - var(--grid-margin));
1306
1074
  }
1307
1075
 
1308
1076
  /* 优化排序过渡效果 */