帧动画(ohos.animator)
说明:
当前为Beta阶段。
帧动画具备逐帧回调的特性,便于开发者在每一帧中处理需调整的属性。通过向应用提供onFrame逐帧回调,帧动画使开发者能够在应用的每一帧设置属性值,从而实现组件属性值变化的自然过渡,营造出动画效果。
与属性动画相比,帧动画能让开发者实时感知动画进程,即时调整UI值,具备事件即时响应和可暂停的优势,但在性能上略逊于属性动画。当属性动画能满足需求时,建议优先采用属性动画接口实现。属性动画接口请参见实现属性动画。
| 名称 | 实现方式 | 事件响应方式 | 可暂停 | 性能 |
|---|---|---|---|---|
| 帧动画(ohos.animator) | 开发者可每帧修改UI侧属性值,UI侧属性实时更新 | 实时响应 | 是 | 较差 |
| 属性动画 | UI侧只计算动画最终状态,动画过程为渲染值在改变,UI侧一直为动画最终状态,不感知实时渲染值 | 按最终状态响应 | 否 | 较好 |
如图所示,帧动画在动画过程中即可实时响应,而属性动画按最终状态响应。


使用帧动画实现动画效果
使用如下步骤可以创建一个简单的animator,并且在每个帧回调中打印当前插值。
-
引入相关依赖。
import kit.ArkUI.* -
创建执行动画的对象。
// 创建动画的初始参数 this.backAnimator = getUIContext.createAnimator(AnimatorOptions( duration: 1500, easing: "friction", delay: 0, fill: AnimatorFill.Forwards, direction: AnimatorDirection.Normal, iterations: 2, // 动画onFrame 插值首帧值 begin: 200.0, // 动画onFrame 插值尾帧值 end: 400.0 )) // 设置接收到帧时回调,动画播放过程中每帧会调用onFrame回调 this.backAnimator?.onFrame = { progress: Float64 => Hilog.info(0,"",current value is :" + progress.toString()) } -
播放动画。
// 播放动画 this.backAnimator?.play() -
动画执行完成后手动释放AnimatorResult对象。
// 释放动画对象 this.backAnimator = None
使用帧动画实现小球抛物运动
-
引入相关依赖。
import kit.ArkUI.* -
定义要做动画的组件。
Button() .borderRadius(45.vp) .width(60) .height(60) .translate( x: this.translateX, y: this.translateY ) -
在onPageShow中创建AnimatorResult对象。
// 创建animatorResult对象 protected override func onPageShow() { this.backAnimator = getUIContext.createAnimator(AnimatorOptions( duration: 4000, easing: "ease", delay: 0, fill: AnimatorFill.Forwards, direction: AnimatorDirection.Normal, iterations: 1, begin: 0.0, end: 200.0 )) this.backAnimator?.onFrame = { progress: Float64 => this.translateX = Int64(progress) if (progress > this.topWidth && this.translateY < this.bottomHeight) { this.translateY = Int64(pow(Float64(progress - this.topWidth), 2) * this.g) } } // 动画取消时执行方法 this.backAnimator?.onCancel = { => this.animatorStatus = '取消' } // 动画完成时执行方法 this.backAnimator?.onFinish = { => this.animatorStatus = '完成' } // 动画重复播放时执行方法 this.backAnimator?.onRepeat = { => Hilog.info(0,"","动画重复播放") } } -
定义动画播放,重置,暂停的按钮。
Button('播放').onClick({ => this.backAnimator?.play() this.animatorStatus = '播放中' }).width(80).height(35) Button("重置").onClick({ => this.translateX = 0 this.translateY = 0 }).width(80).height(35) Button("暂停").onClick({ => this.backAnimator?.pause() this.animatorStatus = '暂停' }).width(80).height(35) -
在页面隐藏或销毁的生命周期中释放动画对象,避免内存泄漏。
protected override func onPageHide() { this.backAnimator = None }
完整示例如下。
package ohos_app_cangjie_entry
import ohos.arkui.ui_context.*
import kit.ArkUI.*
import std.math.*
import ohos.arkui.state_macro_manage.*
import kit.PerformanceAnalysisKit.Hilog
@Entry
@Component
class EntryView {
var backAnimator: ?AnimatorResult = None
@State
var animatorStatus: String = '创建'
var topWidth: Float64 = 150.0
var bottomHeight: Int64 = 100
var g: Float64 = 0.18
@State
var translateX: Int64 = 0
@State
var translateY: Int64 = 0
protected override func onPageShow() {
this.backAnimator = this
.getUIContext()
.createAnimator(
AnimatorOptions(
duration: 4000,
easing: "ease",
delay: 0,
fill: AnimatorFill.Forwards,
direction: AnimatorDirection.Normal,
iterations: 1,
begin: 0.0,
end: 200.0
)
)
this.backAnimator?.onFrame = {
progress: Float64 =>
this.translateX = Int64(progress)
if (progress > this.topWidth && this.translateY < this.bottomHeight) {
this.translateY = Int64(pow(Float64(progress - this.topWidth), 2) * this.g)
}
}
this.backAnimator?.onCancel = {
=> this.animatorStatus = '取消'
}
this.backAnimator?.onFinish = {
=> this.animatorStatus = '完成'
}
this.backAnimator?.onRepeat = {
=> Hilog.info(0, "", "动画重复播放")
}
}
protected override func onPageHide() {
this.backAnimator = None
}
func build() {
Column() {
Column(space: 30) {
Button('播放')
.onClick(
{
evt =>
this.backAnimator?.play()
this.animatorStatus = '播放中'
}
)
.width(80)
.height(35)
.margin(top: 20)
Button("重置")
.onClick(
{
evt =>
this.translateX = 0
this.translateY = 0
}
)
.width(80)
.height(35)
Button("暂停")
.onClick(
{
evt =>
this.backAnimator?.pause()
this.animatorStatus = '暂停'
}
)
.width(80)
.height(35)
}
.width(100.percent)
.height(25.percent)
Stack() {
Button()
.borderRadius(45.vp)
.width(60)
.height(60)
.translate(x: this.translateX, y: this.translateY)
.margin(left: 20)
}
.width(100.percent)
.height(45.percent)
.align(Alignment.Start)
Text("当前动画状态为:" + this.animatorStatus)
}
.width(100.percent)
.height(100.percent)
}
}
