HarmonyOS 拖拽动画循环时使用index后失效?

有个非常奇怪的地方 就是我forEach 只要用到了index拖拽动画就消失了 然后去掉就又好了?

把index去掉就有动画了

import curves from '@ohos.curves'

@Observed
class ItemInfo {
  id: string | number = ''
  text?: string = '测试数据'
  icon?: Resource | string = $r('app.media.app_icon')
}

@Component
@Entry
struct EditListComponent {
  @State myRotate: number = -10; // 旋转角度,初始值为-10
  @State list: ItemInfo[] = [] // 列表项数组
  @State allList: ItemInfo[] = [] // 所有列表项
  @State selectedItem: ItemInfo | undefined = undefined // 选中项,初始值为undefined
  private globalPositionY: number = 0 // 全局Y位置,初始值为0
  @State builderSize: number = 0 // 构建器尺寸
  @State containerHeight: number = 0 // 容器高度
  @State containerWidth: number = 0 // 容器宽度
  // 拖动元素初始位置
  @State dragStartX: number = 0 // 拖动开始的X位置状态
  @State dragStartY: number = 0 // 拖动开始的Y位置状态
  // 拖动元素偏移距离
  @State offsetX: number = 0 // 拖动偏移的X距离状态
  @State offsetY: number = 0 // 拖动偏移的Y距离状态
  // 拖动后滚动距离
  @State scrollLen: number = 0 // 滚动距离状态
  @State dragOffsetX: number = 0 // 拖动后的X偏移状态
  @State dragOffsetY: number = 0 // 拖动后的Y偏移状态
  private scroller: Scroller = new Scroller(); // 滚动控制器


  aboutToAppear(): void {
    setInterval(() => {
      this.myRotate = this.myRotate === -10 ? 10 : -10;
    }, 200);

    for (let index = 0; index < 20; index++) {
      let item: ItemInfo = {
        id: index,
        text: '测试数据' + index,
        icon: $r('app.media.app_icon')
      }
      this.allList.push(item)
    }
    this.list = this.allList
  }

  @Builder
  ItemBuilder(item: ItemInfo) {
    Column({ space: 15 }) {

      Image(
        'https://xxx.png'
      ).draggable(false).height(20).width(20)
      // Text(item.text)
      //   .fontSize(12)
      //   .maxLines(1)
      //   .textOverflow({ overflow: TextOverflow.Ellipsis })
      //   .hitTestBehavior(HitTestMode.None)
    }
    .width('100%')
    .aspectRatio(1)
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
    .borderRadius(10)
    .backgroundColor('#ccc')
    .rotate({
      z: 1,
      angle: this.myRotate
    })
  }

  @Builder
  dragBuilder() {
    Column({ space: 10 }) {
      if (this.selectedItem) {
        this.ItemBuilder(this.selectedItem)
      }
    }
    .hitTestBehavior(HitTestMode.None)
    .width(this.builderSize)
    .aspectRatio(1)
    .zIndex(1)
    .position({ x: this.dragStartX + this.offsetX, y: this.dragStartY + this.offsetY - this.globalPositionY })
    // .shadow({
    //   radius: vp2px(10),
    //   color: '#ff5f5f5f',
    //   fill: true
    // })
    .onAreaChange((_, newValue) => {
      if (this.selectedItem === undefined) {
        return
      }
      if (newValue.position.y && newValue.position.y < 0) {
        this.scroller.scrollEdge(Edge.Top, { velocity: 300 })
      } else if (newValue.position.y && newValue.position.y > this.containerHeight - this.builderSize) {
        this.scroller.scrollEdge(Edge.End, { velocity: 300 })
      } else {
        this.scroller.scrollTo(this.scroller.currentOffset())
      }
    })
  }

  reset() {
    this.selectedItem = undefined
    this.scrollLen = 0
    this.dragStartX = 0
    this.dragStartY = 0
    this.dragOffsetX = 0
    this.dragOffsetY = 0
    this.offsetX = 0
    this.offsetY = 0
    this.scroller.scrollTo(this.scroller.currentOffset())
  }

  itemMove(index: number, newIndex: number) {
    let tmp = this.list.splice(index, 1)
    this.list.splice(newIndex, 0, tmp[0])
  }

