文档管理中心

帧动画(ohos.animator)

帧动画具备逐帧回调的特性,便于开发者在每一帧中处理需调整的属性。通过向应用提供AnimatorResult的onFrame属性逐帧回调,帧动画使开发者能够在应用的每一帧设置属性值,从而实现组件属性值变化的自然过渡,营造出动画效果。帧动画接口详情可参考@ohos.animator (动画)

与属性动画相比,帧动画能让开发者实时感知动画进程,即时调整UI值,具备事件即时响应和可暂停的优势,但在性能上略逊于属性动画。当属性动画能满足需求时,建议优先采用属性动画接口实现。属性动画接口可参考实现属性动画

展开
名称 实现方式 事件响应方式 可暂停 性能
帧动画(ohos.animator) 开发者可每帧修改UI侧属性值,UI侧属性实时更新 实时响应 较差
属性动画 UI侧只计算动画最终状态,动画过程为渲染值在改变,UI侧一直为动画最终状态,不感知实时渲染值 按最终状态响应 较好

如图所示,帧动画在动画过程中即可实时响应,而属性动画按最终状态响应。

使用帧动画实现动画效果

使用如下步骤可以创建一个简单的animator,并且在每个帧回调中打印当前插值。

  1. 引入相关依赖。

    收起
    自动换行
    深色代码主题
    复制
    1. import { AnimatorOptions, AnimatorResult } from '@kit.ArkUI';
  2. 创建执行动画的对象。

    收起
    自动换行
    深色代码主题
    复制
    1. // 创建动画的初始参数
    2. let options: AnimatorOptions = {
    3. duration: 1500,
    4. easing: 'friction',
    5. delay: 0,
    6. fill: 'forwards',
    7. direction: 'normal',
    8. iterations: 2,
    9. // 动画onFrame 插值首帧值
    10. begin: 200.0,
    11. // 动画onFrame 插值尾帧值
    12. end: 400.0
    13. };
    14. let result: AnimatorResult | undefined = this.getUIContext().createAnimator(options);
    15. // 设置接收到帧时回调,动画播放过程中每帧会调用onFrame回调
    16. result.onFrame = (value: number) => {
    17. hilog.info(DOMAIN, TAG, 'current value is :' + value);
    18. }
  3. 播放动画。

    收起
    自动换行
    深色代码主题
    复制
    1. // 播放动画
    2. result.play();
  4. 动画执行完成后手动释放AnimatorResult对象。

    收起
    自动换行
    深色代码主题
    复制
    1. // 释放动画对象
    2. result = undefined;

使用帧动画实现小球抛物运动

  1. 引入相关依赖。

    收起
    自动换行
    深色代码主题
    复制
    1. import { AnimatorOptions, AnimatorResult } from '@kit.ArkUI';
  2. 定义要做动画的组件。

    收起
    自动换行
    深色代码主题
    复制
    1. Button()
    2. .width(60)
    3. .height(60)
    4. .translate({ x: this.translateX, y: this.translateY })
  3. 在onPageShow中创建AnimatorResult对象。

    收起
    自动换行
    深色代码主题
    复制
    1. onPageShow(): void {
    2. // 创建animatorResult对象
    3. this.animatorResult = this.getUIContext().createAnimator(this.animatorOption);
    4. this.animatorResult.onFrame = (progress: number) => {
    5. this.translateX = progress;
    6. if (progress > this.topWidth && this.translateY < this.bottomHeight) {
    7. this.translateY = Math.pow(progress - this.topWidth, 2) * this.g;
    8. }
    9. }
    10. // 动画取消时执行方法
    11. this.animatorResult.onCancel = () => {
    12. // 请将$r('app.string.cancel')替换为实际资源文件,在本示例中该资源文件的value值为"取消"
    13. this.animatorStatus = $r('app.string.cancel');
    14. }
    15. // 动画完成时执行方法
    16. this.animatorResult.onFinish = () => {
    17. // 请将$r('app.string.complete')替换为实际资源文件,在本示例中该资源文件的value值为"完成"
    18. this.animatorStatus = $r('app.string.complete');
    19. }
    20. // 动画重复播放时执行方法
    21. this.animatorResult.onRepeat = () => {
    22. // 'repeat'资源文件中的value值为'动画重复播放'
    23. hilog.info(DOMAIN, TAG, this.manager.getStringByNameSync('repeat'));
    24. }
    25. }
  4. 定义动画播放,重置,暂停的按钮。

    收起
    自动换行
    深色代码主题
    复制
    1. // 请将$r('app.string.play')替换为实际资源文件,在本示例中该资源文件的value值为"播放"
    2. Button($r('app.string.play')).onClick(() => {
    3. this.animatorResult?.play();
    4. // 请将$r('app.string.playing')替换为实际资源文件,在本示例中该资源文件的value值为"播放中"
    5. this.animatorStatus = $r('app.string.playing');
    6. }).width(80).height(35)
    7. // 请将$r('app.string.reset')替换为实际资源文件,在本示例中该资源文件的value值为"重置"
    8. Button($r('app.string.reset')).onClick(() => {
    9. this.translateX = 0;
    10. this.translateY = 0;
    11. }).width(80).height(35)
    12. // 请将$r('app.string.pause')替换为实际资源文件,在本示例中该资源文件的value值为"暂停"
    13. Button($r('app.string.pause')).onClick(() => {
    14. this.animatorResult?.pause();
    15. // 请将$r('app.string.pause')替换为实际资源文件,在本示例中该资源文件的value值为"暂停"
    16. this.animatorStatus = $r('app.string.pause');
    17. }).width(80).height(35)
  5. 在页面隐藏或销毁的生命周期中释放动画对象,避免内存泄漏。

    收起
    自动换行
    深色代码主题
    复制
    1. onPageHide(): void {
    2. this.animatorResult = undefined;
    3. }

