开发步骤
- Grid布局及不同大小的GridItem界面开发。
Grid() {
ForEach(this.numbers, (item: number) => {
GridItem() {
Stack({ alignContent: Alignment.TopEnd }) {
Image(this.changeImage(item))
.width('100%')
.borderRadius(16)
.objectFit(this.curBp === 'md' ? ImageFit.Fill : ImageFit.Cover)
.draggable(false)
.animation({ curve: Curve.Sharp, duration: 300 })
}
}
.rowStart(0)
.rowEnd(this.getRowEnd(item))
.scale({ x: this.scaleItem === item ? 1.02 : 1, y: this.scaleItem === item ? 1.02 : 1 })
.zIndex(this.dragItem === item ? 1 : 0)
.translate(this.dragItem === item ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 })
.hitTestBehavior(this.isDraggable(this.numbers.indexOf(item)) ? HitTestMode.Default : HitTestMode.None)
// ...
}, (item: number) => item.toString())
}
.width('100%')
.height('100%')
.editMode(true)
.scrollBar(BarState.Off)
.columnsTemplate('1fr 1fr')
.supportAnimation(true)
.columnsGap(12)
.rowsGap(12)
.enableScrollInteraction(true)
- 定义网格元素移动过程中的相关计算函数,其中itemMove()方法是实现元素交换重新排序的方法。
itemMove(index: number, newIndex: number): void {
if (!this.isDraggable(newIndex)) {
return;
}
let tmp = this.numbers.splice(index, 1);
this.numbers.splice(newIndex, 0, tmp[0]);
this.bigItemIndex = this.numbers.findIndex((item) => item === 0);
}
isInLeft(index: number) {
if (index === this.bigItemIndex) {
return index % 2 == 0;
}
if (this.bigItemIndex % 2 === 0) {
if (index - this.bigItemIndex === 2 || index - this.bigItemIndex === 1) {
return false;
}
} else {
if (index - this.bigItemIndex === 1) {
return false;
}
}
if (index > this.bigItemIndex) {
return index % 2 == 1;
} else {
return index % 2 == 0;
}
}
down(index: number): void {
if ([this.numbers.length - 1, this.numbers.length - 2].includes(index)) {
return;
}
if (this.bigItemIndex - index === 1) {
return;
}
if ([14, 15].includes(this.bigItemIndex) && this.bigItemIndex === index) {
return;
}
this.offsetY -= this.FIX_VP_Y;
this.dragRefOffSetY += this.FIX_VP_Y;
if (index - 1 === this.bigItemIndex) {
this.itemMove(index, index + 1);
} else {
this.itemMove(index, index + 2);
}
}
up(index: number): void {
if (!this.isDraggable(index - 2)) {
return;
}
if (index - this.bigItemIndex === 3) {
return;
}
this.offsetY += this.FIX_VP_Y;
this.dragRefOffSetY -= this.FIX_VP_Y;
if (this.bigItemIndex === index) {
this.itemMove(index, index - 2);
} else {
if (index - 2 === this.bigItemIndex) {
this.itemMove(index, index - 1);
} else {
this.itemMove(index, index - 2);
}
}
}
left(index: number): void {
if (this.bigItemIndex % 2 === 0) {
if (index - this.bigItemIndex === 2) {
return;
}
}
if (this.isInLeft(index)) {
return;
}
if (!this.isDraggable(index - 1)) {
return;
}
this.offsetX += this.FIX_VP_X;
this.dragRefOffSetX -= this.FIX_VP_X;
this.itemMove(index, index - 1)
}
right(index: number): void {
if (this.bigItemIndex % 2 === 1) {
if (index - this.bigItemIndex === 1) {
return;
}
}
if (!this.isInLeft(index)) {
return;
}
if (!this.isDraggable(index + 1)) {
return;
}
this.offsetX -= this.FIX_VP_X;
this.dragRefOffSetX += this.FIX_VP_X;
this.itemMove(index, index + 1)
}
isDraggable(index: number): boolean {
return index >= 0;
}
- GridItem绑定组合手势:长按,拖拽。并在手势的回调函数中设置显式动画。
.gesture(
GestureGroup(GestureMode.Sequence,
LongPressGesture({ repeat: true })
.onAction(() => {
this.getUIContext().animateTo({ curve: Curve.Friction, duration: 300 }, () => {
this.scaleItem = item;
})
})
.onActionEnd(() => {
this.getUIContext().animateTo({ curve: Curve.Friction, duration: 300 }, () => {
this.scaleItem = -1;
})
}),
PanGesture({ fingers: 1, direction: null, distance: 0 })
.onActionStart(() => {
this.dragItem = item;
this.dragRefOffSetX = 0;
this.dragRefOffSetY = 0;
})
.onActionUpdate((event: GestureEvent) => {
this.offsetX = event.offsetX - this.dragRefOffSetX;
this.offsetY = event.offsetY - this.dragRefOffSetY;
this.getUIContext().animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
let index = this.numbers.indexOf(this.dragItem);
if (this.offsetY >= this.FIX_VP_Y / 2 && (this.offsetX <= 44 && this.offsetX >= -44)) {
this.down(index);
} else if (this.offsetY <= -this.FIX_VP_Y / 2 && (this.offsetX <= 44 && this.offsetX >= -44)) {
this.up(index);
} else if (this.offsetX >= this.FIX_VP_X / 2 && (this.offsetY <= 50 && this.offsetY >= -50)) {
this.right(index);
} else if (this.offsetX <= -this.FIX_VP_Y / 2 && (this.offsetY <= 50 && this.offsetY >= -50)) {
this.left(index);
}
})
})
.onActionEnd(() => {
this.getUIContext().animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
this.dragItem = -1;
})
this.getUIContext().animateTo({ curve: curves.interpolatingSpring(14, 1, 170, 17), delay: 150 }, () => {
this.scaleItem = -1;
})
})
)
.onCancel(() => {
this.getUIContext().animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => {
this.dragItem = -1;
})
this.getUIContext().animateTo({ curve: curves.interpolatingSpring(14, 1, 170, 17), delay: 150 }, () => {
this.scaleItem = -1;
})
})
)
最后效果:
