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