鸿蒙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.动画效果如下:

相关推荐
BoomHe2 小时前
Now in Android 架构模式全面分析
android·android jetpack
Hcourage4 小时前
鸿蒙工程获取C/C++代码覆盖
harmonyos
ssshooter6 小时前
Tauri 踩坑 appLink 修改后闪退
前端·ios·rust
二流小码农10 小时前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少10 小时前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker10 小时前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋11 小时前
Android 协程时代,Handler 应该退休了吗?
android
万少20 小时前
HarmonyOS 开发必会 5 种 Builder 详解
前端·harmonyos
火柴就是我1 天前
让我们实现一个更好看的内部阴影按钮
android·flutter
开心就好20251 天前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios