| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348 |
- 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();
- }
- }
- }
|