鸿蒙NEXT开发动画案例9

1.创建空白项目

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

复制代码
interface GridItemConfig {
  translate:Translate
}

interface Translate {
    x?: number;
    y?: number;
}

/**
 * TODO SpinKit动画组件(重构版)
 * author: 鸿蒙布道师
 * since: 2025/05/15
 */
@ComponentV2
export struct SpinNine {
  @Require @Param spinSize: number = 36;
  @Require @Param spinColor: ResourceColor;

  // 动画相关状态
  @Local scaleSize: number = this.spinSize * 0.75;
  @Local tran1: number = 0;
  private oldSize: number = this.spinSize;

  aboutToAppear(): void {
    this.oldSize = this.spinSize;
    this.scaleSize = this.spinSize * 0.75;
  }

  build() {
    Stack() {
      Grid() {
        ForEach(this.getGridItems(), (item:GridItemConfig, index) => {
          GridItem() {
            Canvas()
              .chunkStyle()
          }
          .translate(item.translate)
        });
      }
      .rowsTemplate('1fr 1fr 1fr')
      .columnsTemplate('1fr 1fr 1fr')
      .width(this.scaleSize)
      .height(this.scaleSize)
    }
    .width(this.oldSize)
    .height(this.oldSize)
    .alignContent(Alignment.Center)
    .onAppear(() => {
      this.startAnimation();
    })
  }

  // 获取 GridItem 配置数据
  private getGridItems():GridItemConfig[] {
    return [
      { translate: { x: this.tran1 } },
      { translate: { x: this.tran1 } },
      { translate: { y: this.tran1 } },
      { translate: { y: -this.tran1 } },
      { translate: {} }, // 中心无位移
      { translate: { y: this.tran1 } },
      { translate: { y: -this.tran1 } },
      { translate: { x: -this.tran1 } },
      { translate: { x: -this.tran1 } },
    ];
  }

  // 启动动画
  private startAnimation() {
    this.getUIContext().keyframeAnimateTo({ iterations: -1, delay: 0 }, [
      {
        duration: 600,
        curve: Curve.EaseInOut,
        event: () => {
          this.tran1 = 0;
          this.scaleSize = this.oldSize * 1.1;
        }
      },
      {
        duration: 600,
        curve: Curve.EaseIn,
        event: () => {
          this.scaleSize = this.oldSize * 0.75;
          this.tran1 = 0;
        }
      }
    ]);
  }

  @Styles
  chunkStyle(){
    .width(this.oldSize * 0.25)
    .height(this.oldSize * 0.25)
    .backgroundColor(this.spinColor)
    .shadow(ShadowStyle.OUTER_DEFAULT_XS)
  }
}
复制代码
代码如下:
TypeScript 复制代码
interface GridItemConfig {
  translate:Translate
}

interface Translate {
    x?: number;
    y?: number;
}

/**
 * TODO SpinKit动画组件(重构版)
 * author: 鸿蒙布道师
 * since: 2025/05/15
 */
@ComponentV2
export struct SpinNine {
  @Require @Param spinSize: number = 36;
  @Require @Param spinColor: ResourceColor;

  // 动画相关状态
  @Local scaleSize: number = this.spinSize * 0.75;
  @Local tran1: number = 0;
  private oldSize: number = this.spinSize;

  aboutToAppear(): void {
    this.oldSize = this.spinSize;
    this.scaleSize = this.spinSize * 0.75;
  }

  build() {
    Stack() {
      Grid() {
        ForEach(this.getGridItems(), (item:GridItemConfig, index) => {
          GridItem() {
            Canvas()
              .chunkStyle()
          }
          .translate(item.translate)
        });
      }
      .rowsTemplate('1fr 1fr 1fr')
      .columnsTemplate('1fr 1fr 1fr')
      .width(this.scaleSize)
      .height(this.scaleSize)
    }
    .width(this.oldSize)
    .height(this.oldSize)
    .alignContent(Alignment.Center)
    .onAppear(() => {
      this.startAnimation();
    })
  }

  // 获取 GridItem 配置数据
  private getGridItems():GridItemConfig[] {
    return [
      { translate: { x: this.tran1 } },
      { translate: { x: this.tran1 } },
      { translate: { y: this.tran1 } },
      { translate: { y: -this.tran1 } },
      { translate: {} }, // 中心无位移
      { translate: { y: this.tran1 } },
      { translate: { y: -this.tran1 } },
      { translate: { x: -this.tran1 } },
      { translate: { x: -this.tran1 } },
    ];
  }

  // 启动动画
  private startAnimation() {
    this.getUIContext().keyframeAnimateTo({ iterations: -1, delay: 0 }, [
      {
        duration: 600,
        curve: Curve.EaseInOut,
        event: () => {
          this.tran1 = 0;
          this.scaleSize = this.oldSize * 1.1;
        }
      },
      {
        duration: 600,
        curve: Curve.EaseIn,
        event: () => {
          this.scaleSize = this.oldSize * 0.75;
          this.tran1 = 0;
        }
      }
    ]);
  }

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

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

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

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

  build() {
    Column() {
      SpinNine({
        spinSize: 60,
        spinColor: '#FF0000'
      })
    }
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)
    .height('100%')
    .width('100%')
  }
}
复制代码
代码如下:
TypeScript 复制代码
import { SpinEight } from './Spin';

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

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

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

5.动画效果如下:

相关推荐
2601_949593651 小时前
高级进阶React Native 鸿蒙跨平台开发:LinearGradient 背景渐变与主题切换
react native·react.js·harmonyos
深海呐1 小时前
鸿蒙基本UI控件(List相关-含Grid)
harmonyos·harmonyos ui·harmonyos list·harmonyos grid·鸿蒙列表view·art列表ui控件·art网格ui控件
小雨青年1 小时前
鸿蒙 HarmonyOS 6 | AI Kit 集成 Core Speech Kit 语音服务
人工智能·华为·harmonyos
优雅的潮叭2 小时前
cud编程之 reduce
android·redis·缓存
2601_949613022 小时前
flutter_for_openharmony家庭药箱管理app实战+用药知识详情实现
android·javascript·flutter
一起养小猫2 小时前
Flutter for OpenHarmony 实战 表单处理与验证完整指南
android·开发语言·前端·javascript·flutter·harmonyos
2601_949975083 小时前
flutter_for_openharmony城市井盖地图app实战+附近井盖实现
android·flutter
倾云鹤3 小时前
通用Digest认证
android·digest
摘星编程3 小时前
React Native鸿蒙版:自定义useMask输入掩码
react native·react.js·harmonyos
mocoding3 小时前
使用Flutter设置UI三方库card_settings_ui重构鸿蒙版天气预报我的页面
flutter·ui·harmonyos