perf: 优化通关弹窗
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { _decorator, Node, Label, AudioClip, AudioSource, view, UITransform, Size, ProgressBar } from 'cc';
|
||||
import { _decorator, Node, Label, AudioClip, AudioSource, view, UITransform, Size, ProgressBar, tween, Tween } from 'cc';
|
||||
import { BaseModal } from 'db://assets/scripts/core/BaseModal';
|
||||
import { WxSDK } from 'db://assets/scripts/utils/WxSDK';
|
||||
const { ccclass, property } = _decorator;
|
||||
@@ -22,6 +22,12 @@ export interface PassModalTitleInfo {
|
||||
interface PassModalParams {
|
||||
levelIndex?: number;
|
||||
titleInfo?: PassModalTitleInfo;
|
||||
/**
|
||||
* 通关前的称号信息。传入后,本次显示会把进度条从该起点动画到 titleInfo 的终点;
|
||||
* 起点与终点 titleText 不同则分两段(先填满当前等级,再切换到新等级后填到目标进度)。
|
||||
* 分享模式等无本地进度变化的场景不要传。
|
||||
*/
|
||||
previousTitleInfo?: PassModalTitleInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,22 +63,40 @@ export class PassModal extends BaseModal {
|
||||
@property(AudioClip)
|
||||
successAudio: AudioClip | null = null;
|
||||
|
||||
/** 进度条动画起始前的等待时长(秒),等弹窗开场动画稳定后再开始 */
|
||||
private static readonly PROGRESS_ANIM_START_DELAY = 0.4;
|
||||
/** 单段进度条填充动画时长(秒) */
|
||||
private static readonly PROGRESS_ANIM_SEGMENT_DURATION = 0.6;
|
||||
/** 跨称号切换时的等级信息刷新停顿(秒),让玩家看清称号变更 */
|
||||
private static readonly PROGRESS_ANIM_LEVELUP_PAUSE = 0.12;
|
||||
|
||||
/** 回调函数 */
|
||||
private _callbacks: PassModalCallbacks = {};
|
||||
|
||||
/** 缓存的屏幕尺寸 */
|
||||
private _screenSize: Size | null = null;
|
||||
|
||||
/** 称号展示数据 */
|
||||
/** 称号展示数据(终态) */
|
||||
private _titleInfo: PassModalTitleInfo = {
|
||||
titleText: '冷场小白1级',
|
||||
nextTitleProgress: 0,
|
||||
progressText: '还差3题获得冷场小白2级'
|
||||
};
|
||||
|
||||
/** 动画起点。为 null 表示不做进度动画,直接展示终态 */
|
||||
private _previousTitleInfo: PassModalTitleInfo | null = null;
|
||||
|
||||
/** 进度动画所绑定的对象,用于 Tween.stopAllByTarget */
|
||||
private readonly _progressTweenTarget: { progress: number } = { progress: 0 };
|
||||
|
||||
setParams(params: PassModalParams): void {
|
||||
super.setParams(params);
|
||||
|
||||
// previousTitleInfo 可以显式传 null 来禁用动画;undefined 表示"保持已有状态"
|
||||
if (params && 'previousTitleInfo' in params) {
|
||||
this._previousTitleInfo = params.previousTitleInfo ?? null;
|
||||
}
|
||||
|
||||
if (params?.titleInfo) {
|
||||
this.setTitleInfo(params.titleInfo);
|
||||
}
|
||||
@@ -93,7 +117,7 @@ export class PassModal extends BaseModal {
|
||||
...this._titleInfo,
|
||||
...titleInfo
|
||||
};
|
||||
this._updateTitleInfo();
|
||||
this._refreshTitleView();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,14 +134,24 @@ export class PassModal extends BaseModal {
|
||||
onViewShow(): void {
|
||||
super.onViewShow();
|
||||
this._updateWidget();
|
||||
this._updateTitleInfo();
|
||||
this._refreshTitleView();
|
||||
this._playSuccessSound();
|
||||
this._playProgressAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面隐藏时调用
|
||||
*/
|
||||
onViewHide(): void {
|
||||
super.onViewHide();
|
||||
this._stopProgressAnimation();
|
||||
}
|
||||
|
||||
/**
|
||||
* 页面销毁时调用
|
||||
*/
|
||||
onViewDestroy(): void {
|
||||
this._stopProgressAnimation();
|
||||
this._unbindButtonEvents();
|
||||
}
|
||||
|
||||
@@ -177,20 +211,151 @@ export class PassModal extends BaseModal {
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新称号体系核心变量
|
||||
* 用当前 _titleInfo 刷新视图(称号、进度条、进度文案)
|
||||
* 进度条动画运行时,会由动画控制进度值,这里仍然把进度写为终态
|
||||
* —— _playProgressAnimation 会在动画开始前覆盖为起点。
|
||||
*/
|
||||
private _updateTitleInfo(): void {
|
||||
if (this.titleLevelLabel && this._titleInfo.titleText !== undefined) {
|
||||
this.titleLevelLabel.string = this._titleInfo.titleText;
|
||||
private _refreshTitleView(): void {
|
||||
this._applyTitleText(this._titleInfo.titleText);
|
||||
this._applyProgressText(this._titleInfo.progressText);
|
||||
this._applyProgressValue(this._titleInfo.nextTitleProgress);
|
||||
}
|
||||
|
||||
private _applyTitleText(text: string | undefined): void {
|
||||
if (this.titleLevelLabel && text !== undefined) {
|
||||
this.titleLevelLabel.string = text;
|
||||
}
|
||||
}
|
||||
|
||||
private _applyProgressText(text: string | undefined): void {
|
||||
if (this.progressLabel && text !== undefined) {
|
||||
this.progressLabel.string = text;
|
||||
}
|
||||
}
|
||||
|
||||
private _applyProgressValue(progress: number | undefined): void {
|
||||
if (this.titleProgressBar && progress !== undefined) {
|
||||
this.titleProgressBar.progress = this._normalizeProgress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 _previousTitleInfo → _titleInfo 驱动进度条过渡动画
|
||||
*
|
||||
* 三种情况:
|
||||
* 1. 无起点信息或起点/终点相同:不播动画
|
||||
* 2. 同称号下涨进度:一段 tween
|
||||
* 3. 跨称号:先把旧称号填到 1.0,然后切换称号文字、进度回 0,再 tween 到终点进度
|
||||
*/
|
||||
private _playProgressAnimation(): void {
|
||||
const prev = this._previousTitleInfo;
|
||||
// 动画是一次性的,播放前消费掉,避免弹窗被复用时重复播
|
||||
this._previousTitleInfo = null;
|
||||
|
||||
if (!this.titleProgressBar || !prev) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.titleProgressBar && this._titleInfo.nextTitleProgress !== undefined) {
|
||||
this.titleProgressBar.progress = this._normalizeProgress(this._titleInfo.nextTitleProgress);
|
||||
const startProgress = prev.nextTitleProgress;
|
||||
const endProgress = this._titleInfo.nextTitleProgress;
|
||||
if (startProgress === undefined || endProgress === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.progressLabel && this._titleInfo.progressText !== undefined) {
|
||||
this.progressLabel.string = this._titleInfo.progressText;
|
||||
const isSameTitle = prev.titleText === undefined
|
||||
|| this._titleInfo.titleText === undefined
|
||||
|| prev.titleText === this._titleInfo.titleText;
|
||||
|
||||
// 同称号且起止相同,没必要播动画
|
||||
if (isSameTitle && Math.abs(startProgress - endProgress) < 1e-4) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._stopProgressAnimation();
|
||||
|
||||
if (isSameTitle) {
|
||||
// 先展示起点,避免 _refreshTitleView 已把条填到终态
|
||||
this._applyProgressValue(startProgress);
|
||||
this._runProgressTween(startProgress, endProgress, PassModal.PROGRESS_ANIM_START_DELAY);
|
||||
return;
|
||||
}
|
||||
|
||||
// 跨称号:先让旧称号文字和起点进度出现在屏上
|
||||
this._applyTitleText(prev.titleText);
|
||||
this._applyProgressText(prev.progressText);
|
||||
this._applyProgressValue(startProgress);
|
||||
|
||||
const self = this;
|
||||
const tweenTarget = this._progressTweenTarget;
|
||||
// raw 值保留 0~1;下发时经 _normalizeProgress
|
||||
tweenTarget.progress = Math.max(0, Math.min(1, startProgress));
|
||||
const clampedEnd = Math.max(0, Math.min(1, endProgress));
|
||||
|
||||
const onUpdate = () => {
|
||||
if (self.titleProgressBar?.isValid) {
|
||||
self.titleProgressBar.progress = self._normalizeProgress(tweenTarget.progress);
|
||||
}
|
||||
};
|
||||
|
||||
tween(tweenTarget)
|
||||
.delay(PassModal.PROGRESS_ANIM_START_DELAY)
|
||||
.to(
|
||||
PassModal.PROGRESS_ANIM_SEGMENT_DURATION,
|
||||
{ progress: 1 },
|
||||
{ easing: 'sineOut', onUpdate }
|
||||
)
|
||||
.call(() => {
|
||||
// 切到新称号。progressText/titleText 都切到终态;进度值从 0 开始
|
||||
self._applyTitleText(self._titleInfo.titleText);
|
||||
self._applyProgressText(self._titleInfo.progressText);
|
||||
tweenTarget.progress = 0;
|
||||
if (self.titleProgressBar?.isValid) {
|
||||
self.titleProgressBar.progress = self._normalizeProgress(0);
|
||||
}
|
||||
})
|
||||
.delay(PassModal.PROGRESS_ANIM_LEVELUP_PAUSE)
|
||||
.to(
|
||||
PassModal.PROGRESS_ANIM_SEGMENT_DURATION,
|
||||
{ progress: clampedEnd },
|
||||
{ easing: 'sineOut', onUpdate }
|
||||
)
|
||||
.start();
|
||||
}
|
||||
|
||||
private _runProgressTween(from: number, to: number, delay: number): void {
|
||||
if (!this.titleProgressBar) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tweenTarget = this._progressTweenTarget;
|
||||
// raw 值保留 0~1 区间,onUpdate 里经 _normalizeProgress 再下发,避免畸变区段
|
||||
tweenTarget.progress = Math.max(0, Math.min(1, from));
|
||||
this.titleProgressBar.progress = this._normalizeProgress(from);
|
||||
|
||||
const self = this;
|
||||
const chain = tween(tweenTarget);
|
||||
if (delay > 0) {
|
||||
chain.delay(delay);
|
||||
}
|
||||
|
||||
chain
|
||||
.to(
|
||||
PassModal.PROGRESS_ANIM_SEGMENT_DURATION,
|
||||
{ progress: Math.max(0, Math.min(1, to)) },
|
||||
{
|
||||
easing: 'sineOut',
|
||||
onUpdate: () => {
|
||||
if (self.titleProgressBar?.isValid) {
|
||||
self.titleProgressBar.progress = self._normalizeProgress(tweenTarget.progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
.start();
|
||||
}
|
||||
|
||||
private _stopProgressAnimation(): void {
|
||||
Tween.stopAllByTarget(this._progressTweenTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user