HarmonyOS ArkUI 属性动画入门详解
- 前言
- 属性动画是什么?
- 我们借助官方的话来说,
- 我们自己简单归纳下
- 参数解释
- 举个例子
- 旋转动画
- 位移动画
- 组合动画
- 总结
前言
鸿蒙OS最近吹的很凶,赶紧卷一下。学习过程中发现很多人吐槽官方属性动画这一章比较敷衍,我第一遍也看的半懂不懂的,所以就有了这篇文章,在官方的介绍基础上补充了自己的理解
属性动画是什么?
我们借助官方的话来说,
属性动画,是最为基础的动画,其功能强大、使用场景多,应用范围较广。常用于如下场景中:
一、页面布局发生变化。例如添加、删除部分组件元素。
二、页面元素的可见性和位置发生变化。例如显示或者隐藏部分元素,或者将部分元素从一端移动到另外一端。
三、页面中图形图片元素动起来。例如使页面中的静态图片动起来。
简单来说,属性动画是组件的通用属性发生改变时而产生的属性渐变效果。如下图所示,其原理是,当组件的通用属性发生改变时,组件状态由初始状态逐渐变为结束状态的过程中,会创建多个连续的中间状态,逐帧播放后,就会形成属性渐变效果,从而形成动画。
属性动画的使用方式也非常简单,只需要给组件(包括基础组件和容器组件)添加animation属性,并设置好参数,如下代码所示:
Image($r('app.media.image1'))
.animation({
duration: 1000,
tempo: 1.0,
delay: 0,
curve: Curve.Linear,
playMode: PlayMode.Normal,
iterations: 1
})
别急哈,等你把上面这个代码贴上去,你就会发现上例代码并没有动画,这是因为
1、animation属性作用域。animation自身也是组件的一个属性,其作用域为animation之前。即产生属性动画的属性须在animation之前声明,其后声明的将不会产生属性动画。以示例中的五个图标动画为例,我们期望产生动画的属性为Image组件的width属性,故该属性width需在animation属性之前声明。如果将该属性width在animation之后声明,则不会产生动画效果。
2、产生属性动画的属性变化时需触发UI状态更新。在上面的示例中,因为缺少产生动画的属性,所以并不会有动画
3、产生属性动画的属性本身需满足一定的要求,并非任何属性都可以产生属性动画。目前支持的属性包括width、height、position、opacity、backgroundColor、scale、rotate、translate等
我们自己简单归纳下
属性动画 = 属性 + a n i m a t i o n (属性作用域) 属性动画 = 属性 + animation (属性作用域) 属性动画=属性+animation(属性作用域)
这个属性就是目前支持的width、height、position、opacity、backgroundColor、scale、rotate、translate等
参数解释
属性名称 | 属性类型 | 默认值 | 描述 |
---|---|---|---|
duration | number | 1000 | 动画时长,单位为毫秒,默认时长为1000毫秒。 |
tempo | number | 1.0 | 动画的播放速度,值越大动画播放越快,值越小播放越慢,为0时无动画效果。 |
curve | Curve | Curve.Linear | 动画变化曲线,默认曲线为线性。 |
delay | number | 0 | 延时播放时间,单位为毫秒,默认不延时播放。 |
iterations | number | 1 | 播放次数,默认一次,设置为-1时表示无限次播放。 |
playMode | PlayMode | PlayMode.Normal | 设置动画播放模式,默认播放完成后重头开始播放。 |
onFinish | function | - | 动画播放结束时回调该函数。 |
其中变化曲线curve枚举值为:
名称 | 描述 |
---|---|
Linear | 表示动画从头到尾的速度都是相同的。 |
Ease | 表示动画以低速开始,然后加快,在结束前变慢,CubicBezier(0.25, 0.1, 0.25, 1.0)。 |
EaseIn | 表示动画以低速开始,CubicBezier(0.42, 0.0, 1.0, 1.0)。 |
EaseOut | 表示动画以低速结束,CubicBezier(0.0, 0.0, 0.58, 1.0)。 |
EaseInOut | 表示动画以低速开始和结束,CubicBezier(0.42, 0.0, 0.58, 1.0)。 |
FastOutSlowIn | 标准曲线,cubic-bezier(0.4, 0.0, 0.2, 1.0)。 |
LinearOutSlowIn | 减速曲线,cubic-bezier(0.0, 0.0, 0.2, 1.0)。 |
FastOutLinearIn | 加速曲线,cubic-bezier(0.4, 0.0, 1.0, 1.0)。 |
ExtremeDeceleration | 急缓曲线,cubic-bezier(0.0, 0.0, 0.0, 1.0)。 |
Sharp | 锐利曲线,cubic-bezier(0.33, 0.0, 0.67, 1.0)。 |
Rhythm | 节奏曲线,cubic-bezier(0.7, 0.0, 0.2, 1.0)。 |
Smooth | 平滑曲线,cubic-bezier(0.4, 0.0, 0.4, 1.0)。 |
Friction | 阻尼曲线,CubicBezier(0.2, 0.0, 0.2, 1.0)。 |
人话解释:相同时间,结果相同,不同的过程
播放模式playMode枚举值为:
名称 | 描述 |
---|---|
Normal | 动画按正常播放。 |
Reverse | 动画反向播放。 |
Alternate | 动画在奇数次(1、3、5…)正向播放,在偶数次(2、4、6…)反向播放。 |
AlternateReverse | 动画在奇数次(1、3、5…)反向播放,在偶数次(2、4、6…)正向播放。 |
注意onFinish回调函数与参数iterations有关。当参数iterations播放结束时,会调用onFinish函数来进行后续的业务处理。而当iterations设置为-1时,表示无限次播放,则onFinish回调函数不会被调用。
举个例子
旋转动画
@State rotateAngle : number = 0
...
Text("旋转动画")
Row(){
Button("旋转动画开始",{type: ButtonType.Capsule,stateEffect:true})
.onClick(()=>{
this.rotateAngle =360
})
Image($r('app.media.loading'))
.width(100)
.height(100)
.rotate({ x: 0, y: 0, z: 1, angle: this.rotateAngle })
.animation({
duration: 2000,
tempo: 1.0,
delay: 0,
curve: Curve.Linear,
playMode: PlayMode.Normal,
iterations: -1
})
}
.height("30%")
.width("100%")
.backgroundColor(Color.Green)
上例可以看到,添加了rotate旋转作用域,和animation作用域,并通过rotateAngle 状态的改变控制了动画的开始,效果如下
位移动画
@State xState : number = 0
@State yState : number = 0
...
Text("位移动画")
Row(){
Text('HarmonyOS')
.width(200)
.height(100)
.fontColor(Color.Blue)
.fontSize(30)
.fontStyle(FontStyle.Italic)
.fontWeight(FontWeight.Bold)
.fontFamily('Arial')
.margin(100)
.position({x: this.xState,y:this.yState})
.animation({
duration: 1000,
tempo: 1.0,
delay: 0,
curve: Curve.Linear,
playMode: PlayMode.Normal,
iterations: 1,
onFinish:()=>{
if ( this.xState == 0) {
this.xState =100
this.yState =100
}else {
this.xState =0
this.yState =0
}
}
})
}
.height("30%")
.width("100%")
.backgroundColor(Color.Yellow)
上例我们可以看到,通过设置position和animation实现了位移动画,并且在其到达指定路径的onFinish回调中重新设置了xState 和yState ,以达到一种另类的永久动画效果。运行效果如下
组合动画
并不是一次只能加一个属性,也可以多个一起加
@State watermelonRotateAngle : number = 0
@State width : number = 50
@State height : number = 50
...
Text("组合动画")
Row(){
Button("组合动画开始",{type: ButtonType.Capsule,stateEffect:true})
.onClick(()=>{
this.watermelonRotateAngle =360
this.width = 100
this.height = 100
})
Image($r('app.media.watermelon'))
.width(this.width)
.height(this.height)
.rotate({ x: 0, y: 0, z: 1, angle: this.watermelonRotateAngle })
.animation({
duration: 2000,
tempo: 1.0,
delay: 0,
curve: Curve.Linear,
playMode: PlayMode.Normal,
iterations: -1
})
}
.height("40%")
.width("100%")
.backgroundColor(Color.Orange)
上面我们有三个属性动画,分别是width ,height和 rotate,效果如下
总结
参考:HarmonyOS第一课
教程跟以前比来说丰富了很多,也比较系统。所以博主只会在比官方现在教程比较笼统或自己觉得理解比较晦涩的地方进行单篇输出,不会像Compose一样边学变更新了。如果对Compose有兴趣可以看看我的Jetpack Compose入门详解(实时更新)
吐槽一下:官方文档属性动画文档写的是真的敷衍,给人一种我都讲了你不懂是你的事情的感觉(当然,可能是我比较菜)