ArkUI 帧动画:一帧一帧动,别把它当 GIF 凑合

举报
蓝瘦的蜕变 发表于 2026/11/06 22:15:28 2026/11/06
【摘要】 今天我们用 ImageAnimator 播放一组图片帧。 帧动画适合“我已经有一组连续素材”的场景。比如小插画、状态表情、加载形象。这个 Demo 为了不引入新素材,复用项目里的 startIcon 做四帧位置变化,重点看 ImageFrameInfo 和播放状态。 这个 Demo 看什么 frames 是 Image

ArkUI 帧动画:一帧一帧动,别把它当 GIF 凑合

今天我们用 ImageAnimator 播放一组图片帧。

帧动画适合“我已经有一组连续素材”的场景。比如小插画、状态表情、加载形象。这个 Demo 为了不引入新素材,复用项目里的 startIcon 做四帧位置变化,重点看 ImageFrameInfo 和播放状态。

这个 Demo 看什么

frames 是 ImageFrameInfo[]。每一帧都可以配置:

  • src:图片资源。
  • width / height:这一帧的尺寸。
  • top / left:这一帧相对动画组件的位置。
  • duration:这一帧停留多久。

ImageAnimator 再通过 images、state、duration、iterations 和 fillMode 控制播放。这里没有使用 @ohos.animator,因为本篇重点是图片帧;如果你要驱动纯数值变化,再考虑数值动画更合适。

你做过最麻烦的帧动画是什么?是素材尺寸不统一,还是循环节奏不自然?这个问题很常见。

完整代码

@Entry
@Component
struct ArkUIFrameAnimationDemo {
  @State playStatus: AnimationStatus = AnimationStatus.Running;

  private frames: ImageFrameInfo[] = [
    { src: $r('app.media.startIcon'), width: 72, height: 72, top: 42, left: 20, duration: 260 },
    { src: $r('app.media.startIcon'), width: 82, height: 82, top: 32, left: 92, duration: 260 },
    { src: $r('app.media.startIcon'), width: 72, height: 72, top: 48, left: 178, duration: 260 },
    { src: $r('app.media.startIcon'), width: 64, height: 64, top: 58, left: 96, duration: 260 }
  ];

  @Builder
  HeaderBlock() {
    Column({ space: 8 }) {
      Text('ArkUI 帧动画')
        .fontSize(28)
        .fontWeight(700)
        .fontColor('#111827')
      Text('一帧一帧动,别把它当 GIF 凑合')
        .fontSize(16)
        .fontColor('#4B5563')
      Text('ImageAnimator 适合播放已经准备好的连续帧。这里复用 startIcon 做四帧位置变化,只看帧动画的基本结构。')
        .fontSize(14)
        .fontColor('#6B7280')
        .lineHeight(22)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  build() {
    Scroll() {
      Column({ space: 22 }) {
        this.HeaderBlock()

        Column({ space: 12 }) {
          ImageAnimator()
            .images(this.frames)
            .state(this.playStatus)
            .duration(1040)
            .iterations(-1)
            .fillMode(FillMode.Forwards)
            .fixedSize(false)
            .width('100%')
            .height(180)
            .borderRadius(20)
            .backgroundColor('#F8FAFC')
            .border({ width: 1, color: '#E5E7EB' })

          Row({ space: 12 }) {
            Button(this.playStatus === AnimationStatus.Running ? '暂停' : '播放')
              .height(42)
              .fontSize(14)
              .fontColor('#FFFFFF')
              .backgroundColor('#2563EB')
              .borderRadius(21)
              .layoutWeight(1)
              .onClick(() => {
                this.playStatus = this.playStatus === AnimationStatus.Running ? AnimationStatus.Paused : AnimationStatus.Running;
              })
            Button('回到初始')
              .height(42)
              .fontSize(14)
              .fontColor('#111827')
              .backgroundColor('#E5E7EB')
              .borderRadius(21)
              .layoutWeight(1)
              .onClick(() => {
                this.playStatus = AnimationStatus.Initial;
              })
          }
          .width('100%')
        }
        .width('100%')

        Column({ space: 8 }) {
          Text('ImageAnimator 适合什么')
            .fontSize(18)
            .fontWeight(700)
            .fontColor('#111827')
          Text('如果素材已经是一组连续图片,用 ImageAnimator 很直接。如果只是数值从 0 到 100 变化,一般用 animation、animateTo 或 @ohos.animator 这类数值驱动方案更合适。')
            .fontSize(14)
            .fontColor('#4B5563')
            .lineHeight(22)
        }
        .width('100%')
        .padding(16)
        .borderRadius(16)
        .backgroundColor('#F8FAFC')
        .border({ width: 1, color: '#E5E7EB' })
        .alignItems(HorizontalAlign.Start)
      }
      .width('100%')
      .padding(24)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#FFFFFF')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。