鸿蒙NEXT开发动画案例4

发布于:2025-05-13 ⋅ 阅读:(17) ⋅ 点赞:(0)

 1.创建空白项目


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

/**
 * TODO SpinKit动画组件 - 双粒子旋转缩放动画
 * author: CSDN-鸿蒙布道师
 * since: 2025/05/08
 */
@ComponentV2
export struct SpinFour {
  // 参数定义
  @Require @Param spinSize: number = 36;
  @Require @Param spinColor: ResourceColor = '#209ED8';

  // 局部状态
  @Local x1: number = 0;
  @Local y1: number = 0;
  @Local scale1: number = 1;
  @Local angle1: number = 0;

  @Local x2: number = this.spinSize * 0.65;
  @Local y2: number = this.spinSize * 0.65;
  @Local scale2: number = 1;
  @Local angle2: number = 0;

  aboutToAppear(): void {
    this.x2 = this.spinSize * 0.65;
    this.y2 = this.spinSize * 0.65;
  }

  build() {
    RelativeContainer() {
      Canvas()
        .chunkStyle()
        .translate({ x: this.x1, y: this.y1 })
        .scale({ x: this.scale1, y: this.scale1 })
        .rotate({ angle: this.angle1 })

      Canvas()
        .chunkStyle()
        .translate({ x: this.x2, y: this.y2 })
        .scale({ x: this.scale2, y: this.scale2 })
        .rotate({ angle: this.angle2 })
    }
    .width(this.spinSize)
    .height(this.spinSize)
    .onAppear(() => {
      this.startAnimation();
    });
  }

  /**
   * 启动无限循环的关键帧动画
   */
  private startAnimation(): void {
    const uiContext = this.getUIContext();
    if (!uiContext) return;

    const keyframes1 = this.createKeyframes(1);
    const keyframes2 = this.createKeyframes(2);

    uiContext.keyframeAnimateTo({ iterations: -1, delay: 0 }, keyframes1);
    uiContext.keyframeAnimateTo({ iterations: -1, delay: 0 }, keyframes2);
  }

  /**
   * 根据粒子编号创建对应的关键帧动画
   * @param particleIndex 粒子索引(1 或 2)
   */
  private createKeyframes(particleIndex: 1 | 2): Array<KeyframeState> {
    const updatePositionAndScale = (step: number): void => {
      if (particleIndex === 1) {
        switch (step) {
          case 1:
            this.scale1 = 0.5;
            this.angle1 = -90;
            this.x1 = this.spinSize * 0.65;
            break;
          case 2:
            this.scale1 = 1;
            this.angle1 = -180;
            this.x1 = this.spinSize * 0.65;
            this.y1 = this.spinSize * 0.65;
            break;
          case 3:
            this.scale1 = 0.5;
            this.angle1 = -270;
            this.x1 = 0;
            this.y1 = this.spinSize * 0.65;
            break;
          case 4:
            this.scale1 = 1;
            this.angle1 = -360;
            this.x1 = 0;
            this.y1 = 0;
            break;
        }
      } else {
        switch (step) {
          case 1:
            this.scale2 = 0.5;
            this.angle2 = -90;
            this.x2 = 0;
            this.y2 = this.spinSize * 0.65;
            break;
          case 2:
            this.scale2 = 1;
            this.angle2 = -180;
            this.x2 = 0;
            this.y2 = 0;
            break;
          case 3:
            this.scale2 = 0.5;
            this.angle2 = -270;
            this.x2 = this.spinSize * 0.65;
            break;
          case 4:
            this.scale2 = 1;
            this.angle2 = -360;
            this.x2 = this.spinSize * 0.65;
            this.y2 = this.spinSize * 0.65;
            break;
        }
      }
    };

    return [
      {
        duration: 500,
        curve: Curve.EaseInOut,
        event: (): void => updatePositionAndScale(1),
      },
      {
        duration: 500,
        curve: Curve.EaseInOut,
        event: (): void => updatePositionAndScale(2),
      },
      {
        duration: 500,
        curve: Curve.EaseInOut,
        event: (): void => updatePositionAndScale(3),
      },
      {
        duration: 500,
        curve: Curve.EaseInOut,
        event: (): void => updatePositionAndScale(4),
      },
    ];
  }

  @Styles
  chunkStyle() {
    .height(this.spinSize * 0.35)
    .width(this.spinSize * 0.35)
    .backgroundColor(this.spinColor)
    .shadow(ShadowStyle.OUTER_DEFAULT_XS)
  }
}
代码如下:
/**
 * TODO SpinKit动画组件 - 双粒子旋转缩放动画
 * author: CSDN-鸿蒙布道师
 * since: 2025/05/08
 */
