前言

数字类信息在应用里非常常见。余额变化、比分刷新、库存跳动、倒计时更新,这些场景都在不停改数字。数字一旦变化得太生硬,页面会显得很硬;变化得太乱,用户又看不清楚重点。鸿蒙 6 把这类效果直接收进了 Text 组件,contentTransition 从 API version 20 开始支持数字翻牌效果。

这项能力的价值很直接。数字变化这件事终于有了系统级的实现路径,代码会明显短很多,效果也更统一。对金融、电商、运动、工具类应用来说,这类动效不是装饰,它本身就是反馈的一部分。

一、先把这项能力的边界看清楚

这次新增的核心入口是 contentTransition。它挂在 Text 组件上,传入 NumericTextTransition 之后,数字内容变化时就会触发翻牌动效。NumericTextTransition 继承自 ContentTransition,这条能力从 API version 20 起可用,同时也支持在元服务中使用。

NumericTextTransition 这次最实用的配置项有两个。一个是 flipDirection,用来控制翻牌方向;另一个是 enableBlur,用来控制翻牌过程中是否启用模糊效果。这两个参数都已经进入 ArkUI 6.0.0(20) Beta3 的新增 API 范围。

这里要先记住一个前提,数字翻牌只对数字内容生效,而且限制条件比较明确。它只支持正整数,不支持小数和负数;不能和渐变色、Text 跑马灯模式一起使用;也不支持选中复制,copyOption 在这个场景里会失效。只要 Text 里存在子组件,或者文本内容是通过属性字符串方式设置,翻牌动效也不会触发。

这几个限制在项目里影响很大。比如金额展示经常带小数,涨跌值经常带负号,跑马灯公告又常常和数字混在一起。接入之前先把场景拆清楚,会比后面再返工省事很多。

二、先把最小可用版本接起来

这项能力上手很快,最简单的写法就是给 Text 直接挂一个 NumericTextTransition。数字变化时,动效就会自动跑起来。

@Entry
@Component
struct CounterDemo {
  @State private count: number = 0

