import curves from '@ohos.curves' import { util } from '@kit.ArkTS' import { YTLog } from '../../../../../Index' @Component export struct PanGrid { /** * @description 需要渲染的数组 * @type extends BasicModel */ @Prop @Require @Watch('changeList') displayArr: ESObject[] /** * @description 需要传入渲染的结构 */ @BuilderParam @Require gridItem: (_: ESObject, index: number) => void /** * @description grid的列数 两列示例: '1fr 1fr' 不支持动态传入 */ @Require columnsTemplate: string sortOrder: string = 'sortOrder' //完成 @Require onFinishCallBack: (arr: ESObject[]) => void @State private dragItemId: number | string = -1 @State private scaleItemId: number | string = -1 @State private offsetX: number = 0 @State private offsetY: number = 0 private columnsCount: number = 0 private dragRefOffsetx: number = 0 private dragRefOffsety: number = 0 private FIX_VP_X: number = 108 private FIX_VP_Y: number = 120 private firstGetArea: boolean = true private isMove: boolean = false //用户传入的数组中是否有id private scroller: Scroller = new Scroller() //使用回调获取Scroller空值grid行为 onPanGridAppear = (_: Scroller) => { } aboutToAppear() { this.columnsCount = this.columnsTemplate.split(' ').length this.onPanGridAppear(this.scroller) } changeList() { this.displayArr.forEach((item: ESObject) => { if (item.id == undefined) { item.id = util.generateRandomUUID() item.haveId = false } }) if (this.isMove) { return } // this.resetStates() } build() { Column() { Grid(this.scroller) { ForEach(this.displayArr, (item: ESObject, index: number) => { GridItem() { this.gridItem(item, index) } .backgroundColor(Color.Transparent) .shadow(this.dragItemId == item.id ? { radius: 20, // 模糊半径(对应 20dp) color: '#0F000000', // 阴影颜色 offsetX: 0, // 水平偏移 offsetY: 0 // 垂直偏移 } : undefined) // .borderRadius(16) .onAreaChange(this.firstGetArea ? (_, newArea) => { this.FIX_VP_X = newArea.width as number this.FIX_VP_Y = newArea.height as number } : undefined) // 指定固定GridItem不响应事件 // .hitTestBehavior(this.isDraggable(this.displayArr.indexOf(item)) ? HitTestMode.Default : HitTestMode.None) // .scale({ x: this.scaleItemId == item.id ? 1.05 : 1, y: this.scaleItemId == item.id ? 1.05 : 1 }) .zIndex(this.dragItemId == item.id ? 1 : 0) .translate(this.dragItemId == item.id ? { x: this.offsetX, y: this.offsetY } : { x: 0, y: 0 }) .gesture( // 以下组合手势为顺序识别,当长按手势事件未正常触发时则不会触发拖动手势事件 GestureGroup(GestureMode.Sequence, LongPressGesture({ repeat: true }) .onAction((event?: GestureEvent) => { this.getUIContext()?.animateTo({ curve: Curve.Friction, duration: 300 }, () => { this.scaleItemId = item.id }) }) .onActionEnd(() => { this.getUIContext()?.animateTo({ curve: Curve.Friction, duration: 300 }, () => { this.scaleItemId = -1 }) }), PanGesture({ fingers: 1, direction: null, distance: 0 }) .onActionStart(() => { this.isMove = true this.dragItemId = item.id this.dragRefOffsetx = 0 this.dragRefOffsety = 0 }) .onActionUpdate((event: GestureEvent) => { this.offsetY = event.offsetY - this.dragRefOffsety this.offsetX = event.offsetX - this.dragRefOffsetx this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => { let index = this.displayArr.findIndex((item: ESObject) => item.id == this.dragItemId) if (this.offsetY >= this.FIX_VP_Y / 2 && (this.offsetX <= 44 && this.offsetX >= -44) && !this.isLastRow(index)) { //向下滑 this.down(index) } else if (this.offsetY <= -this.FIX_VP_Y / 2 && (this.offsetX <= 44 && this.offsetX >= -44) && !this.isFirstRow(index)) { //向上滑 this.up(index) } else if (this.offsetX >= this.FIX_VP_X / 2 && (this.offsetY <= 50 && this.offsetY >= -50) && !this.isLastColumn(index)) { //向右滑 this.right(index) } else if (this.offsetX <= -this.FIX_VP_X / 2 && (this.offsetY <= 50 && this.offsetY >= -50) && !this.isFirstColumn(index)) { //向左滑 this.left(index) } else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2 && !this.isLastRow(index) && !this.isLastColumn(index)) { //向右下滑 this.lowerRight(index) } else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY <= -this.FIX_VP_Y / 2 && !this.isFirstRow(index) && !this.isLastColumn(index)) { //向右上滑 this.upperRight(index) } else if (this.offsetX <= -this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2 && !this.isLastRow(index) && !this.isFirstColumn(index)) { //向左下滑 this.lowerLeft(index) } else if (this.offsetX <= -this.FIX_VP_X / 2 && this.offsetY <= -this.FIX_VP_Y / 2 && !this.isFirstRow(index) && !this.isFirstColumn(index)) { //向左上滑 this.upperLeft(index) } else if (this.offsetX >= this.FIX_VP_X / 2 && this.offsetY >= this.FIX_VP_Y / 2 && this.isLastRow(index) && this.isFirstColumn(index)) { //向右下滑(右下角为空) this.down(index) } } ) }) .onActionEnd(() => { this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => { this.dragItemId = -1 }) this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(14, 1, 170, 17), delay: 150 }, () => { this.scaleItemId = -1 }) // 更新sortOrder this.updateSortOrder() this.isMove = false //返回排序后的数组 this.onFinishCallBack((() => { YTLog.info(this.displayArr, '删除id') this.displayArr.forEach((item: ESObject) => { //排除undefined的情况 if (item.haveId == false) { item.id = undefined } item.haveId = undefined }) YTLog.info(this.displayArr, '删除id') return this.displayArr })()) }) ) .onCancel(() => { this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(0, 1, 400, 38) }, () => { this.dragItemId = -1 }) this.getUIContext()?.animateTo({ curve: curves.interpolatingSpring(14, 1, 170, 17) }, () => { this.scaleItemId = -1 }) }) ) }, (item: ESObject, _: number) => { // 使用更稳定的key,优先使用id,其次使用sortOrder,最后使用JSON.stringify return JSON.stringify(item); }) } .width('100%%') .editMode(true) .scrollBar(BarState.Off) .columnsTemplate(this.columnsTemplate) .rowsGap(8) } .width('100%') .padding({ top: 5 }) } // 重置所有状态 private resetStates(): void { this.dragItemId = -1 this.scaleItemId = -1 this.offsetX = 0 this.offsetY = 0 this.dragRefOffsetx = 0 this.dragRefOffsety = 0 } private itemMove(index: number, newIndex: number): void { if (!this.isDraggable(newIndex)) { return } let tmp: ESObject[] = this.displayArr.splice(index, 1) this.displayArr.splice(newIndex, 0, tmp[0]) } //向下滑 private down(index: number): void { // 指定固定GridItem不响应事件 if (!this.isDraggable(index + this.columnsCount)) { return } this.offsetY -= this.FIX_VP_Y this.dragRefOffsety += this.FIX_VP_Y this.itemMove(index, index + this.columnsCount) } //向上滑 private up(index: number): void { if (!this.isDraggable(index - this.columnsCount)) { return } this.offsetY += this.FIX_VP_Y this.dragRefOffsety -= this.FIX_VP_Y this.itemMove(index, index - this.columnsCount) } //向左滑 private left(index: number): void { if (!this.isDraggable(index - 1)) { return } this.offsetX += this.FIX_VP_X this.dragRefOffsetx -= this.FIX_VP_X this.itemMove(index, index - 1) } //向右滑 private right(index: number): void { if (!this.isDraggable(index + 1)) { return } this.offsetX -= this.FIX_VP_X this.dragRefOffsetx += this.FIX_VP_X this.itemMove(index, index + 1) } //向右下滑 private lowerRight(index: number): void { if (!this.isDraggable(index + this.columnsCount + 1)) { return } this.offsetX -= this.FIX_VP_X this.dragRefOffsetx += this.FIX_VP_X this.offsetY -= this.FIX_VP_Y this.dragRefOffsety += this.FIX_VP_Y this.itemMove(index, index + this.columnsCount + 1) } //向右上滑 private upperRight(index: number): void { if (!this.isDraggable(index - this.columnsCount + 1)) { return } this.offsetX -= this.FIX_VP_X this.dragRefOffsetx += this.FIX_VP_X this.offsetY += this.FIX_VP_Y this.dragRefOffsety -= this.FIX_VP_Y this.itemMove(index, index - this.columnsCount + 1) } //向左下滑 private lowerLeft(index: number): void { const targetIndex = index + this.columnsCount - 1; // 正确目标索引是 2 if (!this.isDraggable(targetIndex)) { // 检查目标索引而非 index+2 return } this.offsetX += this.FIX_VP_X this.dragRefOffsetx -= this.FIX_VP_X this.offsetY -= this.FIX_VP_Y this.dragRefOffsety += this.FIX_VP_Y this.itemMove(index, targetIndex) // 使用计算后的目标索引 } //向左上滑 private upperLeft(index: number): void { if (!this.isDraggable(index - this.columnsCount - 1)) { return } this.offsetX += this.FIX_VP_X this.dragRefOffsetx -= this.FIX_VP_X this.offsetY += this.FIX_VP_Y this.dragRefOffsety -= this.FIX_VP_Y this.itemMove(index, index - this.columnsCount - 1) } private isDraggable(index: number): boolean { return index < this.displayArr.length } // 新增辅助方法:检查当前项是否在最后一行 private isLastRow(index: number): boolean { const rowIndex = Math.floor(index / this.columnsCount); const totalRows = Math.ceil(this.displayArr.length / this.columnsCount); return rowIndex === totalRows - 1; } // 检查是否在第一行 private isFirstRow(index: number): boolean { return Math.floor(index / this.columnsCount) === 0; } // 检查是否在最后一列 private isLastColumn(index: number): boolean { return index % this.columnsCount === this.columnsCount - 1; } // 检查是否在第一列 private isFirstColumn(index: number): boolean { return index % this.columnsCount === 0; } // 更新sortOrder字段 private updateSortOrder(): void { for (let i = 0; i < this.displayArr.length; i++) { this.displayArr[i][this.sortOrder] = (i + 1).toString(); } } }