暂存
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { popupContent } from "./PopupContent";
|
||||
import PopupAnimator, { ActivityPopupAnimationType as PopupType } from "./ActivityPopupAnimator";
|
||||
import SharedPopupBackdrop from "./SharedPopupBackdrop";
|
||||
import ActivityScheduleService from "./module/Config/ActivityScheduleService";
|
||||
import { canClaimPass, passTier } from './module/Config/BattlePassModel';
|
||||
import { getNewShopProduct } from './module/Config/ShopCatalog';
|
||||
@@ -2280,9 +2281,15 @@ export default class JiaZai extends cc.Component {
|
||||
}
|
||||
if (!this.isHomeRuntimeAlive() || cc.fx.GameConfig.isGameplayEntryPreparing()) { if (onClosed) onClosed(); return; }
|
||||
if (!JiaZai.cachedRewardPrefab || !cc.isValid(JiaZai.cachedRewardPrefab, true) || !this.isHomeBundleReady('publicUI')) {
|
||||
const backdrop = SharedPopupBackdrop.get(this.node), transition = {};
|
||||
let cancelled = false;
|
||||
const release = () => { if (cc.isValid(backdrop, true)) backdrop.release(transition); };
|
||||
backdrop.hold(transition, () => {
|
||||
cancelled = true; release(); if (onClosed) onClosed();
|
||||
});
|
||||
this.loadHomePopupPrefab('publicUI', 'prefab/RewardWindow', 'cachedRewardPrefab', 5).then(
|
||||
() => this.openRewardWindow(data, month, onClosed),
|
||||
error => { cc.warn('[首页奖励窗口] 加载失败', error); if (onClosed) onClosed(); });
|
||||
() => { try { if (!cancelled) this.openRewardWindow(data, month, onClosed); } finally { release(); } },
|
||||
error => { release(); cc.warn('[首页奖励窗口] 加载失败', error); if (!cancelled && onClosed) onClosed(); });
|
||||
return;
|
||||
}
|
||||
if (!this.RewardNode) {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import SharedPopupBackdrop, { PopupBackdropSource } from './SharedPopupBackdrop';
|
||||
const { ccclass } = cc._decorator;
|
||||
|
||||
/** Attach only to a full-screen dim mask, never to an activity's artwork.
|
||||
* Keep the original node and input components so prefab event bindings still work. */
|
||||
@ccclass
|
||||
export default class PopupBackdropSourceComponent extends cc.Component {
|
||||
private manager: SharedPopupBackdrop = null;
|
||||
private source: PopupBackdropSource = null;
|
||||
private renderers: Array<{ component: cc.RenderComponent; enabled: boolean }> = [];
|
||||
private originalOpacity = 255;
|
||||
|
||||
onLoad(): void {
|
||||
this.originalOpacity = this.node.opacity;
|
||||
this.renderers = this.node.getComponents(cc.RenderComponent).map(component => ({ component, enabled: component.enabled }));
|
||||
const visual = new cc.Node('ManagedBackdropVisual');
|
||||
visual.active = false;
|
||||
this.node.addChild(visual);
|
||||
visual.addComponent(cc.Graphics);
|
||||
this.source = { node: this.node, visual, active: false };
|
||||
}
|
||||
|
||||
onEnable(): void {
|
||||
let canvas = this.node.parent;
|
||||
while (canvas && canvas.name !== 'Canvas') canvas = canvas.parent;
|
||||
if (!canvas) return;
|
||||
this.manager = SharedPopupBackdrop.get(canvas);
|
||||
this.renderers.forEach(item => item.component.enabled = false);
|
||||
this.source.active = true;
|
||||
this.manager.register(this.source);
|
||||
}
|
||||
|
||||
onDisable(): void {
|
||||
if (this.source) { this.source.active = false; this.source.visual.active = false; }
|
||||
this.node.opacity = this.originalOpacity;
|
||||
this.renderers.forEach(item => { if (cc.isValid(item.component, true)) item.component.enabled = item.enabled; });
|
||||
if (this.manager && cc.isValid(this.manager, true)) this.manager.refresh();
|
||||
}
|
||||
|
||||
onDestroy(): void {
|
||||
if (this.manager && cc.isValid(this.manager, true)) this.manager.unregister(this.source);
|
||||
if (this.source && cc.isValid(this.source.visual, true)) this.source.visual.destroy();
|
||||
this.manager = null; this.source = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "613ae721-73db-477d-9efa-765981d1641b",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
@@ -243,6 +243,7 @@ export default class PyramidView extends cc.Component {
|
||||
this.settlementQueued = true;
|
||||
HomePopupQueue.get(this.node).enqueue("pyramidSettlement", 4, done => {
|
||||
void this.showSettlements(done);
|
||||
return () => { if (this.finishSettlement) this.finishSettlement(); };
|
||||
});
|
||||
}
|
||||
private async showSettlements(done: () => void): Promise<void> {
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
import LoadingCatAnimation from './LoadingCatAnimation';
|
||||
const { ccclass } = cc._decorator;
|
||||
|
||||
export interface PopupBackdropSource {
|
||||
node: cc.Node;
|
||||
visual: cc.Node;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
// Match HomeScene/Canvas/Loading/New Sprite(Splash), including activity handoffs.
|
||||
export const POPUP_BACKDROP_OPACITY = 200;
|
||||
const FADE_SECONDS = 0.15;
|
||||
|
||||
/** Scene-owned backdrop lifetime. Existing mask locations preserve nested draw order
|
||||
* and click handlers; the scene bridge covers the gap between asynchronous popups. */
|
||||
@ccclass
|
||||
export default class SharedPopupBackdrop extends cc.Component {
|
||||
private sources: PopupBackdropSource[] = [];
|
||||
private holds = new Map<object, { cancel: () => void; waiting: boolean }>();
|
||||
private bridge: cc.Node = null;
|
||||
private message: cc.Node = null;
|
||||
private cancelButton: cc.Node = null;
|
||||
private gapSeconds = 0;
|
||||
private fading = false;
|
||||
private bridgeOpacity = POPUP_BACKDROP_OPACITY;
|
||||
private wasVisible = false;
|
||||
private disposed = false;
|
||||
|
||||
static get(canvas: cc.Node): SharedPopupBackdrop {
|
||||
return canvas.getComponent(SharedPopupBackdrop) || canvas.addComponent(SharedPopupBackdrop);
|
||||
}
|
||||
|
||||
register(source: PopupBackdropSource): void {
|
||||
if (this.sources.indexOf(source) < 0) this.sources.push(source);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
unregister(source: PopupBackdropSource): void {
|
||||
this.sources = this.sources.filter(item => item !== source);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
hold(owner: object, cancel: () => void): void {
|
||||
this.holds.set(owner, { cancel, waiting: true });
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
setWaiting(owner: object, waiting: boolean): void {
|
||||
const hold = this.holds.get(owner);
|
||||
if (hold) hold.waiting = waiting;
|
||||
}
|
||||
|
||||
release(owner: object): void {
|
||||
this.holds.delete(owner);
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
/** Compare actual hierarchy order, including sibling zIndex, not registration time. */
|
||||
private path(node: cc.Node): cc.Node[] {
|
||||
const path: cc.Node[] = [];
|
||||
while (node && node !== this.node) { path.unshift(node); node = node.parent; }
|
||||
return node === this.node ? path : [];
|
||||
}
|
||||
|
||||
private compare(a: cc.Node, b: cc.Node): number {
|
||||
const left = this.path(a), right = this.path(b);
|
||||
for (let i = 0; i < Math.min(left.length, right.length); i++) {
|
||||
if (left[i] !== right[i]) return left[i].zIndex - right[i].zIndex
|
||||
|| left[i].getSiblingIndex() - right[i].getSiblingIndex();
|
||||
}
|
||||
return left.length - right.length;
|
||||
}
|
||||
|
||||
/** Draw in the source's local coordinates, covering the canvas even inside scaled panels. */
|
||||
static paint(visual: cc.Node, canvas: cc.Node, opacity = POPUP_BACKDROP_OPACITY): void {
|
||||
const parent = visual.parent;
|
||||
const width = canvas.width, height = canvas.height;
|
||||
const corners = [[-canvas.anchorX * width, -canvas.anchorY * height],
|
||||
[(1 - canvas.anchorX) * width, -canvas.anchorY * height],
|
||||
[(1 - canvas.anchorX) * width, (1 - canvas.anchorY) * height],
|
||||
[-canvas.anchorX * width, (1 - canvas.anchorY) * height]];
|
||||
const graphics = visual.getComponent(cc.Graphics);
|
||||
// Creator 2.4 WebGL Graphics writes fillColor directly to vertex colors;
|
||||
// node.opacity does not change that alpha. Keep it 255 for Canvas parity.
|
||||
visual.opacity = 255;
|
||||
graphics.clear(); graphics.fillColor = cc.color(0, 0, 0, Math.round(opacity));
|
||||
corners.forEach(([x, y], index) => {
|
||||
const point = parent.convertToNodeSpaceAR(canvas.convertToWorldSpaceAR(cc.v2(x, y)));
|
||||
if (index) graphics.lineTo(point.x, point.y); else graphics.moveTo(point.x, point.y);
|
||||
});
|
||||
graphics.close(); graphics.fill();
|
||||
}
|
||||
|
||||
private ensureBridge(): void {
|
||||
if (this.bridge) return;
|
||||
this.bridge = new cc.Node('SharedPopupBackdrop');
|
||||
this.bridge.active = false;
|
||||
this.node.addChild(this.bridge, cc.macro.MAX_ZINDEX - 3);
|
||||
this.bridge.addComponent(cc.Graphics);
|
||||
this.bridge.addComponent(cc.BlockInputEvents);
|
||||
const label = (name: string, text: string, y: number): cc.Node => {
|
||||
const node = new cc.Node(name); this.bridge.addChild(node); node.y = y;
|
||||
const textLabel = node.addComponent(cc.Label);
|
||||
textLabel.string = text; textLabel.fontSize = 28; textLabel.lineHeight = 36;
|
||||
node.setContentSize(320, 70);
|
||||
return node;
|
||||
};
|
||||
const template = this.node.getChildByName('Loading');
|
||||
if (template) {
|
||||
this.message = new cc.Node('LoadingContent');
|
||||
this.message.active = false;
|
||||
this.bridge.addChild(this.message);
|
||||
// Reuse the authored cat, blocks and label, excluding the full-screen mask.
|
||||
['New Label', 'block1', 'block2', 'block3', 'block4', 'cat'].forEach(name => {
|
||||
const child = template.getChildByName(name);
|
||||
if (child) this.message.addChild(cc.instantiate(child));
|
||||
});
|
||||
}
|
||||
const loadingLabel = this.message && this.message.getChildByName('New Label');
|
||||
this.cancelButton = label('CancelWaiting', '暂不等待', loadingLabel ? loadingLabel.y - 90 : -330);
|
||||
this.cancelButton.on(cc.Node.EventType.TOUCH_END, (event: cc.Event) => {
|
||||
event.stopPropagation();
|
||||
// Never allow a stale click to cancel a popup that has already appeared.
|
||||
if (!this.bridge.active || this.gapSeconds < 10 || this.visibleSources().length) return;
|
||||
const callbacks = Array.from(this.holds.values()).filter(hold => hold.waiting && hold.cancel).map(hold => hold.cancel);
|
||||
callbacks.forEach(cancel => cancel());
|
||||
});
|
||||
}
|
||||
|
||||
private setLoadingVisible(visible: boolean): void {
|
||||
if (!this.message || this.message.active === visible) return;
|
||||
if (visible) LoadingCatAnimation.play(this.message);
|
||||
else LoadingCatAnimation.stop(this.message);
|
||||
}
|
||||
|
||||
private visibleSources(): PopupBackdropSource[] {
|
||||
return this.sources.filter(source => source.active && cc.isValid(source.node, true)
|
||||
&& cc.isValid(source.visual, true) && source.node.activeInHierarchy && this.path(source.node).length > 0);
|
||||
}
|
||||
|
||||
refresh(dt = 0): void {
|
||||
if (this.disposed || !cc.isValid(this.node, true)) return;
|
||||
this.sources = this.sources.filter(source => cc.isValid(source.node, true) && cc.isValid(source.visual, true));
|
||||
const visible = this.visibleSources().sort((a, b) => this.compare(a.node, b.node));
|
||||
const top = visible[visible.length - 1];
|
||||
this.sources.forEach(source => {
|
||||
source.visual.active = source === top;
|
||||
if (source === top) {
|
||||
source.node.opacity = 255;
|
||||
SharedPopupBackdrop.paint(source.visual, this.node);
|
||||
}
|
||||
});
|
||||
if (top) {
|
||||
this.setLoadingVisible(false);
|
||||
if (this.bridge) this.bridge.active = false;
|
||||
this.gapSeconds = 0; this.fading = false; this.wasVisible = true;
|
||||
return;
|
||||
}
|
||||
const waiting = Array.from(this.holds.values()).some(hold => hold.waiting);
|
||||
// An unmanaged full-screen view may temporarily own presentation.
|
||||
if (this.holds.size && !waiting) {
|
||||
this.setLoadingVisible(false);
|
||||
if (this.bridge) this.bridge.active = false;
|
||||
this.gapSeconds = 0;
|
||||
return;
|
||||
}
|
||||
if (!waiting && !this.wasVisible) return;
|
||||
this.ensureBridge();
|
||||
this.bridge.active = true;
|
||||
this.bridge.setContentSize(this.node.getContentSize());
|
||||
if (waiting) {
|
||||
this.bridgeOpacity = POPUP_BACKDROP_OPACITY;
|
||||
this.gapSeconds += dt; this.fading = false; this.wasVisible = true;
|
||||
} else {
|
||||
if (!this.fading) { this.bridgeOpacity = POPUP_BACKDROP_OPACITY; this.fading = true; }
|
||||
this.bridgeOpacity = Math.max(0, this.bridgeOpacity - POPUP_BACKDROP_OPACITY * dt / FADE_SECONDS);
|
||||
this.gapSeconds = 0;
|
||||
if (!this.bridgeOpacity) { this.bridge.active = false; this.wasVisible = false; this.fading = false; }
|
||||
}
|
||||
SharedPopupBackdrop.paint(this.bridge, this.node, this.bridgeOpacity);
|
||||
this.setLoadingVisible(waiting && this.gapSeconds >= 0.8);
|
||||
this.cancelButton.active = waiting && this.gapSeconds >= 10
|
||||
&& Array.from(this.holds.values()).every(hold => !!hold.cancel);
|
||||
}
|
||||
|
||||
lateUpdate(dt: number): void { this.refresh(dt); }
|
||||
|
||||
onDestroy(): void {
|
||||
this.setLoadingVisible(false);
|
||||
this.disposed = true; this.holds.clear();
|
||||
this.sources.forEach(source => { if (cc.isValid(source.visual, true)) source.visual.active = false; });
|
||||
this.sources = [];
|
||||
if (this.bridge && cc.isValid(this.bridge, true)) this.bridge.destroy();
|
||||
this.bridge = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"ver": "1.1.0",
|
||||
"uuid": "74f85741-5830-4f3a-aee1-07f7a134534a",
|
||||
"importer": "typescript",
|
||||
"isPlugin": false,
|
||||
"loadPluginInWeb": true,
|
||||
"loadPluginInNative": true,
|
||||
"loadPluginInEditor": false,
|
||||
"subMetas": {}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import HomePopupQueue from '../../home_popup_queue/HomePopupQueue';
|
||||
import GoldMinerService, { GoldMinerDailyReminder } from './GoldMinerService';
|
||||
import Utils from '../module/Pay/Utils';
|
||||
import ActivityBundleLoader from '../module/Config/ActivityBundleLoader';
|
||||
import PopupBackdropSource from '../PopupBackdropSource';
|
||||
const { ccclass } = cc._decorator;
|
||||
const BUNDLE = 'coin_madness';
|
||||
|
||||
@@ -103,6 +104,7 @@ export default class CoinMadnessHost extends cc.Component {
|
||||
this.finishQueue = done;
|
||||
this.automaticPopupKey = 'coinMadnessSettlement';
|
||||
this.load();
|
||||
return () => this.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -251,6 +253,7 @@ export default class CoinMadnessHost extends cc.Component {
|
||||
this.finishQueue = done;
|
||||
this.automaticPopupKey = 'goldMinerDailyReminder';
|
||||
this.load(reminder);
|
||||
return () => this.close();
|
||||
});
|
||||
}
|
||||
private load(reminder?: GoldMinerDailyReminder) {
|
||||
@@ -292,8 +295,7 @@ export default class CoinMadnessHost extends cc.Component {
|
||||
// Recheck after async loading: account, day or activity may have changed in the queue.
|
||||
if (reminder && !this.dailyReminderCurrent(service, reminder)) { this.close(); return; }
|
||||
this.setEntryLoading(false);
|
||||
this.panel = cc.instantiate(prefab); this.node.addChild(this.panel);
|
||||
this.panel.zIndex = cc.macro.MAX_ZINDEX - 1;
|
||||
this.panel = this.mountPopup(prefab, 'Dim', cc.macro.MAX_ZINDEX - 1);
|
||||
this.panel.getComponent('CoinMadnessPanel').initialize(initial.data, {
|
||||
initialReady: true,
|
||||
progressScope: Utils.serverUrl() + ':' + String(cc.fx.GameConfig.GM_INFO.uid),
|
||||
@@ -337,8 +339,7 @@ export default class CoinMadnessHost extends cc.Component {
|
||||
this.refreshCoinDisplay();
|
||||
this.clearHelp();
|
||||
if (this.panel) { this.panel.removeFromParent(); this.panel.destroy(); }
|
||||
this.panel = cc.instantiate(prefab);
|
||||
this.node.addChild(this.panel); this.panel.zIndex = cc.macro.MAX_ZINDEX - 1;
|
||||
this.panel = this.mountPopup(prefab, 'Dim', cc.macro.MAX_ZINDEX - 1);
|
||||
this.panel.getComponent('CoinMadnessSettlement').initialize(data, {
|
||||
real: !!this.service,
|
||||
close: () => {
|
||||
@@ -358,11 +359,10 @@ export default class CoinMadnessHost extends cc.Component {
|
||||
this.loading = false;
|
||||
if (!this.alive || requestId !== this.requestId) { this.release(bundle); return; }
|
||||
if (error) { this.toast('说明加载失败,请重试'); return; }
|
||||
this.help = cc.instantiate(prefab); this.node.addChild(this.help);
|
||||
this.help = this.mountPopup(prefab, 'Mask', cc.macro.MAX_ZINDEX);
|
||||
this.help.setContentSize(cc.view.getVisibleSize());
|
||||
const content = this.help.getChildByName('Content');
|
||||
content.scale = Math.min(this.help.width / content.width, this.help.height / content.height);
|
||||
this.help.zIndex = cc.macro.MAX_ZINDEX;
|
||||
// Match the pass description's staggered backOut entrance, preserving authored scales.
|
||||
this.helpAnimationParts().forEach((part, i) => {
|
||||
const scaleX = part.scaleX, scaleY = part.scaleY;
|
||||
@@ -377,6 +377,14 @@ export default class CoinMadnessHost extends cc.Component {
|
||||
});
|
||||
});
|
||||
}
|
||||
private mountPopup(prefab: cc.Prefab, maskName: string, zIndex: number): cc.Node {
|
||||
const popup = cc.instantiate(prefab);
|
||||
this.node.addChild(popup, zIndex);
|
||||
// Cached/remote prefabs may predate the adapter serialized in local assets.
|
||||
const mask = popup.getChildByName(maskName);
|
||||
if (mask && !mask.getComponent(PopupBackdropSource)) mask.addComponent(PopupBackdropSource);
|
||||
return popup;
|
||||
}
|
||||
private clearHelp() {
|
||||
if (!this.help) return;
|
||||
this.helpAnimationParts().forEach(part => cc.Tween.stopAllByTarget(part));
|
||||
|
||||
Reference in New Issue
Block a user