ArkUI animateTo:让变化有起点,也有落点

举报
蓝瘦的蜕变 发表于 2026/10/27 22:57:10 2026/10/27
【摘要】 今天我们要用 animateTo 做一次显式动画:按钮点击以后,我们把状态修改包起来,让位移、缩放和进度变化一起动。 准备 页面文件是 entry/src/main/ets/pages/arkUI/motion/ArkUIAnimateToDemo.ets。 这一页会故意放一个“直接改状态”的例子。它不是错,只是变化太

ArkUI animateTo:让变化有起点,也有落点

今天我们要用 animateTo 做一次显式动画:按钮点击以后,我们把状态修改包起来,让位移、缩放和进度变化一起动。

准备

页面文件是 entry/src/main/ets/pages/arkUI/motion/ArkUIAnimateToDemo.ets。

这一页会故意放一个“直接改状态”的例子。它不是错,只是变化太硬,方便我们看出 animateTo 的差别。

第一步:直接改状态

this.directMoved = !this.directMoved;

直接改状态时,UI 会马上进入新结果。对于普通开关,这样完全够用。但如果元素位置明显变化,用户就容易觉得页面闪了一下。

第二步:用 animateTo 包住状态修改

this.getUIContext().animateTo({ duration: 520, curve: Curve.EaseInOut }, () => {
  this.animatedMoved = !this.animatedMoved;
  this.animatedProgress = this.animatedMoved ? 86 : 36;
})

animateTo 的好处是入口清楚:这次点击会带来哪些状态变化,都集中放在回调里。一般来说,一个按钮同时驱动多个位置变化时,用它更好读。

运行后看什么

先点“直接改状态”,你会看到标签直接换位置。再点“用 animateTo 改状态”,标签和进度条会一起过渡。

下一次你遇到“点一下以后多个模块都要动”的场景,可以试着先用 animateTo 收住,不要每个模块各写一段零散动画。

完整示例代码

@Entry
@Component
struct ArkUIAnimateToDemo {
  @State directMoved: boolean = false;
  @State animatedMoved: boolean = false;
  @State animatedProgress: number = 36;

  private runAnimatedChange(): void {
    this.getUIContext().animateTo({ duration: 520, curve: Curve.EaseInOut }, () => {
      this.animatedMoved = !this.animatedMoved;
      this.animatedProgress = this.animatedMoved ? 86 : 36;
    })
  }

  @Builder
  sectionTitle(title: string, desc: string) {
    Column({ space: 6 }) {
      Text(title)
        .fontSize(19)
        .fontWeight(FontWeight.Bold)
        .fontColor('#111827')
      Text(desc)
        .fontSize(13)
        .fontColor('#6B7280')
        .lineHeight(20)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  build() {
    Scroll() {
      Column({ space: 22 }) {
        Column({ space: 8 }) {
          Text('ArkUI animateTo')
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor('#111827')
          Text('让变化有起点,也有落点')
            .fontSize(16)
            .fontColor('#4B5563')
          Text('这一页对比两种写法:直接改状态会立刻跳到结果,用 animateTo 包住状态修改,变化会被组织成一段动画。')
            .fontSize(14)
            .fontColor('#6B7280')
            .lineHeight(22)
        }
        .width('100%')
        .alignItems(HorizontalAlign.Start)

        this.sectionTitle('直接改状态:结果对,但变化太硬', '不是不能用,只是用户看不到过程。适合不需要动效的开关,不适合明显位置变化。')
        Column({ space: 12 }) {
          Row() {
            Text('直接移动')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .fontColor('#FFFFFF')
              .padding({ left: 16, right: 16, top: 10, bottom: 10 })
              .borderRadius(18)
              .backgroundColor('#64748B')
              .translate({ x: this.directMoved ? 128 : 0 })
          }
          .width('100%')
          .height(68)
          .padding(12)
          .borderRadius(16)
          .backgroundColor('#F1F5F9')

          Button('直接改状态')
            .height(38)
            .fontSize(14)
            .fontColor('#FFFFFF')
            .backgroundColor('#64748B')
            .borderRadius(19)
            .padding({ left: 18, right: 18 })
            .onClick(() => {
              this.directMoved = !this.directMoved;
            })
        }
        .width('100%')
        .alignItems(HorizontalAlign.Start)

        this.sectionTitle('animateTo:把状态变化包成一段动作', '显式动画适合一次按钮点击带动多处变化,逻辑入口更清楚。')
        Column({ space: 14 }) {
          Row() {
            Text('显式动画')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
              .fontColor('#FFFFFF')
              .padding({ left: 16, right: 16, top: 10, bottom: 10 })
              .borderRadius(18)
              .backgroundColor('#2563EB')
              .translate({ x: this.animatedMoved ? 128 : 0 })
              .scale({ x: this.animatedMoved ? 1.12 : 1, y: this.animatedMoved ? 1.12 : 1 })
          }
          .width('100%')
          .height(72)
          .padding(12)
          .borderRadius(16)
          .backgroundColor('#EFF6FF')

          Column({ space: 6 }) {
            Row() {
              Text('进度')
                .fontSize(13)
                .fontColor('#475569')
              Blank()
              Text(this.animatedProgress.toString() + '%')
                .fontSize(13)
                .fontWeight(FontWeight.Bold)
                .fontColor('#2563EB')
            }
            .width('100%')

            Row()
              .width(this.animatedProgress.toString() + '%')
              .height(8)
              .borderRadius(4)
              .backgroundColor('#2563EB')
          }
          .width('100%')
          .padding(12)
          .borderRadius(14)
          .backgroundColor('#F8FAFC')

          Button('用 animateTo 改状态')
            .height(38)
            .fontSize(14)
            .fontColor('#FFFFFF')
            .backgroundColor('#2563EB')
            .borderRadius(19)
            .padding({ left: 18, right: 18 })
            .onClick(() => {
              this.runAnimatedChange();
            })
        }
        .width('100%')
        .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个月内不可修改。