HarmonyOS 动画一:如何优雅编排控件
本文概述:
属性动画
属性动画概述
-
属性动画最大的特点:其在使用时,使用链式调用,作为被修饰对象的一个属性
-
属性动画是什么
- 在修饰对象的内置属性改变时,使用动画进行过渡。
- 理论上讲,只要修饰对象的属性存在二值性,即可使用属性动画进行过渡
-
属性动画有什么作用
-
常用动画参数:用户可以指定什么
- 参数:动画持续时间、动画播放速度、动画速率曲线、动画延迟执行时长、动画播放次数、动画播放模式、
- 回调:在动画播放时触发
-
-
我们怎么使用属性动画
- 确定修饰对象的属性,及属性始末状态
- 确定修饰作用域,及动画参数
-
细节问题
- 尽量不要对颜色使用属性动画过渡,因为实现的效果不固定
- 在对尺寸进行变化时,需考虑变化前后及过程中对其余布局的影响
属性动画的使用步骤
-
实际效果:可以用作进度条填充,做一个完整进度的实时动态展示
-
首先,确定动画的作用对象及相关属性
- 常用属性:宽高尺寸、摆放状态、控件样式
- 一般使用@State 修饰控件状态变量,因为其值改变,会自动触发build() 二次执行
@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}) } }
-
其次,确定控件的始末状态及触发逻辑
- 一般使用控件监听,并在监听逻辑中对控件状态变量进行二次赋值,从而确定控件的始末状态
@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({
的上面一行
@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}) } }
属性动画的作用域问题
-
剔除控件高度动画过渡:可将上述代码改为
-
注意,此时控件的高度将不再受动画修饰
-
实际效果:
@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
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() 二次执行
@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}) } }
-
其次,确定控件的始末状态及触发逻辑
-
核心在于补全两个参数
animateTo({},() => {})
@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}) } }
-
显示动画实际使用实例
-
点击按钮后实现图片动态隐藏及展示
- 文本框表示当前图像状态,
- 隐藏逻辑:先旋转再隐藏
- 显示逻辑:有明显展开
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:是否跟随路径进行旋转。
路径动画使用实例
-
点击按钮之后,可观察按钮在水平方向上进行动态移动
-
实际效果:
/*路径动画*/
@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)
}
}