HarmonyOS鸿蒙Next中怎么解决Progress组件配合animateTo实现循环动画无反应问题

HarmonyOS鸿蒙Next中怎么解决Progress组件配合animateTo实现循环动画无反应问题

【问题现象】

使用Progress组件配合animateTo来实现进度条循环动画,动画效果失效无反应。代码:

/** 进度条最小值 */
const PROGRESS_MIN = 0
/** 进度条最大值 */
const PROGRESS_MAX = 100

@Entry
@Component
struct ProgressAnim {
  /** 进度条当前值 */
  @State progressValue: number = PROGRESS_MIN

  build() {
    Column({ space: 15 }) {
      Progress({
        value: this.progressValue, // 进度条当前进度值
        total: PROGRESS_MAX, // 进度条总长
        type: ProgressType.Ring, // 进度条类型,分为Linear线性样式、ScaleRing环形有刻度样式、Ring环形无刻度样式、Eclipse圆形样式、Capsule胶囊样式
      })
        .style({
          strokeWidth: 10, // 进度条宽度,默认4vp
          enableSmoothEffect: true // 进度平滑动效的开关。开启平滑动效后设置进度,进度会从当前值渐变至设定值,否则进度从当前值突变至设定值,默认值true
        })
        .width(100) // 进度条组件宽度
        .color('# A97CF9') // 进度条前景色
        .backgroundColor(Color.White) // 进度条背景色
      Button('开始动画')
        .onClick(() => {
          this.getUIContext()?.animateTo({
            duration: 2000,
            iterations: -1, // 设置-1表示动画无限循环
          }, () => {
            this.progressValue = PROGRESS_MAX
          })
        })
    }
    .width('100%')
    .padding({ top: 5 })
    .backgroundColor(Color.Gray)
  }
}

【背景知识】

  • Progress进度条组件:用于显示内容加载或操作处理等进度。可以有多种表现形式,官方提供胶囊型、环形有刻度、环形无刻度、圆形,且支持自定义图形样式。

    点击放大

  • 显式动画animateTo:提供全局animateTo显式动画接口来指定由于闭包代码导致的状态变化插入过渡动效。同属性动画,布局类改变宽高的动画,内容都是直接到终点状态,例如文字、Canvas的内容等。

  • 定时器:setInterval()方法重复调用一个函数或执行一个代码片段,在每次调用之间具有固定的时间间隔。

    此方法返回一个间隔ID,其唯一地标识时间间隔,因此你可以稍后通过调用clearInterval()来移除它。

    点击放大

【定位思路】

animateTo适用于组件自身属性动画场景(如尺寸、颜色改变等),问题代码使用animateTo改变Progress组件的进度值,结果是进度条从0到100动画仅执行一次。可见不支持使用animateTo控制Progress组件进度条循环效果。

【解决方案】

使用定时器来控制Progress组件进度值。用setInterval方法设置定时任务,每间隔一段时间(如20毫秒)均匀地改变Progress组件进度值(如每次加1),即可实现预期效果,详情请参见示例代码注释部分。

点击放大

示例代码:

/** 进度条最小值 */
const PROGRESS_MIN = 0
/** 进度条最大值 */
const PROGRESS_MAX = 100

@Entry
@Component
struct ProgressAnim {
  /** 进度条当前值 */
  @State progressValue: number = PROGRESS_MIN
  /** interval操作的标识符 */
  @State intervalId: number | null = null
  aboutToAppear(): void {
    // 进入界面时即启动进度条动画
    this.startAnim()
  }

  build() {
    Column({ space: 15 }) {
      Progress({
        value: this.progressValue, // 进度条当前进度值
        total: PROGRESS_MAX, // 进度条总长
        type: ProgressType.Ring, // 进度条类型,分为Linear线性样式、ScaleRing环形有刻度样式、Ring环形无刻度样式、Eclipse圆形样式、Capsule胶囊样式
      })
        .style({
          strokeWidth: 10, // 进度条宽度,默认4vp
          enableSmoothEffect: true // 进度平滑动效的开关。开启平滑动效后设置进度,进度会从当前值渐变至设定值,否则进度从当前值突变至设定值,默认值true
        })
        .width(100) // 进度条组件宽度
        .color('# A97CF9') // 进度条前景色
        .backgroundColor(Color.White) // 进度条背景色
      Button('开始动画')
        .onClick(() => {
          this.startAnim()
        })
      Button('结束动画')
        .onClick(() => {
          // 取消先前通过调用 setInterval() 设置的重复定时任务(进度条从最小值到最大值循环增加)
          clearInterval(this.intervalId)
          // 将intervalId重置为null,表示重复定时任务未启动
          this.intervalId = null
          // 进度值重置为最小值
          this.progressValue = PROGRESS_MIN
        })
    }
    .width('100%')
    .padding({ top: 5 })
    .backgroundColor(Color.Gray)
  }

  // 开启进度条动画
  private startAnim() {
    // intervalId为null时表示未启动interval
    if (this.intervalId === null) {
      this.intervalId = setInterval(() => {
        // 使用setInterval() 方法重复执行以下代码片段,在每次调用之间具有固定的时间间隔20毫秒
        // 每次进度值+1
        this.progressValue++
        // 当进度值达到最大值时,将进度值重置为最小值,循环往复
        if (this.progressValue == PROGRESS_MAX) {
          this.progressValue = PROGRESS_MIN
        }
      }, 20)
    }
  }
}

更多关于HarmonyOS鸿蒙Next中怎么解决Progress组件配合animateTo实现循环动画无反应问题的实战教程也可以访问 https://www.itying.com/category-93-b0.html

1 回复

更多关于HarmonyOS鸿蒙Next中怎么解决Progress组件配合animateTo实现循环动画无反应问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


使用animateTo无法实现Progress组件的循环动画,应改用setInterval定时器控制进度值

示例代码如下:

@Entry
@Component
struct ProgressAnim {
  @State progressValue: number = PROGRESS_MIN
  @State intervalId: number | null = null

  aboutToAppear(): void {
    this.startAnim()
  }

  build() {
    Column({ space: 15 }) {
      Progress({
        value: this.progressValue,
        total: PROGRESS_MAX,
        type: ProgressType.Ring,
      })
        .style({
          strokeWidth: 10,
          enableSmoothEffect: true
        })
        .width(100)
        .color('#A97CF9')
        .backgroundColor(Color.White)
      Button('开始动画')
        .onClick(() => {
          this.startAnim()
        })
      Button('结束动画')
        .onClick(() => {
          clearInterval(this.intervalId)
          this.intervalId = null
          this.progressValue = PROGRESS_MIN
        })
    }
    .width('100%')
    .padding({ top: 5 })
    .backgroundColor(Color.Gray)
  }

  private startAnim() {
    if (this.intervalId === null) {
      this.intervalId = setInterval(() => {
        this.progressValue++
        if (this.progressValue == PROGRESS_MAX) {
          this.progressValue = PROGRESS_MIN
        }
      }, 20)
    }
  }
}
回到顶部