鸿蒙NEXT开发动画案例3

1.创建空白项目

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

复制代码
// ===== 接口定义(必须放在使用前)=====
interface KeyframeAnimationConfig {
  iterations: number;
  delay: number;
}

interface KeyframeState {
  duration: number;
  curve?: Curve;
  event: () => void;
}
// ===== 接口定义结束 =====


/**
 * TODO SpinKit动画组件
 * author: 鸿蒙布道师
 * since: 2025/05/07
 */
@ComponentV2
export struct SpinThree {
  // 参数定义
  @Require @Param spinSize: number = 48;
  @Require @Param spinColor: ResourceColor = '#209ED8';

  // 动画状态
  @Local scale1: number = 0.4;
  @Local scale2: number = 0.4;
  @Local scale3: number = 0.4;
  @Local scale4: number = 0.4;
  @Local scale5: number = 0.4;

  build() {
    Row() {
      Canvas()
        .chunkStyle()
        .scale({ y: this.scale1 })

      Canvas()
        .chunkStyle()
        .scale({ y: this.scale2 })

      Canvas()
        .chunkStyle()
        .scale({ y: this.scale3 })

      Canvas()
        .chunkStyle()
        .scale({ y: this.scale4 })

      Canvas()
        .chunkStyle()
        .scale({ y: this.scale5 })
    }
    .width(this.spinSize)
    .height(this.spinSize * 0.8)
    .onAppear(() => {
      this.startAnimations();
    });
  }

  /**
   * 启动所有子动画
   */
  private startAnimations(): void {
    const uiContext = this.getUIContext();
    if (!uiContext) return;

    const ANIMATION_DELAY_INTERVAL = 100; // 每个动画延迟间隔

    for (let i = 1; i <= 5; i++) {
      const keyframes = this.createKeyframes(i);
      const delay = -1100 + (i - 1) * ANIMATION_DELAY_INTERVAL;

      uiContext.keyframeAnimateTo(
        { iterations: -1, delay },
        keyframes
      );
    }
  }

  /**
   * 根据索引创建对应的关键帧动画
   * @param index - 第几个动画(1~5)
   */
  private createKeyframes(index: number): Array<KeyframeState> {
    const updateScale = (value: number) => {
      switch (index) {
        case 1: this.scale1 = value; break;
        case 2: this.scale2 = value; break;
        case 3: this.scale3 = value; break;
        case 4: this.scale4 = value; break;
        case 5: this.scale5 = value; break;
      }
    };

    return [
      {
        duration: 240,
        curve: Curve.EaseInOut,
        event: ():void => updateScale(1),
      },
      {
        duration: 240,
        curve: Curve.EaseInOut,
        event: ():void => updateScale(0.4),
      },
      {
        duration: 720,
        event: () => {},
      }
    ];
  }

  /**
   * 公共样式封装
   */
  @Styles
  chunkStyle() {
    .height('100%')
    .width('14%')
    .margin({ left: '3%', right: '3%' })
    .backgroundColor(this.spinColor)
    .shadow(ShadowStyle.OUTER_DEFAULT_XS)
  }
}
TypeScript 复制代码
// ===== 接口定义(必须放在使用前)=====
interface KeyframeAnimationConfig {
  iterations: number;
  delay: number;
}

interface KeyframeState {
  duration: number;
  curve?: Curve;
  event: () => void;
}
// ===== 接口定义结束 =====


/**
 * TODO SpinKit动画组件
 * author: 鸿蒙布道师
 * since: 2025/05/07
 */
@ComponentV2
export struct SpinThree {
  // 参数定义
  @Require @Param spinSize: number = 48;
  @Require @Param spinColor: ResourceColor = '#209ED8';

  // 动画状态
  @Local scale1: number = 0.4;
  @Local scale2: number = 0.4;
  @Local scale3: number = 0.4;
  @Local scale4: number = 0.4;
  @Local scale5: number = 0.4;

  build() {
    Row() {
      Canvas()
        .chunkStyle()
        .scale({ y: this.scale1 })

      Canvas()
        .chunkStyle()
        .scale({ y: this.scale2 })

      Canvas()
        .chunkStyle()
        .scale({ y: this.scale3 })

      Canvas()
        .chunkStyle()
        .scale({ y: this.scale4 })

      Canvas()
        .chunkStyle()
        .scale({ y: this.scale5 })
    }
    .width(this.spinSize)
    .height(this.spinSize * 0.8)
    .onAppear(() => {
      this.startAnimations();
    });
  }

  /**
   * 启动所有子动画
   */
  private startAnimations(): void {
    const uiContext = this.getUIContext();
    if (!uiContext) return;

    const ANIMATION_DELAY_INTERVAL = 100; // 每个动画延迟间隔

    for (let i = 1; i <= 5; i++) {
      const keyframes = this.createKeyframes(i);
      const delay = -1100 + (i - 1) * ANIMATION_DELAY_INTERVAL;

      uiContext.keyframeAnimateTo(
        { iterations: -1, delay },
        keyframes
      );
    }
  }

  /**
   * 根据索引创建对应的关键帧动画
   * @param index - 第几个动画(1~5)
   */
  private createKeyframes(index: number): Array<KeyframeState> {
    const updateScale = (value: number) => {
      switch (index) {
        case 1: this.scale1 = value; break;
        case 2: this.scale2 = value; break;
        case 3: this.scale3 = value; break;
        case 4: this.scale4 = value; break;
        case 5: this.scale5 = value; break;
      }
    };

    return [
      {
        duration: 240,
        curve: Curve.EaseInOut,
        event: ():void => updateScale(1),
      },
      {
        duration: 240,
        curve: Curve.EaseInOut,
        event: ():void => updateScale(0.4),
      },
      {
        duration: 720,
        event: () => {},
      }
    ];
  }

  /**
   * 公共样式封装
   */
  @Styles
  chunkStyle() {
    .height('100%')
    .width('14%')
    .margin({ left: '3%', right: '3%' })
    .backgroundColor(this.spinColor)
    .shadow(ShadowStyle.OUTER_DEFAULT_XS)
  }
}

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

复制代码
import { SpinThree } from './Spin';

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

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

代码如下:

TypeScript 复制代码
import { SpinThree } from './Spin';

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

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

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

5.动画效果如下:

相关推荐
HarmonyOS_SDK1 小时前
借助AR Engine人脸识别与跟踪能力,直播不露脸也生动
harmonyos
寰宇星城2 小时前
AppActuator - 让 AI Agent 直接理解并操作运行中的 Android App
android
波罗丁牌2 小时前
uni-app 实现视频聊天、屏幕分享,支持Android、HarmonyOS、iOS
android·uni-app·音视频
峥嵘life2 小时前
Android WiFi 热点 Channel 信道 和 Frequency 频率 转换总结
android·开发语言
jieyucx2 小时前
【高级利用】条件竞争与逻辑漏洞:与服务器赛跑的艺术
android·运维·服务器·web安全·文件上传
程序员黑豆3 小时前
鸿蒙应用开发 @BuilderParam 使用教程:实现灵活的 UI 插槽
前端·harmonyos
HMS Core4 小时前
基于人体骨骼点识别与跟踪,实现低时延体感游戏
游戏·华为·harmonyos
XR1234567884 小时前
政府办公楼网络搭建方案对比:信锐、华为、华三选型指南
网络·华为
叶辞树5 小时前
死锁问题分析
android
程序员黑豆5 小时前
鸿蒙应用开发 @Builder 使用教程:从入门到精通
前端·harmonyos