1# 帧动画(ohos.animator) 2 3帧动画具备逐帧回调的特性,便于开发者在每一帧中处理需调整的属性。通过向应用提供onFrame逐帧回调,帧动画使开发者能够在应用的每一帧设置属性值,从而实现组件属性值变化的自然过渡,营造出动画效果。帧动画接口详情可参考[@ohos.animator (动画)](../reference/apis-arkui/js-apis-animator.md)。 4 5与属性动画相比,帧动画能让开发者实时感知动画进程,即时调整UI值,具备事件即时响应和可暂停的优势,但在性能上略逊于属性动画。当属性动画能满足需求时,建议优先采用属性动画接口实现。属性动画接口可参考[实现属性动画](./arkts-attribute-animation-apis.md)。 6 7| 名称 | 实现方式 | 事件响应方式 | 可暂停 | 性能 | 8| -------- | -------- | -------- | -------- |-------- | 9| 帧动画(ohos.animator) | 开发者可每帧修改UI侧属性值,UI侧属性实时更新 | 实时响应 | 是 | 较差 | 10| 属性动画 | UI侧只计算动画最终状态,动画过程为渲染值在改变,UI侧一直为动画最终状态,不感知实时渲染值 | 按最终状态响应 | 否 | 较好 | 11 12如图所示,帧动画在动画过程中即可实时响应,而属性动画按最终状态响应。 13 14 15 16 17 18## 使用帧动画实现动画效果 19 20使用如下步骤可以创建一个简单的animator,并且在每个帧回调中打印当前插值。 21 221. 引入相关依赖。 23 24 ```ts 25 import { AnimatorOptions, AnimatorResult } from '@kit.ArkUI'; 26 ``` 27 282. 创建执行动画的对象。 29 30 ```ts 31 // 创建动画的初始参数 32 let options: AnimatorOptions = { 33 duration: 1500, 34 easing: "friction", 35 delay: 0, 36 fill: "forwards", 37 direction: "normal", 38 iterations: 2, 39 // 动画onFrame 插值首帧值 40 begin: 200.0, 41 // 动画onFrame 插值尾帧值 42 end: 400.0 43 }; 44 let result: AnimatorResult = this.getUIContext().createAnimator(options); 45 // 设置接收到帧时回调,动画播放过程中每帧会调用onFrame回调 46 result.onFrame = (value: number) => { 47 console.log("current value is :" + value); 48 } 49 ``` 50 513. 播放动画。 52 53 ```ts 54 // 播放动画 55 result.play(); 56 ``` 57 584. 动画执行完成后手动释放AnimatorResult对象。 59 60 ```ts 61 // 释放动画对象 62 result = undefined; 63 ``` 64 65## 使用帧动画实现小球抛物运动 66 671. 引入相关依赖。 68 69 ```ts 70 import { AnimatorOptions, AnimatorResult } from '@kit.ArkUI'; 71 ``` 72 732. 定义要做动画的组件。 74 75 ```ts 76 Button() 77 .width(60) 78 .height(60) 79 .translate({ x: this.translateX, y: this.translateY }) 80 ``` 81 823. 在onPageShow中创建AnimatorResult对象。 83 84 ```ts 85 onPageShow(): void { 86 //创建animatorResult对象 87 this.animatorOptions = this.getUIContext().createAnimator(options); 88 this.animatorOptions.onFrame = (progress: number) => { 89 this.translateX = progress; 90 if (progress > this.topWidth && this.translateY < this.bottomHeight) { 91 this.translateY = Math.pow(progress - this.topWidth, 2) * this.g; 92 } 93 } 94 //动画取消时执行方法 95 this.animatorOptions.onCancel = () => { 96 this.animatorStatus = '取消'; 97 } 98 //动画完成时执行方法 99 this.animatorOptions.onFinish = () => { 100 this.animatorStatus = '完成'; 101 } 102 //动画重复播放时执行方法 103 this.animatorOptions.onRepeat = () => { 104 console.log("动画重复播放"); 105 } 106 } 107 ``` 108 1094. 定义动画播放,重置,暂停的按钮。 110 111 ```ts 112 Button('播放').onClick(() => { 113 this.animatorOptions?.play(); 114 this.animatorStatus = '播放中' 115 }).width(80).height(35) 116 Button("重置").onClick(() => { 117 this.translateX = 0; 118 this.translateY = 0; 119 }).width(80).height(35) 120 Button("暂停").onClick(() => { 121 this.animatorOptions?.pause(); 122 this.animatorStatus = '暂停' 123 }).width(80).height(35) 124 ``` 1255. 在页面隐藏或销毁的生命周期中释放动画对象,避免内存泄漏。 126 ```ts 127 onPageHide(): void { 128 this.animatorOptions = undefined; 129 } 130 ``` 131 132完整示例如下。 133 134```ts 135import { AnimatorOptions, AnimatorResult } from '@kit.ArkUI'; 136 137@Entry 138@Component 139struct Index { 140 @State animatorOptions: AnimatorResult | undefined = undefined; 141 @State animatorStatus: string = '创建'; 142 begin: number = 0; 143 end: number = 300 144 topWidth: number = 150; 145 bottomHeight: number = 100; 146 g: number = 0.18 147 animatorOption: AnimatorOptions = { 148 duration: 4000, 149 delay: 0, 150 easing: 'linear', 151 iterations: 1, 152 fill: "forwards", 153 direction: 'normal', 154 begin: this.begin, 155 end: this.end 156 }; 157 @State translateX: number = 0; 158 @State translateY: number = 0; 159 160 onPageShow(): void { 161 this.animatorOptions = this.getUIContext().createAnimator(this.animatorOption) 162 this.animatorOptions.onFrame = (progress: number) => { 163 this.translateX = progress; 164 if (progress > this.topWidth && this.translateY < this.bottomHeight) { 165 this.translateY = Math.pow(progress - this.topWidth, 2) * this.g; 166 } 167 } 168 this.animatorOptions.onCancel = () => { 169 this.animatorStatus = '取消'; 170 } 171 this.animatorOptions.onFinish = () => { 172 this.animatorStatus = '完成'; 173 } 174 this.animatorOptions.onRepeat = () => { 175 console.log("动画重复播放"); 176 } 177 } 178 179 onPageHide(): void { 180 this.animatorOptions = undefined; 181 } 182 183 build() { 184 Column() { 185 Column({ space: 30 }) { 186 Button('播放').onClick(() => { 187 this.animatorOptions?.play(); 188 this.animatorStatus = '播放中'; 189 }).width(80).height(35) 190 Button("重置").onClick(() => { 191 this.translateX = 0; 192 this.translateY = 0; 193 }).width(80).height(35) 194 Button("暂停").onClick(() => { 195 this.animatorOptions?.pause(); 196 this.animatorStatus = '暂停'; 197 }).width(80).height(35) 198 }.width("100%").height('25%') 199 200 Stack() { 201 Button() 202 .width(60) 203 .height(60) 204 .translate({ x: this.translateX, y: this.translateY }) 205 } 206 .width("100%") 207 .height('45%') 208 .align(Alignment.Start) 209 210 Text("当前动画状态为:" + this.animatorStatus) 211 }.width("100%").height('100%') 212 } 213} 214``` 215 216