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),},];}@StyleschunkStyle() {.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),},];}@StyleschunkStyle() {.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.动画效果如下: