HarmonyOS 动画一:如何优雅编排控件

HarmonyOS 动画一:如何优雅编排控件

本文概述:

  • 技术背景:优化用户体验是移动端开发中老生常谈的话题,合理的动画植入可提升控件编排上的美感。
  • 文章大纲:基本动画:属性动画、显示动画、显示动画进阶之路径动画
  • 本文代码链接
  • 动画完整代码链接

属性动画

属性动画概述

  • 属性动画最大的特点:其在使用时,使用链式调用,作为被修饰对象的一个属性

  • 属性动画是什么

    • 在修饰对象的内置属性改变时,使用动画进行过渡。
    • 理论上讲,只要修饰对象的属性存在二值性,即可使用属性动画进行过渡
  • 属性动画有什么作用

    • 常用动画参数:用户可以指定什么

      • 参数:动画持续时间、动画播放速度、动画速率曲线、动画延迟执行时长、动画播放次数、动画播放模式、
      • 回调:在动画播放时触发
  • 我们怎么使用属性动画

    • 确定修饰对象的属性,及属性始末状态
    • 确定修饰作用域,及动画参数
  • 细节问题

    • 尽量不要对颜色使用属性动画过渡,因为实现的效果不固定
    • 在对尺寸进行变化时,需考虑变化前后及过程中对其余布局的影响

