有个非常奇怪的地方 就是我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')
}
}
使用ForEach进行渲染时,如果在键值生成规则中包含数据项的索引index,可能会导致拖拽动画循环时使用index后失效。因为ArkUI框架在键值生成规则中检测到重复的索引时,会认为数据项没有变化,从而不创建新的组件。这会导致拖拽动画无法正确执行。为了避免索引对拖拽动画的影响,建议在键值生成规则中避免使用数据项的索引。可以使用其他唯一的标识符来生成键值,例如对象数据中的唯一ID。这样可以确保每个数据项都有一个唯一的键值,从而保证拖拽动画的正常运行。
参考文档:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-rendering-control-foreach-V5\#使用建议