@ComponentV2
export struct SpinFour {
  // 参数定义
  @Require @Param spinSize: number = 36;
  @Require @Param spinColor: ResourceColor = '#209ED8';

  // 局部状态
  @Local x1: number = 0;
  @Local y1: number = 0;
  @Local scale1: number = 1;
  @Local angle1: number = 0;

  @Local x2: number = this.spinSize * 0.65;
  @Local y2: number = this.spinSize * 0.65;
  @Local scale2: number = 1;
  @Local angle2: number = 0;

  aboutToAppear(): void {
    this.x2 = this.spinSize * 0.65;
    this.y2 = this.spinSize * 0.65;
  }

  build() {
    RelativeContainer() {
      Canvas()
        .chunkStyle()
        .translate({ x: this.x1, y: this.y1 })
        .scale({ x: this.scale1, y: this.scale1 })
        .rotate({ angle: this.angle1 })

      Canvas()
        .chunkStyle()
        .translate({ x: this.x2, y: this.y2 })
        .scale({ x: this.scale2, y: this.scale2 })
        .rotate({ angle: this.angle2 })
    }
    .width(this.spinSize)
    .height(this.spinSize)
    .onAppear(() => {
      this.startAnimation();
    });
  }

  /**
   * 启动无限循环的关键帧动画
   */
  private startAnimation(): void {
    const uiContext = this.getUIContext();
    if (!uiContext) return;

    const keyframes1 = this.createKeyframes(1);
    const keyframes2 = this.createKeyframes(2);

    uiContext.keyframeAnimateTo({ iterations: -1, delay: 0 }, keyframes1);
    uiContext.keyframeAnimateTo({ iterations: -1, delay: 0 }, keyframes2);
  }

  /**
   * 根据粒子编号创建对应的关键帧动画
   * @param particleIndex 粒子索引(1 或 2)
   */
  private createKeyframes(particleIndex: 1 | 2): Array<KeyframeState> {
    const updatePositionAndScale = (step: number): void => {
      if (particleIndex === 1) {
        switch (step) {
          case 1:
            this.scale1 = 0.5;
            this.angle1 = -90;
            this.x1 = this.spinSize * 0.65;
            break;
          case 2:
            this.scale1 = 1;
            this.angle1 = -180;
            this.x1 = this.spinSize * 0.65;
            this.y1 = this.spinSize * 0.65;
            break;
          case 3:
            this.scale1 = 0.5;
            this.angle1 = -270;
            this.x1 = 0;
            this.y1 = this.spinSize * 0.65;
            break;
          case 4:
            this.scale1 = 1;
            this.angle1 = -360;
            this.x1 = 0;
            this.y1 = 0;
            break;
        }
      } else {
        switch (step) {
          case 1:
            this.scale2 = 0.5;
            this.angle2 = -90;
            this.x2 = 0;
            this.y2 = this.spinSize * 0.65;
            break;
          case 2:
            this.scale2 = 1;
            this.angle2 = -180;
            this.x2 = 0;
            this.y2 = 0;
            break;
          case 3:
            this.scale2 = 0.5;
            this.angle2 = -270;
            this.x2 = this.spinSize * 0.65;
            break;
          case 4:
            this.scale2 = 1;
            this.angle2 = -360;
            this.x2 = this.spinSize * 0.65;
            this.y2 = this.spinSize * 0.65;
            break;
        }
      }
    };

    return [
      {
        duration: 500,
        curve: Curve.EaseInOut,
        event: (): void => updatePositionAndScale(1),
      },
      {
        duration: 500,
        curve: Curve.EaseInOut,
        event: (): void => updatePositionAndScale(2),
      },
      {
        duration: 500,
        curve: Curve.EaseInOut,
        event: (): void => updatePositionAndScale(3),
      },
      {
        duration: 500,
        curve: Curve.EaseInOut,
        event: (): void => updatePositionAndScale(4),
      },
    ];
  }

  @Styles
  chunkStyle() {
    .height(this.spinSize * 0.35)
    .width(this.spinSize * 0.35)
    .backgroundColor(this.spinColor)
    .shadow(ShadowStyle.OUTER_DEFAULT_XS)
  }
}

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

import { SpinFour } from './Spin';

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

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

代码如下:
import { SpinFour } from './Spin';

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

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

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

5.动画效果如下:
 


网站公告

今日签到

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