import { _decorator, Node, Tween, UIOpacity, Vec3, tween } from 'cc'; import { BaseView } from './BaseView'; const { ccclass, property } = _decorator; @ccclass('BaseModal') export class BaseModal extends BaseView { @property([Node]) protected animationNodes: Node[] = []; @property(Node) protected backdropNode: Node | null = null; @property protected openAnimationEnabled: boolean = true; @property protected openAnimationDuration: number = 0.36; private readonly _originalScales: Map = new Map(); onViewShow(): void { this.playOpenAnimation(); } onViewHide(): void { this.stopOpenAnimation(); } protected playOpenAnimation(): void { if (!this.openAnimationEnabled) { return; } this.playBackdropFadeIn(); const targets = this.getAnimationTargets(); targets.forEach((target, index) => { this.playBounceIn(target, index * 0.035); }); } protected stopOpenAnimation(): void { this.getAnimationTargets().forEach((target) => { Tween.stopAllByTarget(target); }); const opacity = this.backdropNode?.getComponent(UIOpacity); if (opacity) { Tween.stopAllByTarget(opacity); } } private getAnimationTargets(): Node[] { const configuredTargets = this.animationNodes.filter((node) => node?.isValid); return configuredTargets.length > 0 ? configuredTargets : [this.node]; } private getOriginalScale(target: Node): Vec3 { const cachedScale = this._originalScales.get(target); if (cachedScale) { return cachedScale; } const originalScale = target.scale.clone(); this._originalScales.set(target, originalScale); return originalScale; } private playBounceIn(target: Node, delay: number): void { const originalScale = this.getOriginalScale(target); const startScale = this.multiplyScale(originalScale, 0.82); const peakScale = this.multiplyScale(originalScale, 1.045); Tween.stopAllByTarget(target); target.setScale(startScale); tween(target) .delay(delay) .to(this.openAnimationDuration * 0.58, { scale: peakScale }, { easing: 'backOut' }) .to(this.openAnimationDuration * 0.24, { scale: this.multiplyScale(originalScale, 0.985) }, { easing: 'sineOut' }) .to(this.openAnimationDuration * 0.18, { scale: originalScale }, { easing: 'sineOut' }) .start(); } private playBackdropFadeIn(): void { if (!this.backdropNode?.isValid) { return; } const opacity = this.backdropNode.getComponent(UIOpacity) ?? this.backdropNode.addComponent(UIOpacity); Tween.stopAllByTarget(opacity); opacity.opacity = 0; tween(opacity) .to(0.18, { opacity: 255 }, { easing: 'sineOut' }) .start(); } private multiplyScale(scale: Vec3, factor: number): Vec3 { return new Vec3(scale.x * factor, scale.y * factor, scale.z); } }