HarmonyOS鸿蒙Next中list的子条目item中子组件点击旋转动画

HarmonyOS鸿蒙Next中list的子条目item中子组件点击旋转动画

@Builder
itemHeader(index: number, str: string, isExpand: boolean) {
  Row() {
    Text(str + JSON.stringify(isExpand))
      .fontColor(Color.Red)
      .padding(6)

    Button() {
      Image($r('sys.media.ohos_ic_public_arrow_down'))
        .width(24)
        .height(24)
        .fillColor('#3F72AF')
        .rotate({ angle: isExpand ? 90 : 0 })
    }
    .width(36)
    .height(36)
    .backgroundColor(Color.Transparent)
    .onClick(() => {
      let bol = this.dataSource.getData(index).isExpand
      this.dataSource.getData(index).isExpand = !bol
      this.dataSource.notifyDataChange(index)
    })
  }
  .justifyContent(FlexAlign.SpaceBetween)
  .width('100%')
  .backgroundColor(Color.Orange)
}

如何实现点击之后, 图片旋转的动画


更多关于HarmonyOS鸿蒙Next中list的子条目item中子组件点击旋转动画的实战教程也可以访问 https://www.itying.com/category-93-b0.html

9 回复

如有复杂需求,可参考组件旋转动效实现。下面给出简单实现:

@Entry
@Component
struct Index {
  uiContext: UIContext = this.getUIContext()
  private images: Resource[] = [$r('app.media.xxx'), $r('app.media.xxx'), $r('app.media.xxx')]
  @State angles: number[] = [0, 0, 0]

  build() {
    Column() {
      List() {
        ForEach(this.images, (item: Resource, index: number) => {
          ListItem() {
            Image(item)
              .size({width: 40, height: 40})
              .onClick(() => {
                this.uiContext.animateTo({
                  duration: 1000,
                  curve: Curve.Linear
                }, () => {
                  this.angles[index] += 90
                })
              })
              .rotate({angle: this.angles[index]})
          }
        })
      }
      .size({width: 50, height: 50})
    }
  }
}

更多关于HarmonyOS鸿蒙Next中list的子条目item中子组件点击旋转动画的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


属性改变的时候加上试试animateTo

animateTo({ duration: 10 }, () => {
  let bol = this.dataSource.getData(index).isExpand
  this.dataSource.getData(index).isExpand = !bol
  this.dataSource.notifyDataChange(index)
})

根据你的代码看动画的代码是没问题的,没生效的原因可能是有:

  1. dataSource的item的class没有加@ObservedV2装饰器,isExpand没有加@Trace装饰器。

  2. @Builder函数中,当传递的参数为状态变量时,状态变量的改变不会引起@Builder函数内的UI刷新。按引用传递参数时,传递的参数可为状态变量,且状态变量的改变会引起@Builder函数内的UI刷新。故itemHeader的第三个参数应该为item,然后Image组件rotate用item.isExpand绑定。

参考代码如下:

[@ObservedV2](/user/ObservedV2)
class Data {
  [@Trace](/user/Trace) isExpand: boolean = false;
}

class BasicDataSource<T> implements IDataSource {
  private listeners: DataChangeListener[] = [];
  private originDataArray: T[] = [];

  public totalCount(): number {
    return this.originDataArray.length;
  }

  public getData(index: number): T {
    return this.originDataArray[index];
  }

  registerDataChangeListener(listener: DataChangeListener): void {
    if (this.listeners.indexOf(listener) < 0) {
      console.info('add listener');
      this.listeners.push(listener);
    }
  }

  unregisterDataChangeListener(listener: DataChangeListener): void {
    const pos = this.listeners.indexOf(listener);
    if (pos >= 0) {
      console.info('remove listener');
      this.listeners.splice(pos, 1);
    }
  }

  notifyDataReload(): void {
    this.listeners.forEach(listener => {
      listener.onDataReloaded();
    });
  }

  notifyDataAdd(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataAdd(index);
    });
  }

  notifyDataChange(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataChange(index);
    });
  }

  notifyDataDelete(index: number): void {
    this.listeners.forEach(listener => {
      listener.onDataDelete(index);
    });
  }

  notifyDataMove(from: number, to: number): void {
    this.listeners.forEach(listener => {
      listener.onDataMove(from, to);
    });
  }

  notifyDatasetChange(operations: DataOperation[]): void {
    this.listeners.forEach(listener => {
      listener.onDatasetChange(operations);
    });
  }
}

class MyDataSource<T> extends BasicDataSource<T> {
  private dataArray: T[] = [];

  public totalCount(): number {
    return this.dataArray.length;
  }

  public getData(index: number): T {
    return this.dataArray[index];
  }

