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

相关推荐
二流小码农1 小时前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos
鹏程十八少2 小时前
4.Android 30分钟手写一个简单版shadow, 从零理解shadow插件化零反射插件化原理
android·前端·面试
Kapaseker2 小时前
一杯美式搞定 Kotlin 空安全
android·kotlin
三少爷的鞋2 小时前
Android 协程时代,Handler 应该退休了吗?
android
万少11 小时前
HarmonyOS 开发必会 5 种 Builder 详解
前端·harmonyos
火柴就是我16 小时前
让我们实现一个更好看的内部阴影按钮
android·flutter
开心就好202517 小时前
UniApp开发应用多平台上架全流程:H5小程序iOS和Android
后端·ios
开心就好202520 小时前
免 Xcode 的 iOS 开发新选择?聊聊一款更轻量的 iOS 开发 IDE kxapp 快蝎
后端·ios
砖厂小工1 天前
用 GLM + OpenClaw 打造你的 AI PR Review Agent — 让龙虾帮你审代码
android·github
恋猫de小郭1 天前
Apple 的 ANE 被挖掘,AI 硬件公开,宣传的 38 TOPS 居然是"数字游戏"?
前端·人工智能·ios