  build() {
    Column({ space: 20 }) {
      Text(this.count.toString())
        .fontSize(60)
        .fontWeight(FontWeight.Bold)
        .contentTransition(new NumericTextTransition())

      Row({ space: 30 }) {
        Button('减少')
          .onClick(() => {
            if (this.count > 0) {
              this.count--
            }
          })

        Button('增加')
          .onClick(() => {
            this.count++
          })
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

这段代码最适合先做能力验证。按钮点击之后,数字直接走系统翻牌效果,页面逻辑只需要维护状态值,不需要额外动画控制。contentTransition 本身就是声明式属性,状态一变,Text 负责把过渡补上。

如果项目里有方向要求,比如希望数字向上翻或者向下翻,可以把 flipDirection 一起补进去。这个配置很适合比分牌、步数、楼层、电梯层数、库存数量这类有明确视觉方向感的数字场景。

@Entry
@Component
struct FlipDirectionDemo {
  @State private value: number = 12

  build() {
    Column({ space: 24 }) {
      Text('向下翻')
        .fontSize(16)
        .fontColor(Color.Gray)

      Text(this.value.toString())
        .fontSize(48)
        .fontWeight(FontWeight.Bold)
        .contentTransition(new NumericTextTransition({
          flipDirection: FlipDirection.DOWN
        }))

      Divider().margin({ top: 12, bottom: 12 })

      Text('向上翻')
        .fontSize(16)
        .fontColor(Color.Gray)

      Text(this.value.toString())
        .fontSize(48)
        .fontWeight(FontWeight.Bold)
        .contentTransition(new NumericTextTransition({
          flipDirection: FlipDirection.UP
        }))

      Button('更新数值')
        .onClick(() => {
          this.value = Math.floor(Math.random() * 100)
        })
    }
    .width('100%')
    .padding(24)
  }
}

如果页面希望翻牌过程更有运动感,可以把 enableBlur 打开。这个选项更适合大字号数字、中心区域大卡片、仪表盘类界面。小字号、高频更新、密集列表这类场景就要谨慎一点,模糊开多了会影响识别效率。

@Entry
@Component
struct BlurDemo {
  @State private score: number = 88

  build() {
    Column({ space: 20 }) {
      Text(this.score.toString())
        .fontSize(72)
        .fontWeight(FontWeight.Bold)
        .contentTransition(new NumericTextTransition({
          enableBlur: true
        }))

      Button('刷新分数')
        .onClick(() => {
          this.score = Math.floor(Math.random() * 100)
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

三、哪些场景适合用,哪些场景不要硬上

数字翻牌最适合三类场景。第一类是强调数值变化本身的页面,比如余额、积分、收益、库存、评分。第二类是强节奏场景,比如倒计时、秒杀、步数、比赛比分。第三类是中大型数字展示,比如统计面板、仪表盘、运营大屏里的重点指标。这里的共同点很明显,用户的注意力本来就落在数字变化上,翻牌动效能把这种变化放大。

拿步数场景举例,这类页面很适合用原生翻牌。数值持续增长,节奏清晰,内容又是纯整数,刚好符合组件边界。

@Entry
@Component
struct StepCounter {
  @State private steps: number = 0
  private timerId: number = -1

  aboutToAppear() {
    this.timerId = setInterval(() => {
      this.steps += Math.floor(Math.random() * 3) + 1
    }, 1000) as number
  }

  aboutToDisappear() {
    if (this.timerId !== -1) {
      clearInterval(this.timerId)
      this.timerId = -1
    }
  }

  build() {
    Column({ space: 24 }) {
      Text('今日步数')
        .fontSize(20)
        .fontColor(Color.Gray)

      Text(this.steps.toString())
        .fontSize(72)
        .fontWeight(FontWeight.Bold)
        .fontColor('#1890FF')
        .contentTransition(new NumericTextTransition())

      Text('步')
        .fontSize(18)
        .fontColor(Color.Gray)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

不太适合硬上翻牌的场景也很明确。带小数的金额、带负号的涨跌值、需要渐变字色的品牌海报、跑马灯数字公告、可复制的验证码或订单号,这些都容易碰到能力边界。这里与其硬套一层翻牌,再去和限制条件较劲,不如直接回退到普通数字刷新,或者单独做一套场景化动画。

四、开发时有几个坑要提前避开

第一个坑,是把数字翻牌拿去处理金额和比率。很多金融页面第一眼就想把金额动起来,但金额经常带两位小数,收益变化又常常带负号,这两种都超出了 NumericTextTransition 的直接适用范围。项目里更稳的做法,是把整数部分和小数部分拆开,或者只给整数部分上翻牌,小数和符号仍然按普通 Text 处理。

第二个坑,是给已经有复杂样式的 Text 继续硬挂翻牌。渐变色、跑马灯、复制、属性字符串、嵌套子组件,这几类情况本身就会让翻牌失效。这里最省事的处理方式,是把翻牌数字独立成一个纯 Text,不和其他复杂样式混在一起。这样写出来的结构更干净,问题也更容易查。

第三个坑,是把更新频率拉得太高。数字翻牌虽然是系统原生能力,但高频刷新仍然会影响阅读体验。做实时行情、大屏监控、秒表这类场景时,最好先判断用户真正需要的刷新密度,再决定是不是每次变化都触发动效。很多页面里,100ms 一跳和 1s 一跳带来的感知差别并没有想象中那么大,后者反而更稳。这个问题和 API 本身无关,属于交互节奏设计。

总结

鸿蒙 6 API 20 给 Text 组件补进来的数字翻牌动效,真正解决的是一个长期存在的小问题。数字变化明明很重要,过去却总要靠自定义动画去补,代码写起来重,效果还不一定稳。现在 contentTransitionNumericTextTransition 直接把这件事收进了组件能力里,开发成本和维护成本都会低很多。

项目里更适合优先接入这项能力的地方也很清楚。纯整数、状态明确、变化可感知的数字场景先上,金额小数、负号、渐变字色、跑马灯、可复制文本这些场景谨慎处理。把这条边界想清楚,数字翻牌这项能力会很好用,也很容易出效果。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