  public pushData(data: T): void {
    this.dataArray.push(data);
    this.notifyDataAdd(this.dataArray.length - 1);
  }
}

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';
  @State private dataSource: MyDataSource<Data> = new MyDataSource<Data>();

  @Builder
  itemHeader(index: number, str: string, item: Data) {
      Row() {
        Text(str + JSON.stringify(item.isExpand))
          .fontColor(Color.Red)
          .padding(6)

        Button() {

          Image($r('sys.media.ohos_ic_public_arrow_down'))
            .width(24)
            .height(24)
            .fillColor('#3F72AF')
            .rotate({ angle: item.isExpand? 90: 0 })
            .animation({
              duration : 2000,
              curve : Curve.Linear,
              delay : 0
            })
        }
        .width(36)
        .height(36)
        .backgroundColor(Color.Transparent)
        .onClick(() => {
          let bool:boolean = this.dataSource.getData(index).isExpand
          this.dataSource.getData(index).isExpand = !bool
          this.dataSource.notifyDataChange(index)
        })

      }.justifyContent(FlexAlign.SpaceBetween).width('100%').backgroundColor(Color.Orange)

    }

    aboutToAppear(): void {
      this.dataSource.pushData(new Data())
      this.dataSource.pushData(new Data())
      this.dataSource.pushData(new Data())
    }

    build() {
      List({ space: 3 }) {
        LazyForEach(this.dataSource, (item: Data, index) => {
          ListItem() {
            this.itemHeader(index, index.toString(), item)
            Text(item.isExpand ? '展开' : '收起')
          }
        }, (item: string) => item)
      }.cachedCount(5)
    }
}

有要学HarmonyOS AI的同学吗,联系我:https://www.itying.com/goods-1206.html

哈哈,之前那个长按左/*右滑动删除条目也是你的问题吧,你这是研究动画呢?

显式动画方案:

Button() {
  Image($r('sys.media.ohos_ic_public_arrow_down'))
    .rotate({ angle: isExpand ? 90 : 0 })
}
.onClick(() => {
  animateTo({
    duration: 300,
    curve: Curve.EaseInOut
  }, () => {
    let bol = this.dataSource.getData(index).isExpand
    this.dataSource.getData(index).isExpand = !bol
    this.dataSource.notifyDataChange(index)
  })
})

属性动画方案

Image($r('sys.media.ohos_ic_public_arrow_down'))
  .rotate({ 
    angle: isExpand ? 90 : 0 
  })
  .animation({
    duration: 300,
    curve: Curve.EaseInOut,
    iterations: 1,
    playMode: PlayMode.Normal
  })

2个方案,你体会一下

试过了,我那个场景可能比较特殊,是list的itemgroup 里的,sticky的header 加了isExpand ,我一设置动画.onClick(() => { animateTo({ duration: 300, curve: Curve.EaseInOut }, () => { let bol = this.dataSource.getData(index).isExpand this.dataSource.getData(index).isExpand = !bol this.dataSource.notifyDataChange(index) }) }),整个itemgroup 都在变,只需要header 旋转一个单独的image.

我的header 里图片的代码

```jsx
Image($r('sys.media.ohos_ic_public_arrow_down'))
  .width(24)
  .height(24)
  .fillColor('#3F72AF')
  .rotate({ angle: isExpand ? 90 : 0 })
  .width(36)
  .height(36)
  .backgroundColor(Color.Transparent)
  .onClick(() => {
    animateTo({
      duration: 2000,
      curve: Curve.EaseInOut
    }, () => {
      let bol = this.dataSource.getData(index).isExpand
      this.dataSource.getData(index).isExpand = !bol
      this.dataSource.notifyDataChange(index)
    })
  })

在HarmonyOS鸿蒙Next中实现list子条目item中子组件的点击旋转动画,可以使用ArkUI的动画能力。通过给子组件添加点击事件,在事件回调中触发旋转动画。使用animateTo方法或显式动画API,设置旋转角度(如0到360度)和持续时间。例如:

// 子组件
@Component
struct RotatingComponent {
  @State angle: number = 0
  
  build() {
    Column() {
      Image($r('app.media.icon'))
        .onClick(() => {
          animateTo({ duration: 500 }, () => {
            this.angle = this.angle === 0 ? 360 : 0
          })
        })
        .rotate({ x: 0, y: 0, z: 1, angle: this.angle })
    }
  }
}

注意在list中使用时需处理好组件状态管理。

在HarmonyOS Next中实现点击旋转动画,建议使用显式动画API。修改代码如下:

Image($r('sys.media.ohos_ic_public_arrow_down'))
  .width(24)
  .height(24)
  .fillColor('#3F72AF')
  .rotate({ angle: isExpand ? 90 : 0 })
  .onClick(() => {
    animateTo({
      duration: 200,
      curve: Curve.EaseInOut
    }, () => {
      this.dataSource.getData(index).isExpand = !this.dataSource.getData(index).isExpand;
      this.dataSource.notifyDataChange(index);
    });
  })

关键点:

  1. 使用animateTo包裹状态变更逻辑
  2. 移除原先Button的onClick,改为直接在Image上处理
  3. 动画参数建议使用较短的duration(200ms)和EaseInOut曲线

这样修改后,图片旋转时会带有平滑的动画效果。注意确保isExpand状态变更后能正确触发UI更新。

回到顶部