鸿蒙NEXT开发动画案例1

发布于:2025-07-06 ⋅ 阅读:(18) ⋅ 点赞:(0)

1.创建空白项目


2.Page文件夹下面新建Spin.ets文件,代码如下:

/**
 * SpinKit 风格的旋转加载动画组件。
 *
 * @component
 * @param spinSize - 动画容器大小(必须为正数)
 * @param spinColor - 动画颜色(支持资源引用)
 *
 * 示例:
 * ```ets
 * SpinOne({
 *   spinSize: 60,
 *   spinColor: '#FF0000'
 * })
 * ```
 */

// 定义关键帧动画配置接口
interface KeyframeItem {
  duration: number;
  curve: Curve;
  event: () => void;
}

// 动画整体配置接口(已存在)
interface KeyframeAnimationConfig {
  iterations: number;
  delay: number;
}

// 更新动画状态的参数接口
interface AnimationUpdateParams {
  angle?: number;
  x?: number;
  y?: number;
}

@ComponentV2
export struct SpinOne {
  @Require @Param spinSize: number = 48;       // 父组件必须传值,这里默认值不会实际生效
  @Require @Param spinColor: ResourceColor = '#209ED8';

  @Local angle: number = 0;
  @Local x: number = 0;
  @Local y: number = 0;

  private readonly ANIMATION_DURATION: number = 600;

  build() {
    Canvas()
      .width(this.spinSize)
      .height(this.spinSize)
      .backgroundColor(this.spinColor)
      .renderFit(RenderFit.CENTER)
      .shadow(ShadowStyle.OUTER_DEFAULT_XS)
      .rotate({
        perspective: this.spinSize,
        x: this.x,
        y: this.y,
        angle: this.angle
      })
      .onAppear(() => {
        this.startAnimation();
      });
  }

  private startAnimation(): void {
    const uiContext = this.getUIContext();
    if (!uiContext) return;

    const animationConfig: KeyframeAnimationConfig = {
      iterations: -1, // 无限循环
      delay: 0,
    };

    uiContext.keyframeAnimateTo(animationConfig, [
      this.createKeyframe(this.ANIMATION_DURATION, { angle: 180, x: this.spinSize / 2, y: 0 }),
      this.createKeyframe(0, { angle: 0 }),
      this.createKeyframe(this.ANIMATION_DURATION, { angle: 180, x: 0, y: this.spinSize / 2 })
    ]);
  }

  /**
   * 创建关键帧动画配置项
   * @param duration 动画持续时间
   * @param update 动画更新参数
   */
  private createKeyframe(
    duration: number,
    update: AnimationUpdateParams
  ): KeyframeItem {
    return {
      duration,
      curve: Curve.EaseInOut,
      event: () => {
        if (update.angle !== undefined) this.angle = update.angle;
        if (update.x !== undefined) this.x = update.x;
        if (update.y !== undefined) this.y = update.y;
      }
    };
  }
}

代码如下:
/**
 * SpinKit 风格的旋转加载动画组件。
 *
 * @component
 * @param spinSize - 动画容器大小(必须为正数)
 * @param spinColor - 动画颜色(支持资源引用)
 *
 * 示例:
 * ```ets
 * SpinOne({
 *   spinSize: 60,
 *   spinColor: '#FF0000'
 * })
 * ```
 */

// 定义关键帧动画配置接口
interface KeyframeItem {
  duration: number;
  curve: Curve;
  event: () => void;
}

// 动画整体配置接口(已存在)
interface KeyframeAnimationConfig {
  iterations: number;
  delay: number;
}

// 更新动画状态的参数接口
interface AnimationUpdateParams {
  angle?: number;
  x?: number;
  y?: number;
}

@ComponentV2
export struct SpinOne {
  @Require @Param spinSize: number = 48;       // 父组件必须传值,这里默认值不会实际生效
  @Require @Param spinColor: ResourceColor = '#209ED8';

  @Local angle: number = 0;
  @Local x: number = 0;
  @Local y: number = 0;

  private readonly ANIMATION_DURATION: number = 600;

  build() {
    Canvas()
      .width(this.spinSize)
      .height(this.spinSize)
      .backgroundColor(this.spinColor)
      .renderFit(RenderFit.CENTER)
      .shadow(ShadowStyle.OUTER_DEFAULT_XS)
      .rotate({
        perspective: this.spinSize,
        x: this.x,
        y: this.y,
        angle: this.angle
      })
      .onAppear(() => {
        this.startAnimation();
      });
  }

  private startAnimation(): void {
    const uiContext = this.getUIContext();
    if (!uiContext) return;

    const animationConfig: KeyframeAnimationConfig = {
      iterations: -1, // 无限循环
      delay: 0,
    };

    uiContext.keyframeAnimateTo(animationConfig, [
      this.createKeyframe(this.ANIMATION_DURATION, { angle: 180, x: this.spinSize / 2, y: 0 }),
      this.createKeyframe(0, { angle: 0 }),
      this.createKeyframe(this.ANIMATION_DURATION, { angle: 180, x: 0, y: this.spinSize / 2 })
    ]);
  }

  /**
   * 创建关键帧动画配置项
   * @param duration 动画持续时间
   * @param update 动画更新参数
   */
  private createKeyframe(
    duration: number,
    update: AnimationUpdateParams
  ): KeyframeItem {
    return {
      duration,
      curve: Curve.EaseInOut,
      event: () => {
        if (update.angle !== undefined) this.angle = update.angle;
        if (update.x !== undefined) this.x = update.x;
        if (update.y !== undefined) this.y = update.y;
      }
    };
  }
}

3.修改Index.ets文件,代码如下:
 

import { SpinOne } from './Spin';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Column() {
      SpinOne({
        spinSize: 60,
        spinColor: '#FF0000'
      })
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}
代码如下:

import { SpinOne } from './Spin';

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Column() {
      SpinOne({
        spinSize: 60,
        spinColor: '#FF0000'
      })
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}

4.运行项目,登录华为账号,需进行签名

5.动画效果如下:


网站公告

今日签到

点亮在社区的每一天
去签到