You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
AdLike2048/assets/mx/components/tween/TweenBreathe.ts

119 lines
3.2 KiB

import { _decorator, Component, Vec3, v3, UIOpacity } from 'cc';
const { ccclass, property, executeInEditMode } = _decorator;
/**
* 呼吸动画(缩放 + 透明度)
* 在 update 中按 speed 做 min↔max 往复插值,周期 = 1 / speed 秒。
*/
@ccclass('TweenBreathe')
@executeInEditMode(true)
export class TweenBreathe extends Component {
@property
speed = 1;
@property
set playing(value: boolean) {
this._playing = value;
if (value) {
this.play();
} else {
this.stop();
}
}
get playing(): boolean {
return this._playing;
}
private _playing: boolean = true;
@property
useScale = true;
@property
defaultScale: Vec3 = v3(1, 1, 1);
@property
minScale: Vec3 = v3(0.9, 0.9, 0.9);
@property
maxScale: Vec3 = v3(1.1, 1.1, 1.1);
@property
useOpacity = true;
@property
defaultOpacity = 255;
@property
opacity = 255;
@property
minOpacity = 100;
@property({ tooltip: '几秒钟后暂停,-1 表示不暂停' })
timePause = -1;
private _elapsed = 0;
private uiOpacity: UIOpacity = null;
protected onLoad(): void {
this.uiOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
}
protected onEnable(): void {
this.play();
}
protected onDisable(): void {
this.stop();
}
protected update(dt: number): void {
if (!this._playing) return;
this._elapsed += dt;
if (this.timePause > 0 && this._elapsed >= this.timePause) {
this.stop();
return;
}
const factor = this._calcFactor(this._elapsed);
if (this.useScale) {
this.node.setScale(
this.defaultScale.x * this._lerp(this.minScale.x, this.maxScale.x, factor),
this.defaultScale.y * this._lerp(this.minScale.y, this.maxScale.y, factor),
this.defaultScale.z * this._lerp(this.minScale.z, this.maxScale.z, factor),
);
}
if (this.useOpacity) {
const opacity = this.bindOpacity();
if (opacity) opacity.opacity = this._lerp(this.minOpacity, this.opacity, factor);
}
}
play(): void {
this._elapsed = 0;
this._playing = true;
}
stop(): void {
this._playing = false;
this.unscheduleAllCallbacks();
if (this.node?.isValid) this.node.setScale(this.defaultScale);
const opacity = this.bindOpacity();
if (opacity) opacity.opacity = this.defaultOpacity;
}
/** 父节点 onLoad 可能早于本组件,stop/update 须自行取 UIOpacity */
private bindOpacity(): UIOpacity | null {
if (this.uiOpacity) return this.uiOpacity;
if (!this.node?.isValid) return null;
this.uiOpacity = this.node.getComponent(UIOpacity) || this.node.addComponent(UIOpacity);
return this.uiOpacity;
}
/** 0→1→0 往复,完整周期 1/speed 秒 */
private _calcFactor(elapsed: number): number {
const angle = elapsed * this.speed * Math.PI * 2;
return (1 - Math.cos(angle)) * 0.5;
}
private _lerp(min: number, max: number, t: number): number {
return min + (max - min) * t;
}
}