属性动画的使用步骤

  • 实际效果:可以用作进度条填充,做一个完整进度的实时动态展示

  • 首先,确定动画的作用对象及相关属性

    • 常用属性:宽高尺寸、摆放状态、控件样式
    • 一般使用@State 修饰控件状态变量,因为其值改变,会自动触发build() 二次执行
    less 复制代码
    @Entry
    @Component
    struct AttrAnimationPage {
      @State wSize: number = 250//控件宽度
      @State hSize: number = 100//控件高度
    ​
      build() {
        Column() {
          Button('动画调整按钮宽高')
            .margin(30)
            .width(this.wSize)
            .height(this.hSize)
        }.width('100%').margin({top: 20})
      }
    }
  • 其次,确定控件的始末状态及触发逻辑

    • 一般使用控件监听,并在监听逻辑中对控件状态变量进行二次赋值,从而确定控件的始末状态
    kotlin 复制代码
    @Entry
    @Component
    struct AttrAnimationPage {
      ..................
    ​
      build() {
        Column() {
          Button('动画调整按钮宽高')
            .onClick(() => {//确定控件的始末状态及触发逻辑
              if (this.flag) {
                this.wSize = 150
                this.hSize = 60
              } else {
                this.wSize = 250
                this.hSize = 100
              }
              this.flag = !this.flag // 取反
            })
            .margin(30)
            .width(this.wSize)
            .height(this.hSize)
        }.width('100%').margin({top: 20})
      }
    }
  • 最后,根据需要,确定属性动画作用域及动画参数

    • 属性动画作用域起点:其修饰的控件内的第一行代码
    • 属性动画作用域起点:.animation({ 的上面一行
    less 复制代码
    @Entry
    @Component
    struct AttrAnimationPage {
      ..................
    ​
      build() {
        Column() {
          Button('动画调整按钮宽高')
            .onClick(() => {//属性动画作用域起点
              ..................
            })
            .margin(30)
            .width(this.wSize)
            .height(this.hSize)//属性动画作用域终点
            .animation({
              duration: 2000,//动画的持续时间
              curve: Curve.EaseOut, // 动画的速率 默认值Linear,均速
              iterations: -1,//动画重复次数
              playMode: /*PlayMode.Normal 默认值*/ PlayMode.Alternate//保证开启结束都有动画
            })
        }.width('100%').margin({top: 20})
      }
    }

属性动画的作用域问题

  • 剔除控件高度动画过渡:可将上述代码改为

    • 注意,此时控件的高度将不再受动画修饰

    • 实际效果:

    less 复制代码
    @Entry
    @Component
    struct AttrAnimationPage {
      ..................
    ​
      build() {
        Column() {
          Button('动画调整按钮宽度')
            .onClick(() => {//属性动画作用域起点
              ..................
            })
            .margin(30)
            .backgroundColor(this.btnBackColor)
            .width(this.wSize)//属性动画作用域终点
            .animation({
              ............
            })
            
            .height(this.hSize)//此时,按钮的高度不再被动画所修饰
            
        }.width('100%').margin({top: 20})
      }
    }
  • 多作用域相互覆盖

    • 该动画最终持续时间仍为500ms,虽然说,后面的动画作用域看起来会掩盖前面。但是,属性动画的最终效果遵从就近原则

    • 实际效果:动画的持续时间只有500ms

    yaml 复制代码
    Button('作用域覆盖')
        .onClick(() => {
          this.rotateAngle = 90
        })
        .margin(30)
        .rotate({angle: this.rotateAngle})
        .animation({
          duration: 500,
          curve: Curve.Friction, // 动画的速率,刚开始很快,快结束变慢
          iterations: -1, // 设置-1表示动画无限执行循环
          playMode: PlayMode.Alternate//保证开始结束都有动画
        })
        .animation({
          duration: 2000,
        })
        .animation({
          duration: 4000,
        })
        .animation({
          duration: 6000,
        })
        .animation({
          duration: 10000,
        })

显示动画

显示动画概述

  • 显示动画的最大特点:使用animateTo ({},()=>{}) 的形式

  • 显示动画有什么作用

    • 常用动画参数:用户可以指定什么

      • 参数:动画持续时间、动画播放速度、动画速率曲线、动画延迟执行时长、动画播放次数、动画播放模式、
      • 回调:在动画播放时触发
  • 我们怎么使用显示动画:核心在于确定两个参数

    • 确定代码位置:明确显示动画触发时机
    • 第一个参数:显示动画的基本属性及回调逻辑
    • 第二个参数:动画启动后UI 属性的更新逻辑
  • 细节问题

    • 显示动画对代码的侵入性较高,应优先考虑属性动画

显示动画使用步骤

  • 实现效果:点击后动态调整宽高,且自动触发动画播放结束回调
  • 首先,确定动画的作用对象及相关属性

    • 常用属性:宽高尺寸、摆放状态、控件样式
    • 一般使用@State 修饰控件状态变量,因为其值改变,会自动触发build() 二次执行
    less 复制代码
    @Entry
    @Component
    struct AnimateToPage {
      @State wSize: number = 250//控件宽度
      @State hSize: number = 100//控件高度
    ​
      build() {
        Column() {
          Button('动态调整按钮宽高')
            .margin(30)
            .width(this.wSize)
            .height(this.hSize)
            .onClick(() => {
                ..................
            }
        }.width('100%').margin({top: 20})
      }
    }
  • 其次,确定控件的始末状态及触发逻辑

    • 核心在于补全两个参数

      less 复制代码
      animateTo({},() => {})
    less 复制代码
    @Entry
    @Component
    struct AnimateToPage {
      @State flag: boolean = true
      ..................
    ​
      @State rotateAngle: number = 0
    ​
      build() {
        Column() {
          Button('动态调整按钮宽高')
            .margin(30)
            .width(this.wSize)
            .height(this.hSize)
            
            .onClick(() => {
              if (this.flag) {
                animateTo({//配置显示动画参数,用于动画过渡
                  duration: 2000,
                  curve: Curve.Friction,
                  onFinish: () => {//显示动画结束后回调
                    promptAction.showToast({message: '动画1执行结束了'})
                  }
                }, () => {//显示动画修饰对象的结束状态
                  this.wSize = 150
                  this.hSize = 60
                })
              } else {
                animateTo({}, () => {
                  this.wSize = 250
                  this.hSize = 100
                })
              }
              this.flag = !this.flag//监听标志为取反,支持重复点击
            })
        }.width('100%').margin({top: 20})
      }
    }

显示动画实际使用实例

  • 点击按钮后实现图片动态隐藏及展示

    • 文本框表示当前图像状态,
    • 隐藏逻辑:先旋转再隐藏
    • 显示逻辑:有明显展开
    kotlin 复制代码
    import promptAction from '@ohos.promptAction'
    @Entry
    @Component
    struct AnimateToPage {
    
      @State flag: boolean = true
      @State show: string = 'show'
    
      build() {
        Column() {
          Button(this.show).width(88).height(38).margin(30)
            .onClick(() => {
              animateTo({duration: 2000}, () => {
                // 点击Button的时候 控制 下面的Image的显示与隐藏
                if (this.flag) {
                  this.show = 'hide'
                } else {
                  this.show = 'show'
                }
                this.flag = !this.flag
              })
            })
          if (this.flag) {
            // Image的显示与隐藏 配置不同的 过度效果
            Image($r('app.media.app_icon')).width(300).height(300)
              .transition({type: TransitionType.Insert, scale: {x: 0, y: 1.0}}) // Image出现显示的时候, 渐变缩放的效果
              .transition({type: TransitionType.Delete, rotate: {angle: 180}}) // Image出现隐藏的时候, 选择180°
          }
        }
        .width('100%').margin({top: 20})
      }
    }

路径动画

路径动画概述

  • 概述:路径动画属于显示动画的一种,但此动画不常用,仅做补充
  • 修饰对象:一般用于修饰系统控件,类似按钮等

路径动画使用步骤

  • 设置组件的运动路径。

    • path:位移动画的运动路径,使用svg路径字符串。path中支持使用start和end进行起点和终点的替代,如:'Mstart.x start.y L50 50 Lend.x end.y Z',更多说明请参考绘制路径
    • from:运动路径的起点。取值范围:[0, 1]
    • to:运动路径的终点。取值范围:[0, 1]
    • rotatable:是否跟随路径进行旋转。

路径动画使用实例

  • 点击按钮之后,可观察按钮在水平方向上进行动态移动

  • 实际效果:

less 复制代码
/*路径动画*/
@Entry
@Component
struct PathStudy {
  @State value: boolean = true
​
  build() {
    Column() {
      Button("PathAnimation OK").margin(50)
        // 从 起点移动到 (300/200), 紧接着 再次移动到 (300/500)
        .motionPath({path: 'Mstart.x start.y L300 200 L300 500 Lend.x end.y', from: 0.0, to: 1.0, rotatable:true})
        .onClick(() => {
          animateTo({duration: 6000, curve: Curve.Friction}, () => {
            this.value = !this.value // 通过this.value改变组件的位置
          })
        })
    }.width('100%').height('100%').alignItems(this.value ? HorizontalAlign.Start : HorizontalAlign.Center)
  }
}
相关推荐
前端大卫3 小时前
Vue3 + Element-Plus 自定义虚拟表格滚动实现方案【附源码】
前端
却尘3 小时前
Next.js 请求最佳实践 - vercel 2026一月发布指南
前端·react.js·next.js
ccnocare3 小时前
浅浅看一下设计模式
前端
Lee川3 小时前
🎬 从标签到屏幕:揭秘现代网页构建与适配之道
前端·面试
一个处女座的程序猿3 小时前
AI之Agent之VibeCoding:《Vibe Coding Kills Open Source》翻译与解读
人工智能·开源·vibecoding·氛围编程
Ticnix3 小时前
ECharts初始化、销毁、resize 适配组件封装(含完整封装代码)
前端·echarts
纯爱掌门人3 小时前
终焉轮回里,藏着 AI 与人类的答案
前端·人工智能·aigc
twl4 小时前
OpenClaw 深度技术解析
前端
崔庆才丨静觅4 小时前
比官方便宜一半以上!Grok API 申请及使用
前端
星光不问赶路人4 小时前
vue3使用jsx语法详解
前端·vue.js