ArkUI @AnimatableExtend:样式能动,过渡才不生硬

举报
蓝瘦的蜕变 发表于 2026/11/12 20:56:03 2026/11/12
【摘要】 今天我们给 Text 扩展一个会参与动画的宽度属性,让一条蓝色进度块在按钮点击后平滑展开和收回。 普通 @Extend 更像是把一组样式收起来。@AnimatableExtend 多了一层意思:这个扩展属性的参数会参与动画过程,ArkUI 会在动画中逐帧把中间值传进去。 参考文档:组件扩展 先说边界 @Animatab

ArkUI @AnimatableExtend:样式能动,过渡才不生硬

今天我们给 Text 扩展一个会参与动画的宽度属性,让一条蓝色进度块在按钮点击后平滑展开和收回。

普通 @Extend 更像是把一组样式收起来。@AnimatableExtend 多了一层意思:这个扩展属性的参数会参与动画过程,ArkUI 会在动画中逐帧把中间值传进去。

参考文档:组件扩展

先说边界

@AnimatableExtend 很有用,但别一上来就写很复杂。

  • 它必须定义在全局,不能写在组件内部。
  • 函数体里只能调用括号里指定组件的属性方法。
  • 参数可以是 number,也可以是实现 AnimatableArithmetic 的自定义类型。

这篇我们只用 number。自定义类型能做更复杂的动画,但新手第一步没必要把门槛抬那么高。

定义一个可动画扩展属性

我们先给 Text 扩展一个宽度方法:

@AnimatableExtend(Text)
function animatableBarWidth(widthValue: number) {
  .width(widthValue)
}

这段代码只做一件事:把传进来的数字用到 Text.width() 上。

容易写错的地方也在这里:函数体里不要顺手写别的组件,也不要在组件内部声明这个函数。

用状态驱动宽度变化

页面里保留一个宽度值:

@State barWidth: number = 120;

点击按钮时,在两个宽度之间切换:

this.barWidth = this.expanded ? 260 : 120;

如果只是这么写,宽度会变,但变化可能比较硬。我们给组件挂上动画:

Text(' ')
  .height(44)
  .animatableBarWidth(this.barWidth)
  .animation({ duration: 500, curve: Curve.EaseInOut })

这样 barWidth 从 120 变到 260 时,中间过程就会被动画接住。

这个 Demo 看什么

页面上只有一个蓝色条块、一个当前宽度数字和一个按钮。

点击“展开宽度”,条块会平滑变长;再点“收回宽度”,它会缩回去。我们刻意没有加更多视觉效果,因为这篇想让你盯住一个重点:自定义扩展属性也可以参与动画。

如果你项目里有一些“明明只是样式变化,却看起来很生硬”的地方,可以先想想它是不是适合拆成一个可动画扩展属性。

完整示例代码

文件位置:entry/src/main/ets/pages/arkUI/custom/ArkUIAnimatableExtendDemo.ets

@AnimatableExtend(Text)
function animatableBarWidth(widthValue: number) {
  .width(widthValue)
}

@Entry
@Component
struct ArkUIAnimatableExtendDemo {
  @State barWidth: number = 120;
  @State expanded: boolean = false;

  @Builder
  HeaderBlock() {
    Column({ space: 8 }) {
      Text('ArkUI @AnimatableExtend')
        .fontSize(28)
        .fontWeight(700)
        .fontColor('#111827')
      Text('样式能动,过渡才不生硬')
        .fontSize(16)
        .fontColor('#4B5563')
      Text('@AnimatableExtend 可以把自定义扩展属性接入动画过程。本篇先用 number 参数做最小示例。')
        .fontSize(14)
        .fontColor('#6B7280')
        .lineHeight(22)
    }
    .width('100%')
    .alignItems(HorizontalAlign.Start)
  }

  private toggleWidth(): void {
    this.expanded = !this.expanded;
    this.barWidth = this.expanded ? 260 : 120;
  }

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

        Column({ space: 14 }) {
          Text('宽度跟着数字参数动')
            .fontSize(18)
            .fontWeight(700)
            .fontColor('#111827')
          Text(' ')
            .height(44)
            .animatableBarWidth(this.barWidth)
            .borderRadius(22)
            .backgroundColor('#2563EB')
            .animation({ duration: 500, curve: Curve.EaseInOut })
          Text(`当前宽度:${this.barWidth}`)
            .fontSize(14)
            .fontColor('#4B5563')
        }
        .width('100%')
        .padding(16)
        .borderRadius(18)
        .backgroundColor('#FFFFFF')
        .border({ width: 1, color: '#E5E7EB' })
        .alignItems(HorizontalAlign.Start)

        Button(this.expanded ? '收回宽度' : '展开宽度')
          .height(44)
          .borderRadius(22)
          .fontSize(15)
          .fontWeight(700)
          .fontColor('#FFFFFF')
          .backgroundColor('#DC2626')
          .onClick(() => {
            this.toggleWidth();
          })

        Text('@AnimatableExtend 必须全局定义,函数体里只能调用括号里那个组件的属性方法。参数可以是 number,也可以是实现 AnimatableArithmetic 的自定义类型;这篇先不展开后者。')
          .width('100%')
          .padding(16)
          .borderRadius(16)
          .backgroundColor('#FEF2F2')
          .fontSize(14)
          .fontColor('#4B5563')
          .lineHeight(22)
      }
      .width('100%')
      .padding(24)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F8FAFC')
  }
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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