完整示例如下。

收起
自动换行
深色代码主题
复制
  1. import { AnimatorOptions, AnimatorResult } from '@kit.ArkUI';
  2. import { common } from '@kit.AbilityKit';
  3. import { hilog } from '@kit.PerformanceAnalysisKit';
  4. const DOMAIN = 0x0000;
  5. const TAG: string = '[AnimatorTest]';
  6. @Entry
  7. @Component
  8. struct Index {
  9. private context = this.getUIContext().getHostContext() as common.UIAbilityContext;
  10. private manager = this.context.resourceManager;
  11. @State animatorResult: AnimatorResult | undefined = undefined;
  12. // 'create'资源文件中的value值为'创建'
  13. @State animatorStatus: string = 'create';
  14. begin: number = 0;
  15. end: number = 300;
  16. topWidth: number = 150;
  17. bottomHeight: number = 100;
  18. // 自由落体运动的加速度系数
  19. g: number = 0.18;
  20. animatorOption: AnimatorOptions = {
  21. duration: 4000,
  22. delay: 0,
  23. easing: 'linear',
  24. iterations: 1,
  25. fill: "forwards",
  26. direction: 'normal',
  27. begin: this.begin,
  28. end: this.end
  29. };
  30. @State translateX: number = 0;
  31. @State translateY: number = 0;
  32. onPageShow(): void {
  33. this.animatorResult = this.getUIContext().createAnimator(this.animatorOption);
  34. this.animatorResult.onFrame = (progress: number) => {
  35. this.translateX = progress;
  36. if (progress > this.topWidth && this.translateY < this.bottomHeight) {
  37. this.translateY = Math.pow(progress - this.topWidth, 2) * this.g;
  38. }
  39. }
  40. this.animatorResult.onCancel = () => {
  41. // 'cancel'资源文件中的value值为'取消'
  42. this.animatorStatus = 'cancel';
  43. }
  44. this.animatorResult.onFinish = () => {
  45. // 'complete'资源文件中的value值为'完成'
  46. this.animatorStatus = 'complete';
  47. }
  48. this.animatorResult.onRepeat = () => {
  49. // 'repeat'资源文件中的value值为'动画重复播放'
  50. hilog.info(DOMAIN, TAG, this.manager.getStringByNameSync('repeat'));
  51. }
  52. }
  53. onPageHide(): void {
  54. this.animatorResult = undefined;
  55. }
  56. build() {
  57. Column() {
  58. Column({ space: 30 }) {
  59. // 请将$r('app.string.play')替换为实际资源文件,在本示例中该资源文件的value值为"播放"
  60. Button($r('app.string.play')).onClick(() => {
  61. this.animatorResult?.play();
  62. // 'playing'资源文件中的value值为'播放中'
  63. this.animatorStatus = 'playing';
  64. }).width(80).height(35)
  65. // 请将$r('app.string.reset')替换为实际资源文件,在本示例中该资源文件的value值为"重置"
  66. Button($r('app.string.reset')).onClick(() => {
  67. this.translateX = 0;
  68. this.translateY = 0;
  69. }).width(80).height(35)
  70. // 请将$r('app.string.pause')替换为实际资源文件,在本示例中该资源文件的value值为"暂停"
  71. Button($r('app.string.pause')).onClick(() => {
  72. this.animatorResult?.pause();
  73. // 'pause'资源文件中的value值为'暂停'
  74. this.animatorStatus = 'pause';
  75. }).width(80).height(35)
  76. }.width('100%').height('25%')
  77. Stack() {
  78. Button()
  79. .width(60)
  80. .height(60)
  81. .translate({ x: this.translateX, y: this.translateY })
  82. }
  83. .width('100%')
  84. .height('45%')
  85. .align(Alignment.Start)
  86. // 'animatorStatus'资源文件中的value值为'当前动画状态为:'
  87. Text(this.manager.getStringByNameSync('animatorStatus') + this.manager.getStringByNameSync(this.animatorStatus))
  88. }.width('100%').height('100%')
  89. }
  90. }

在 指南 中进行搜索
请输入您想要搜索的关键词