ArkUI animation:状态一变,页面别硬跳

举报
蓝瘦的蜕变 发表于 2026/10/28 11:19:07 2026/10/28
【摘要】 今天我们要用 animation 做一个最常见的动效:状态改变以后,卡片不要突然跳到另一个样子,而是顺着尺寸、颜色、透明度、缩放和位移慢慢变过去。 准备 这一页只需要一个 ArkTS 页面:entry/src/main/ets/pages/arkUI/motion/ArkUIAnimationDemo.ets。 我们不

ArkUI animation:状态一变,页面别硬跳

今天我们要用 animation 做一个最常见的动效:状态改变以后,卡片不要突然跳到另一个样子,而是顺着尺寸、颜色、透明度、缩放和位移慢慢变过去。

准备

这一页只需要一个 ArkTS 页面:entry/src/main/ets/pages/arkUI/motion/ArkUIAnimationDemo.ets。

我们不引入业务数据,也不讲状态管理原理。这里的 @State 只负责触发动画。

第一步:先准备两个状态

@State expanded: boolean = false;
@State highlighted: boolean = false;

expanded 控制卡片是收起还是展开,highlighted 控制提醒卡片是不是强调态。状态本身很普通,真正让页面动起来的是后面的 .animation(...)。

第二步:把 animation 放在会变化的属性后面

.width(this.expanded ? '100%' : '76%')
.height(this.expanded ? 156 : 96)
.backgroundColor(this.expanded ? '#2563EB' : '#FFFFFF')
.opacity(this.expanded ? 1 : 0.86)
.scale({ x: this.expanded ? 1 : 0.96, y: this.expanded ? 1 : 0.96 })
.translate({ x: this.expanded ? 0 : 18, y: this.expanded ? 0 : 8 })
.animation({ duration: 420, curve: Curve.EaseInOut })

这一步很简单:哪些属性会跟着状态变,就把它们写清楚。animation 会在状态变化时接住这些属性,不让它们硬切。

容易踩坑的地方是别把所有动效都塞进一页。这里我们只看属性动画,组件插入移除交给下一篇 transition。

运行后看什么

打开案例页后,点“展开卡片”。你应该能看到卡片宽高、颜色、透明度、缩放和位移一起过渡。再点下面那张提醒卡片,它会在普通态和强调态之间切换。

你平时写页面时,最容易突然跳变的是哪类 UI?是卡片展开、按钮状态,还是列表项选中?可以带着这个问题继续改这页参数。

完整示例代码

@Entry
@Component
struct ArkUIAnimationDemo {
  @State expanded: boolean = false;
  @State highlighted: boolean = false;

  @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 animation')
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor('#111827')
          Text('状态一变,页面别硬跳')
            .fontSize(16)
            .fontColor('#4B5563')
          Text('这一页只看属性动画:状态改了以后,尺寸、颜色、透明度、缩放和位移跟着补一段过渡。')
            .fontSize(14)
            .fontColor('#6B7280')
            .lineHeight(22)
        }
        .width('100%')
        .alignItems(HorizontalAlign.Start)

        this.sectionTitle('一段 animation 管一组属性', 'animation 会跟随状态变化工作,适合卡片展开、按钮强调、模块轻微移动这类场景。')
        Column({ space: 16 }) {
          Column({ space: 10 }) {
            Text(this.expanded ? '展开后的卡片' : '收起的卡片')
              .fontSize(this.expanded ? 21 : 17)
              .fontWeight(FontWeight.Bold)
              .fontColor(this.expanded ? '#FFFFFF' : '#1D4ED8')
            Text(this.expanded ? '尺寸、背景色、透明度和位移一起变化,用户能看清页面发生了什么。' : '点下面按钮,让这张卡片自然展开。')
              .fontSize(13)
              .fontColor(this.expanded ? '#DBEAFE' : '#475569')
              .lineHeight(20)
          }
          .width(this.expanded ? '100%' : '76%')
          .height(this.expanded ? 156 : 96)
          .padding(18)
          .borderRadius(this.expanded ? 22 : 14)
          .backgroundColor(this.expanded ? '#2563EB' : '#FFFFFF')
          .opacity(this.expanded ? 1 : 0.86)
          .scale({ x: this.expanded ? 1 : 0.96, y: this.expanded ? 1 : 0.96 })
          .translate({ x: this.expanded ? 0 : 18, y: this.expanded ? 0 : 8 })
          .shadow({
            radius: this.expanded ? 18 : 6,
            color: this.expanded ? '#402563EB' : '#1A000000',
            offsetX: 0,
            offsetY: this.expanded ? 10 : 4
          })
          .animation({ duration: 420, curve: Curve.EaseInOut })
          .alignItems(HorizontalAlign.Start)

          Button(this.expanded ? '收起卡片' : '展开卡片')
            .height(40)
            .fontSize(14)
            .fontColor('#FFFFFF')
            .backgroundColor('#111827')
            .borderRadius(20)
            .padding({ left: 18, right: 18 })
            .onClick(() => {
              this.expanded = !this.expanded;
            })
        }
        .width('100%')
        .padding(16)
        .borderRadius(16)
        .backgroundColor('#F8FAFC')
        .border({ width: 1, color: '#E5E7EB' })
        .alignItems(HorizontalAlign.Start)

        this.sectionTitle('强调也可以有过渡', '颜色、透明度和缩放不一定要跳变。轻一点,页面会稳很多。')
        Row({ space: 12 }) {
          Column({ space: 6 }) {
            Text('消息提醒')
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
              .fontColor(this.highlighted ? '#FFFFFF' : '#0F766E')
            Text(this.highlighted ? '当前被强调' : '普通展示状态')
              .fontSize(13)
              .fontColor(this.highlighted ? '#CCFBF1' : '#64748B')
          }
          .layoutWeight(1)
          .alignItems(HorizontalAlign.Start)

          Text(this.highlighted ? 'ON' : 'OFF')
            .fontSize(13)
            .fontWeight(FontWeight.Bold)
            .fontColor(this.highlighted ? '#0F766E' : '#64748B')
            .padding({ left: 12, right: 12, top: 6, bottom: 6 })
            .borderRadius(12)
            .backgroundColor(this.highlighted ? '#CCFBF1' : '#E2E8F0')
        }
        .width('100%')
        .padding(18)
        .borderRadius(16)
        .backgroundColor(this.highlighted ? '#0F766E' : '#FFFFFF')
        .scale({ x: this.highlighted ? 1.02 : 1, y: this.highlighted ? 1.02 : 1 })
        .opacity(this.highlighted ? 1 : 0.88)
        .animation({ duration: 320, curve: Curve.EaseOut })
        .onClick(() => {
          this.highlighted = !this.highlighted;
        })
      }
      .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个月内不可修改。