  handleDrag() {
    let index = -1
    this.list.forEach((item: ItemInfo, i: number) => {
      if (item.text === this?.selectedItem?.text) {
        index = i
      }
    })
    if (index < 0) {
      return
    }
    let x = this.offsetX - this.dragOffsetX
    let y = this.scrollLen + this.offsetY - this.dragOffsetY
    let itemLength = this.builderSize
    let translateLength = itemLength / 2
    let slashLength = itemLength / 2
    if (y >= translateLength && (x < slashLength && x > -slashLength)) {
      // 下
      if (index + 5 > (this.list.length)) {
        return
      }
      this.itemMove(index, index + 4)
      this.dragOffsetY += itemLength
    } else if (y <= -translateLength && (x < slashLength && x > -slashLength)) {
      // 上
      if (index < 4) {
        return
      }
      this.itemMove(index, index - 4)
      this.dragOffsetY -= itemLength
    } else if (x >= translateLength && (y < slashLength && y > -slashLength)) {
      // 右
      if (index + 1 >= this.list.length) {
        return
      }
      this.itemMove(index, index + 1)
      this.dragOffsetX += itemLength
    } else if (x <= -translateLength && (y < slashLength && y > -slashLength)) {
      // 左
      if (index % 4 === 0) {
        return
      }
      this.itemMove(index, index - 1)
      this.dragOffsetX -= itemLength
    } else if (x >= translateLength && y >= translateLength) {
      // 右下
      if (index % 4 === 3 || index + 5 > this.list.length) {
        return
      }
      this.itemMove(index, index + 5)
      this.dragOffsetX += itemLength
      this.dragOffsetY += itemLength
    } else if (x >= translateLength && y <= -translateLength) {
      // 右上
      if (index % 4 === 3 || (index - 3 < 0)) {
        return
      }
      this.itemMove(index, index - 3)
      this.dragOffsetX += itemLength
      this.dragOffsetY -= itemLength
    } else if (x <= -translateLength && y <= -translateLength) {
      // 左上
      if (index % 4 === 0 || (index - 5 < 0)) {
        return
      }
      this.itemMove(index, index - 5)
      this.dragOffsetX -= itemLength
      this.dragOffsetY -= itemLength
    } else if (x <= -translateLength && y >= translateLength) {
      // 左下
      if (index % 4 === 0 || index + 4 > this.list.length) {
        return
      }
      this.itemMove(index, index + 3)
      this.dragOffsetX -= itemLength
      this.dragOffsetY += itemLength
    }
  }

  build() {
    Column() {
      if (this.selectedItem !== undefined) {
        this.dragBuilder()
      }
      Scroll(this.scroller) {
        Column({ space: 10 }) {
          Grid() {
            ForEach(this.list, (item: ItemInfo,index:number) => {
              GridItem() {
                this.ItemBuilder(item)
              }
              .opacity(this.selectedItem?.text === item.text ? 0 : 1)
              .gesture(
                GestureGroup(GestureMode.Sequence,
                  LongPressGesture({ repeat: true })
                    .onAction((event?: GestureEvent) => {
                      if (this.selectedItem === undefined) {
                        this.selectedItem = item
                        this.dragStartX = Number(event?.target.area.globalPosition.x)
                        this.dragStartY = Number(event?.target.area.globalPosition.y)
                      }
                    })
                    .onActionEnd(() => {
                      this.reset()
                    }),
                  PanGesture({ fingers: 1, direction: null, distance: 0 })
                    .onActionStart(() => {
                      this.scrollLen = 0
                      this.selectedItem = item
                    })
                    .onActionUpdate(event => {
                      this.offsetY = event.offsetY
                      this.offsetX = event.offsetX
                      animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
                        this.handleDrag()
                      })
                    })
                    .onActionEnd(() => {
                      this.reset()
                    })
                )
                  .onCancel(() => {
                    this.reset()
                  })
              )
            }, (item: ItemInfo) => {
              return item.text
            })
          }
          .columnsTemplate('repeat(4, 1fr)')
          .columnsGap(10)
          .rowsGap(10)
        }
        .padding(10)
        .alignItems(HorizontalAlign.Start)
      }
      .width('100%')
      .height('500px')
      .align(Alignment.Top)
      .scrollBar(BarState.Off)
      .onDidScroll((_, y) => {
        if (this.selectedItem !== undefined) {
          this.scrollLen += y
          this.handleDrag()
        }
      })
      .onAreaChange((_, newValue) => {
        this.globalPositionY = Number(newValue.globalPosition.y)
        this.containerWidth = Number(newValue.width)
        this.containerHeight = Number(newValue.height)
        this.builderSize = (this.containerWidth - 50) / 4;
      })
    }
    .backgroundColor('#f2f3f4')
  }
}
阅读 529
1 个回答

使用ForEach进行渲染时,如果在键值生成规则中包含数据项的索引index,可能会导致拖拽动画循环时使用index后失效。因为ArkUI框架在键值生成规则中检测到重复的索引时,会认为数据项没有变化,从而不创建新的组件。这会导致拖拽动画无法正确执行。为了避免索引对拖拽动画的影响,建议在键值生成规则中避免使用数据项的索引。可以使用其他唯一的标识符来生成键值,例如对象数据中的唯一ID。这样可以确保每个数据项都有一个唯一的键值,从而保证拖拽动画的正常运行。

参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-rendering-control-foreach-V5\#使用建议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进