dzkcc-mflow 0.0.37 → 0.0.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/Api.d.ts +8 -2
- package/dist/core/Core.js +2 -2
- package/dist/libs/BaseView.d.ts +36 -0
- package/dist/libs/BaseView.js +74 -1
- package/dist/libs/ResLoader.d.ts +14 -0
- package/dist/libs/ResLoader.js +67 -17
- package/dist/libs/UIManager.d.ts +162 -17
- package/dist/libs/UIManager.js +505 -137
- package/dist/mflow-tools.zip +0 -0
- package/dist/utils/ImageUtil.d.ts +5 -1
- package/dist/utils/ImageUtil.js +25 -1
- package/package.json +1 -1
package/dist/core/Api.d.ts
CHANGED
|
@@ -65,6 +65,10 @@ export interface IView {
|
|
|
65
65
|
onPause(): void;
|
|
66
66
|
/** 恢复视图(从暂停状态恢复) */
|
|
67
67
|
onResume(): void;
|
|
68
|
+
/** 进入动画(可选) */
|
|
69
|
+
onEnterAnimation?(): Promise<void>;
|
|
70
|
+
/** 退出动画(可选) */
|
|
71
|
+
onExitAnimation?(): Promise<void>;
|
|
68
72
|
}
|
|
69
73
|
/**
|
|
70
74
|
* UI 管理器接口 - 管理视图的打开、关闭和栈操作
|
|
@@ -75,15 +79,17 @@ export interface IUIManager {
|
|
|
75
79
|
/** 打开视图 */
|
|
76
80
|
open<T extends keyof UIRegistry>(viewClass: T, args?: any): Promise<InstanceType<UIRegistry[T]>>;
|
|
77
81
|
/** 关闭视图 */
|
|
78
|
-
close<T extends keyof UIRegistry>(viewClass: T): void
|
|
82
|
+
close<T extends keyof UIRegistry>(viewClass: T): Promise<void>;
|
|
79
83
|
/** 打开视图并入栈 */
|
|
80
84
|
openAndPush<T extends keyof UIRegistry>(viewClass: T, group: string, args?: any): Promise<InstanceType<UIRegistry[T]>>;
|
|
81
85
|
/** 关闭栈顶视图并弹出 */
|
|
82
|
-
closeAndPop(group: string, destroy?: boolean): void
|
|
86
|
+
closeAndPop(group: string, destroy?: boolean): Promise<void>;
|
|
83
87
|
/** 获取栈顶视图 */
|
|
84
88
|
getTopView(): IView | undefined;
|
|
85
89
|
/** 清空视图栈 */
|
|
86
90
|
clearStack(group: string, destroy?: boolean): void;
|
|
91
|
+
/** 关闭所有视图 */
|
|
92
|
+
closeAll(destroy?: boolean): void;
|
|
87
93
|
}
|
|
88
94
|
/**
|
|
89
95
|
* 资源管理器接口
|
package/dist/core/Core.js
CHANGED
|
@@ -33,7 +33,7 @@ class AbstractCore {
|
|
|
33
33
|
* ```
|
|
34
34
|
*/
|
|
35
35
|
getModel(modelClass) {
|
|
36
|
-
const className = modelClass
|
|
36
|
+
const className = modelClass;
|
|
37
37
|
// 如果已存在实例,直接返回
|
|
38
38
|
if (this.container.has(className)) {
|
|
39
39
|
return this.container.get(className);
|
|
@@ -55,7 +55,7 @@ class AbstractCore {
|
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
57
|
getManager(managerClass) {
|
|
58
|
-
const className = managerClass
|
|
58
|
+
const className = managerClass;
|
|
59
59
|
// 如果已存在实例,直接返回
|
|
60
60
|
if (this.container.has(className)) {
|
|
61
61
|
return this.container.get(className);
|
package/dist/libs/BaseView.d.ts
CHANGED
|
@@ -36,4 +36,40 @@ export declare abstract class BaseView extends Component implements IView {
|
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
38
|
protected getManager<T extends keyof ManagerRegistry>(managerClass: T): InstanceType<ManagerRegistry[T]>;
|
|
39
|
+
/**
|
|
40
|
+
* 进入动画(可被子类覆盖以实现自定义动画)
|
|
41
|
+
* 默认实现:缩放+淡入效果
|
|
42
|
+
* @returns 返回 Promise 以支持异步动画
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* async onEnterAnimation(): Promise<void> {
|
|
46
|
+
* // 自定义动画实现
|
|
47
|
+
* return new Promise<void>((resolve) => {
|
|
48
|
+
* tween(this.node)
|
|
49
|
+
* .to(0.5, { scale: new Vec3(1, 1, 1) })
|
|
50
|
+
* .call(() => resolve())
|
|
51
|
+
* .start();
|
|
52
|
+
* });
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
onEnterAnimation(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* 退出动画(可被子类覆盖以实现自定义动画)
|
|
59
|
+
* 默认实现:缩放+淡出效果
|
|
60
|
+
* @returns 返回 Promise 以支持异步动画
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* async onExitAnimation(): Promise<void> {
|
|
64
|
+
* // 自定义动画实现
|
|
65
|
+
* return new Promise<void>((resolve) => {
|
|
66
|
+
* tween(this.node)
|
|
67
|
+
* .to(0.3, { scale: new Vec3(0, 0, 1) })
|
|
68
|
+
* .call(() => resolve())
|
|
69
|
+
* .start();
|
|
70
|
+
* });
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
onExitAnimation(): Promise<void>;
|
|
39
75
|
}
|
package/dist/libs/BaseView.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { __awaiter } from '../_virtual/_tslib.js';
|
|
2
|
+
import { _decorator, Component, UIOpacity, tween, Vec3 } from 'cc';
|
|
2
3
|
|
|
3
4
|
const { ccclass, property } = _decorator;
|
|
4
5
|
class BaseView extends Component {
|
|
@@ -93,6 +94,78 @@ class BaseView extends Component {
|
|
|
93
94
|
getManager(managerClass) {
|
|
94
95
|
return mf.core.getManager(managerClass);
|
|
95
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* 进入动画(可被子类覆盖以实现自定义动画)
|
|
99
|
+
* 默认实现:缩放+淡入效果
|
|
100
|
+
* @returns 返回 Promise 以支持异步动画
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* async onEnterAnimation(): Promise<void> {
|
|
104
|
+
* // 自定义动画实现
|
|
105
|
+
* return new Promise<void>((resolve) => {
|
|
106
|
+
* tween(this.node)
|
|
107
|
+
* .to(0.5, { scale: new Vec3(1, 1, 1) })
|
|
108
|
+
* .call(() => resolve())
|
|
109
|
+
* .start();
|
|
110
|
+
* });
|
|
111
|
+
* }
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
onEnterAnimation() {
|
|
115
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const node = this.node;
|
|
117
|
+
node.setScale(0.8, 0.8, 1);
|
|
118
|
+
let uiOpacity = node.getComponent(UIOpacity);
|
|
119
|
+
if (uiOpacity) {
|
|
120
|
+
uiOpacity.opacity = 0;
|
|
121
|
+
}
|
|
122
|
+
return new Promise((resolve) => {
|
|
123
|
+
const tweenNode = tween(node).to(0.3, { scale: new Vec3(1, 1, 1) }, { easing: 'backOut' });
|
|
124
|
+
if (uiOpacity) {
|
|
125
|
+
tween(uiOpacity)
|
|
126
|
+
.to(0.3, { opacity: 255 })
|
|
127
|
+
.start();
|
|
128
|
+
}
|
|
129
|
+
tweenNode
|
|
130
|
+
.call(() => resolve())
|
|
131
|
+
.start();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 退出动画(可被子类覆盖以实现自定义动画)
|
|
137
|
+
* 默认实现:缩放+淡出效果
|
|
138
|
+
* @returns 返回 Promise 以支持异步动画
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* async onExitAnimation(): Promise<void> {
|
|
142
|
+
* // 自定义动画实现
|
|
143
|
+
* return new Promise<void>((resolve) => {
|
|
144
|
+
* tween(this.node)
|
|
145
|
+
* .to(0.3, { scale: new Vec3(0, 0, 1) })
|
|
146
|
+
* .call(() => resolve())
|
|
147
|
+
* .start();
|
|
148
|
+
* });
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
onExitAnimation() {
|
|
153
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
154
|
+
const node = this.node;
|
|
155
|
+
const uiOpacity = node.getComponent(UIOpacity);
|
|
156
|
+
return new Promise((resolve) => {
|
|
157
|
+
const tweenNode = tween(node).to(0.2, { scale: new Vec3(0.8, 0.8, 1) }, { easing: 'backIn' });
|
|
158
|
+
if (uiOpacity) {
|
|
159
|
+
tween(uiOpacity)
|
|
160
|
+
.to(0.2, { opacity: 0 })
|
|
161
|
+
.start();
|
|
162
|
+
}
|
|
163
|
+
tweenNode
|
|
164
|
+
.call(() => resolve())
|
|
165
|
+
.start();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
96
169
|
}
|
|
97
170
|
|
|
98
171
|
export { BaseView };
|
package/dist/libs/ResLoader.d.ts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import { Asset, Prefab, SpriteFrame, Sprite, sp } from "cc";
|
|
2
2
|
import { ICocosResManager, AssetType } from "../core";
|
|
3
3
|
export declare class ResLoader implements ICocosResManager {
|
|
4
|
+
/**
|
|
5
|
+
*
|
|
6
|
+
* @param nameOrUrl 资源包名称或路径
|
|
7
|
+
* @returns Promise<AssetManager.Bundle>
|
|
8
|
+
*/
|
|
9
|
+
private _loadBundle;
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param path
|
|
13
|
+
* @param type
|
|
14
|
+
* @param nameOrUrl
|
|
15
|
+
* @returns
|
|
16
|
+
*/
|
|
4
17
|
loadAsset<T extends Asset>(path: string, type: AssetType<T>, nameOrUrl?: string): Promise<T>;
|
|
5
18
|
loadPrefab(path: string, nameOrUrl?: string): Promise<Prefab>;
|
|
6
19
|
loadSpriteFrame(ref: Sprite, path: string, nameOrUrl?: string): Promise<SpriteFrame>;
|
|
7
20
|
loadSpine(ref: sp.Skeleton, path: string, nameOrUrl?: string): Promise<sp.SkeletonData>;
|
|
21
|
+
preloadAsset(paths: string | string[], type: AssetType<Asset>, nameOrUrl?: string): Promise<void>;
|
|
8
22
|
release(asset: Asset, force?: boolean): void;
|
|
9
23
|
release(path: string, type?: AssetType<Asset>, nameOrUrl?: string, force?: boolean): void;
|
|
10
24
|
}
|
package/dist/libs/ResLoader.js
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { __awaiter } from '../_virtual/_tslib.js';
|
|
2
|
-
import { assetManager, Prefab,
|
|
2
|
+
import { path, assetManager, Prefab, SpriteFrame, sp, Asset } from 'cc';
|
|
3
3
|
|
|
4
4
|
const DefaultBundle = "resources";
|
|
5
5
|
class ResLoader {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @param nameOrUrl 资源包名称或路径
|
|
9
|
+
* @returns Promise<AssetManager.Bundle>
|
|
10
|
+
*/
|
|
11
|
+
_loadBundle(nameOrUrl = DefaultBundle) {
|
|
12
|
+
const bundleName = path.basename(nameOrUrl);
|
|
13
|
+
const bundle = assetManager.getBundle(bundleName);
|
|
14
|
+
if (bundle) {
|
|
15
|
+
return Promise.resolve(bundle);
|
|
11
16
|
}
|
|
12
17
|
return new Promise((resolve, reject) => {
|
|
13
18
|
assetManager.loadBundle(nameOrUrl, (err, bundle) => {
|
|
@@ -15,22 +20,52 @@ class ResLoader {
|
|
|
15
20
|
reject(err);
|
|
16
21
|
}
|
|
17
22
|
else {
|
|
18
|
-
bundle
|
|
19
|
-
if (err) {
|
|
20
|
-
reject(err);
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
data.addRef();
|
|
24
|
-
resolve(data);
|
|
25
|
-
}
|
|
26
|
-
});
|
|
23
|
+
resolve(bundle);
|
|
27
24
|
}
|
|
28
25
|
});
|
|
29
26
|
});
|
|
30
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
*
|
|
30
|
+
* @param path
|
|
31
|
+
* @param type
|
|
32
|
+
* @param nameOrUrl
|
|
33
|
+
* @returns
|
|
34
|
+
*/
|
|
35
|
+
loadAsset(path, type, nameOrUrl = DefaultBundle) {
|
|
36
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
+
// 如果是在resources路径下,则截取resources之后的路径,否则直接使用路径
|
|
38
|
+
const _path = (nameOrUrl == DefaultBundle && path.includes('resources')) ? path.split('resources/')[1] : path;
|
|
39
|
+
// 加载资源的通用逻辑
|
|
40
|
+
const loadFromBundle = (bundle) => {
|
|
41
|
+
const cachedAsset = bundle.get(_path, type);
|
|
42
|
+
if (cachedAsset) {
|
|
43
|
+
cachedAsset.addRef();
|
|
44
|
+
return Promise.resolve(cachedAsset);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return new Promise((resolve, reject) => {
|
|
48
|
+
bundle.load(_path, type, (err, data) => {
|
|
49
|
+
if (err) {
|
|
50
|
+
reject(err);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
data.addRef();
|
|
54
|
+
resolve(data);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const bundle = yield this._loadBundle(nameOrUrl);
|
|
61
|
+
return loadFromBundle(bundle);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
31
64
|
loadPrefab(path, nameOrUrl = DefaultBundle) {
|
|
32
|
-
|
|
33
|
-
|
|
65
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
//refCount 记录的是持有者数量,不是资源份数,所以prefab也需要addRef
|
|
67
|
+
return yield this.loadAsset(path, Prefab, nameOrUrl);
|
|
68
|
+
});
|
|
34
69
|
}
|
|
35
70
|
loadSpriteFrame(ref, path, nameOrUrl = DefaultBundle) {
|
|
36
71
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -60,6 +95,21 @@ class ResLoader {
|
|
|
60
95
|
}
|
|
61
96
|
});
|
|
62
97
|
}
|
|
98
|
+
preloadAsset(paths, type, nameOrUrl = DefaultBundle) {
|
|
99
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
const bundle = yield this._loadBundle(nameOrUrl);
|
|
101
|
+
new Promise((resolve, reject) => {
|
|
102
|
+
bundle.preload(paths, type, (err, data) => {
|
|
103
|
+
if (err) {
|
|
104
|
+
reject(err);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
resolve(data);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
}
|
|
63
113
|
release(pathOrAsset, typeOrForce, nameOrUrl = DefaultBundle, forceParam = false) {
|
|
64
114
|
var _a;
|
|
65
115
|
let asset;
|
package/dist/libs/UIManager.d.ts
CHANGED
|
@@ -1,34 +1,179 @@
|
|
|
1
|
-
import { Component } from "cc";
|
|
1
|
+
import { Component, Node, Color } from "cc";
|
|
2
2
|
import { IUIManager, IView } from "../core";
|
|
3
|
+
/**
|
|
4
|
+
* UI遮罩配置选项
|
|
5
|
+
*/
|
|
6
|
+
export interface UIMaskOptions {
|
|
7
|
+
/** 遮罩颜色 */
|
|
8
|
+
color?: Color;
|
|
9
|
+
/** 是否可点击关闭顶层UI */
|
|
10
|
+
clickToClose?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* UI打开选项
|
|
14
|
+
*/
|
|
15
|
+
export interface UIOpenOptions {
|
|
16
|
+
/** 是否显示等待视图 */
|
|
17
|
+
showLoading?: boolean;
|
|
18
|
+
/** 是否可点击遮罩关闭 */
|
|
19
|
+
clickToClose?: boolean;
|
|
20
|
+
/** 自定义参数 */
|
|
21
|
+
args?: any;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 等待视图配置
|
|
25
|
+
*/
|
|
26
|
+
export interface UILoadingConfig {
|
|
27
|
+
/** 是否全局启用等待视图 */
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
/** 等待视图预制体路径 */
|
|
30
|
+
prefabPath?: string;
|
|
31
|
+
/** 等待视图显示延迟(毫秒) */
|
|
32
|
+
delay?: number;
|
|
33
|
+
/** 最小显示时间(毫秒) */
|
|
34
|
+
minShowTime?: number;
|
|
35
|
+
/** 自定义创建函数(高级用法) */
|
|
36
|
+
createCustomLoading?: () => Node | Promise<Node>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* LRU缓存配置
|
|
40
|
+
*/
|
|
41
|
+
export interface UICacheConfig {
|
|
42
|
+
/** 最大缓存数量 */
|
|
43
|
+
maxSize?: number;
|
|
44
|
+
/** 是否启用LRU策略 */
|
|
45
|
+
enableLRU?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* 预加载配置
|
|
49
|
+
*/
|
|
50
|
+
export interface UIPreloadConfig {
|
|
51
|
+
/** 预加载的视图列表 */
|
|
52
|
+
views?: (keyof UIRegistry)[];
|
|
53
|
+
/** 预加载延迟(毫秒) */
|
|
54
|
+
delay?: number;
|
|
55
|
+
}
|
|
3
56
|
type ICocosView = IView & Component;
|
|
4
57
|
declare abstract class CcocosUIManager implements IUIManager {
|
|
5
58
|
getTopView(): IView | undefined;
|
|
6
|
-
open<T extends keyof UIRegistry>(viewClass: T, args?:
|
|
7
|
-
close<T extends keyof UIRegistry>(viewClass: T): void
|
|
8
|
-
openAndPush<T extends keyof UIRegistry>(viewClass: T, group: string, args?:
|
|
9
|
-
closeAndPop(group: string, destroy?: boolean): void
|
|
59
|
+
open<T extends keyof UIRegistry>(viewClass: T, args?: UIOpenOptions): Promise<InstanceType<UIRegistry[T]>>;
|
|
60
|
+
close<T extends keyof UIRegistry>(viewClass: T): Promise<void>;
|
|
61
|
+
openAndPush<T extends keyof UIRegistry>(viewClass: T, group: string, args?: UIOpenOptions): Promise<InstanceType<UIRegistry[T]>>;
|
|
62
|
+
closeAndPop(group: string, destroy?: boolean): Promise<void>;
|
|
10
63
|
clearStack(group: string, destroy?: boolean): void;
|
|
11
|
-
|
|
12
|
-
protected abstract
|
|
13
|
-
protected abstract
|
|
14
|
-
protected abstract
|
|
15
|
-
protected abstract
|
|
16
|
-
protected abstract
|
|
64
|
+
closeAll(destroy?: boolean): void;
|
|
65
|
+
protected abstract _internalOpen(viewKey: string, args?: UIOpenOptions): Promise<ICocosView>;
|
|
66
|
+
protected abstract _internalClose(viewKey: string | IView, destory?: boolean): Promise<void>;
|
|
67
|
+
protected abstract _internalOpenAndPush(viewKey: string, group: string, args?: UIOpenOptions): Promise<ICocosView>;
|
|
68
|
+
protected abstract _internalCloseAndPop(group: string, destroy?: boolean): Promise<void>;
|
|
69
|
+
protected abstract _internalClearStack(group: string, destroy?: boolean): void;
|
|
70
|
+
protected abstract _internalGetTopView(): ICocosView | undefined;
|
|
71
|
+
protected abstract _internalCloseAll(destroy?: boolean): void;
|
|
17
72
|
}
|
|
18
73
|
export declare class UIManager extends CcocosUIManager {
|
|
19
74
|
private _cache;
|
|
20
75
|
private _groupStacks;
|
|
76
|
+
private _inputBlocker;
|
|
77
|
+
private _loadingView;
|
|
78
|
+
private _loadingPromises;
|
|
79
|
+
private _lruOrder;
|
|
80
|
+
private _preloadedViews;
|
|
81
|
+
private _maskOptions;
|
|
82
|
+
private _loadingConfig;
|
|
83
|
+
private _cacheConfig;
|
|
84
|
+
private _preloadConfig;
|
|
85
|
+
private _openOptions;
|
|
21
86
|
constructor();
|
|
87
|
+
/**
|
|
88
|
+
* 设置遮罩配置
|
|
89
|
+
*/
|
|
90
|
+
setMaskConfig(options: UIMaskOptions): void;
|
|
91
|
+
/**
|
|
92
|
+
* 设置等待视图配置
|
|
93
|
+
*/
|
|
94
|
+
setLoadingConfig(config: UILoadingConfig): void;
|
|
95
|
+
/**
|
|
96
|
+
* 设置缓存配置
|
|
97
|
+
*/
|
|
98
|
+
setCacheConfig(config: UICacheConfig): void;
|
|
99
|
+
/**
|
|
100
|
+
* 设置预加载配置
|
|
101
|
+
*/
|
|
102
|
+
setPreloadConfig(config: UIPreloadConfig): void;
|
|
103
|
+
/**
|
|
104
|
+
* 检查指定视图是否已打开
|
|
105
|
+
*/
|
|
106
|
+
contains<T extends keyof UIRegistry>(viewKey: T): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* 检查视图是否正在加载
|
|
109
|
+
*/
|
|
110
|
+
isLoading<T extends keyof UIRegistry>(viewKey: T): boolean;
|
|
111
|
+
/**
|
|
112
|
+
* 预加载视图(支持单个或多个)
|
|
113
|
+
*/
|
|
114
|
+
preload<T extends keyof UIRegistry>(viewKeyOrKeys: T | T[]): Promise<void>;
|
|
22
115
|
private _getPrefabPath;
|
|
116
|
+
/**
|
|
117
|
+
* 获取所有活跃的视图节点(排除遮罩节点)
|
|
118
|
+
*/
|
|
119
|
+
private _getActiveViews;
|
|
120
|
+
/**
|
|
121
|
+
* 通过prefab创建Node对象
|
|
122
|
+
* @param args
|
|
123
|
+
* @returns Node对象
|
|
124
|
+
*/
|
|
125
|
+
private _generateNode;
|
|
126
|
+
/**
|
|
127
|
+
* 调整遮罩层级:始终保持在最顶层UI的下一层
|
|
128
|
+
*/
|
|
23
129
|
private _adjustMaskLayer;
|
|
130
|
+
/**
|
|
131
|
+
* 更新LRU顺序
|
|
132
|
+
*/
|
|
133
|
+
private _updateLRUOrder;
|
|
134
|
+
/**
|
|
135
|
+
* 阻塞/解除输入事件
|
|
136
|
+
*/
|
|
24
137
|
private _blockInput;
|
|
138
|
+
/**
|
|
139
|
+
* 设置遮罩点击处理器
|
|
140
|
+
*/
|
|
141
|
+
private _setupMaskClickHandler;
|
|
142
|
+
/**
|
|
143
|
+
* 显示等待视图
|
|
144
|
+
*/
|
|
145
|
+
private _showLoading;
|
|
146
|
+
/**
|
|
147
|
+
* 隐藏等待视图
|
|
148
|
+
*/
|
|
149
|
+
private _hideLoading;
|
|
150
|
+
/**
|
|
151
|
+
* 预加载视图
|
|
152
|
+
*/
|
|
153
|
+
private _startPreload;
|
|
154
|
+
/**
|
|
155
|
+
* 预加载单个视图
|
|
156
|
+
*/
|
|
157
|
+
private _preloadView;
|
|
158
|
+
/**
|
|
159
|
+
* 获取当前最顶层的视图
|
|
160
|
+
*/
|
|
161
|
+
protected _internalGetTopView(): ICocosView | undefined;
|
|
25
162
|
private _load;
|
|
163
|
+
private _loadInternal;
|
|
164
|
+
protected _internalOpen(viewKey: string, options?: UIOpenOptions): Promise<ICocosView>;
|
|
165
|
+
protected _internalClose(viewKeyOrInstance: string | IView, destroy?: boolean): Promise<void>;
|
|
166
|
+
protected _internalOpenAndPush(viewKey: string, group: string, options?: UIOpenOptions): Promise<ICocosView>;
|
|
167
|
+
protected _internalCloseAndPop(group: string, destroy?: boolean): Promise<void>;
|
|
168
|
+
/**
|
|
169
|
+
* 移除视图
|
|
170
|
+
*/
|
|
26
171
|
private _remove;
|
|
27
|
-
|
|
28
|
-
protected
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
protected
|
|
172
|
+
private _releasePrefab;
|
|
173
|
+
protected _internalClearStack(group: string, destroy?: boolean): void;
|
|
174
|
+
/**
|
|
175
|
+
* 关闭所有视图,不播放动画
|
|
176
|
+
*/
|
|
177
|
+
protected _internalCloseAll(destroy?: boolean): void;
|
|
33
178
|
}
|
|
34
179
|
export {};
|
package/dist/libs/UIManager.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { __awaiter } from '../_virtual/_tslib.js';
|
|
2
|
-
import { director, Node, Sprite, Widget,
|
|
2
|
+
import { director, Node, Sprite, Widget, input, Input, Prefab, instantiate } from 'cc';
|
|
3
3
|
import { ServiceLocator } from '../core/ServiceLocator.js';
|
|
4
4
|
import { getViewClass } from '../core/Decorators.js';
|
|
5
|
+
import '../utils/ArrayExt.js';
|
|
6
|
+
import { ImageUtil } from '../utils/ImageUtil.js';
|
|
7
|
+
import '../utils/MathUtil.js';
|
|
5
8
|
|
|
9
|
+
/**
|
|
10
|
+
* 为节点添加全屏Widget组件
|
|
11
|
+
*/
|
|
6
12
|
function addWidget(node) {
|
|
7
13
|
const widget = node.getComponent(Widget) || node.addComponent(Widget);
|
|
8
14
|
widget.isAlignLeft = widget.isAlignRight = widget.isAlignTop = widget.isAlignBottom = true;
|
|
@@ -33,19 +39,21 @@ function addChild(node) {
|
|
|
33
39
|
setLayer(node);
|
|
34
40
|
}
|
|
35
41
|
let _uiMask;
|
|
42
|
+
/**
|
|
43
|
+
* UI遮罩节点代理
|
|
44
|
+
*/
|
|
36
45
|
const UIMask = new Proxy({}, {
|
|
37
46
|
get(target, prop) {
|
|
38
47
|
if (!_uiMask) {
|
|
39
48
|
_uiMask = new Node('__UIMask__');
|
|
40
49
|
addChild(_uiMask);
|
|
41
|
-
addWidget(_uiMask);
|
|
42
50
|
_uiMask.setPosition(0, 0);
|
|
43
|
-
_uiMask.addComponent(Sprite).
|
|
51
|
+
_uiMask.addComponent(Sprite).spriteFrame = ImageUtil.createSolidColorSpriteFrame();
|
|
52
|
+
addWidget(_uiMask);
|
|
44
53
|
}
|
|
45
54
|
const value = Reflect.get(_uiMask, prop);
|
|
46
|
-
//
|
|
55
|
+
// 绑定方法到原始实例
|
|
47
56
|
return typeof value === 'function' ? value.bind(_uiMask) : value;
|
|
48
|
-
// return Reflect.get(_uiMask, prop)
|
|
49
57
|
},
|
|
50
58
|
set(target, p, newValue, receiver) {
|
|
51
59
|
if (p === 'active') {
|
|
@@ -58,25 +66,25 @@ const UIMask = new Proxy({}, {
|
|
|
58
66
|
// 接口隔离,实现具体的CcocosUIManager
|
|
59
67
|
class CcocosUIManager {
|
|
60
68
|
getTopView() {
|
|
61
|
-
return this.
|
|
69
|
+
return this._internalGetTopView();
|
|
62
70
|
}
|
|
63
71
|
open(viewClass, args) {
|
|
64
|
-
|
|
65
|
-
return this.internalOpen(className, args);
|
|
72
|
+
return this._internalOpen(viewClass, args);
|
|
66
73
|
}
|
|
67
74
|
close(viewClass) {
|
|
68
|
-
|
|
69
|
-
this.internalClose(className);
|
|
75
|
+
return this._internalClose(viewClass);
|
|
70
76
|
}
|
|
71
77
|
openAndPush(viewClass, group, args) {
|
|
72
|
-
|
|
73
|
-
return this.internalOpenAndPush(className, group, args);
|
|
78
|
+
return this._internalOpenAndPush(viewClass, group, args);
|
|
74
79
|
}
|
|
75
80
|
closeAndPop(group, destroy) {
|
|
76
|
-
this.
|
|
81
|
+
return this._internalCloseAndPop(group, destroy);
|
|
77
82
|
}
|
|
78
83
|
clearStack(group, destroy) {
|
|
79
|
-
this.
|
|
84
|
+
this._internalClearStack(group, destroy);
|
|
85
|
+
}
|
|
86
|
+
closeAll(destroy) {
|
|
87
|
+
this._internalCloseAll(destroy);
|
|
80
88
|
}
|
|
81
89
|
}
|
|
82
90
|
class UIManager extends CcocosUIManager {
|
|
@@ -84,19 +92,80 @@ class UIManager extends CcocosUIManager {
|
|
|
84
92
|
super();
|
|
85
93
|
this._cache = new Map();
|
|
86
94
|
this._groupStacks = new Map();
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
this._inputBlocker = null;
|
|
96
|
+
this._loadingView = null;
|
|
97
|
+
this._loadingPromises = new Map();
|
|
98
|
+
this._lruOrder = [];
|
|
99
|
+
this._preloadedViews = new Set();
|
|
100
|
+
this._maskOptions = { clickToClose: true };
|
|
101
|
+
this._loadingConfig = { enabled: true, delay: 200, minShowTime: 500 };
|
|
102
|
+
this._cacheConfig = { maxSize: 10, enableLRU: true };
|
|
103
|
+
this._preloadConfig = { views: [], delay: 1000 };
|
|
104
|
+
this._openOptions = { showLoading: true, clickToClose: true };
|
|
105
|
+
this._setupMaskClickHandler();
|
|
106
|
+
this._startPreload();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* 设置遮罩配置
|
|
110
|
+
*/
|
|
111
|
+
setMaskConfig(options) {
|
|
112
|
+
this._maskOptions = Object.assign(Object.assign({}, this._maskOptions), options);
|
|
113
|
+
if (options.color && UIMask) {
|
|
114
|
+
const sprite = UIMask.getComponent(Sprite);
|
|
115
|
+
if (sprite) {
|
|
116
|
+
sprite.color = options.color;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* 设置等待视图配置
|
|
122
|
+
*/
|
|
123
|
+
setLoadingConfig(config) {
|
|
124
|
+
this._loadingConfig = Object.assign(Object.assign({}, this._loadingConfig), config);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 设置缓存配置
|
|
128
|
+
*/
|
|
129
|
+
setCacheConfig(config) {
|
|
130
|
+
this._cacheConfig = Object.assign(Object.assign({}, this._cacheConfig), config);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 设置预加载配置
|
|
134
|
+
*/
|
|
135
|
+
setPreloadConfig(config) {
|
|
136
|
+
this._preloadConfig = Object.assign(Object.assign({}, this._preloadConfig), config);
|
|
137
|
+
this._startPreload();
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 检查指定视图是否已打开
|
|
141
|
+
*/
|
|
142
|
+
contains(viewKey) {
|
|
143
|
+
const viewType = getViewClass(viewKey);
|
|
144
|
+
return this._cache.has(viewType.name) && this._cache.get(viewType.name).parent !== null;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* 检查视图是否正在加载
|
|
148
|
+
*/
|
|
149
|
+
isLoading(viewKey) {
|
|
150
|
+
return this._loadingPromises.has(viewKey);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* 预加载视图(支持单个或多个)
|
|
154
|
+
*/
|
|
155
|
+
preload(viewKeyOrKeys) {
|
|
156
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
157
|
+
if (Array.isArray(viewKeyOrKeys)) {
|
|
158
|
+
const promises = viewKeyOrKeys.map(key => this._preloadView(key));
|
|
159
|
+
yield Promise.all(promises);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
yield this._preloadView(viewKeyOrKeys);
|
|
97
163
|
}
|
|
98
164
|
});
|
|
99
165
|
}
|
|
166
|
+
//----------------------------------------------------------
|
|
167
|
+
// ⬇️⬇️⬇️⬇️⬇️ 内部私有方法 ⬇️⬇️⬇️⬇️⬇️
|
|
168
|
+
//----------------------------------------------------------
|
|
100
169
|
_getPrefabPath(viewType) {
|
|
101
170
|
let prototype = Object.getPrototypeOf(viewType);
|
|
102
171
|
// 沿着原型链向上查找直到找到定义__path__的基类。注意通过类只能找到静态属性。
|
|
@@ -108,175 +177,474 @@ class UIManager extends CcocosUIManager {
|
|
|
108
177
|
}
|
|
109
178
|
throw new Error(`Prefab path not found for ${viewType.name}`);
|
|
110
179
|
}
|
|
111
|
-
|
|
180
|
+
/**
|
|
181
|
+
* 获取所有活跃的视图节点(排除遮罩节点)
|
|
182
|
+
*/
|
|
183
|
+
_getActiveViews() {
|
|
184
|
+
return UIRoot.children.filter(child => child !== _uiMask && child.name !== '__UIMask__');
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* 通过prefab创建Node对象
|
|
188
|
+
* @param args
|
|
189
|
+
* @returns Node对象
|
|
190
|
+
*/
|
|
191
|
+
_generateNode(args) {
|
|
192
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
193
|
+
let prefabPath = args.prefabPath;
|
|
194
|
+
if (!prefabPath) {
|
|
195
|
+
prefabPath = this._getPrefabPath(getViewClass(args.viewKey));
|
|
196
|
+
}
|
|
197
|
+
const ResMgr = ServiceLocator.getService('ResLoader');
|
|
198
|
+
const prefab = yield ResMgr.loadPrefab(prefabPath);
|
|
199
|
+
return instantiate(prefab);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* 调整遮罩层级:始终保持在最顶层UI的下一层
|
|
204
|
+
*/
|
|
112
205
|
_adjustMaskLayer() {
|
|
113
|
-
|
|
114
|
-
|
|
206
|
+
const activeViews = this._getActiveViews();
|
|
207
|
+
// 没有活跃视图时隐藏遮罩
|
|
208
|
+
if (activeViews.length === 0) {
|
|
115
209
|
UIMask.active = false;
|
|
116
210
|
return;
|
|
117
211
|
}
|
|
212
|
+
// 有视图时显示遮罩并调整到倒数第二层
|
|
118
213
|
UIMask.active = true;
|
|
119
|
-
|
|
214
|
+
const targetIndex = Math.max(UIRoot.children.length - 2, 0);
|
|
215
|
+
UIMask.setSiblingIndex(targetIndex);
|
|
120
216
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
217
|
+
/**
|
|
218
|
+
* 更新LRU顺序
|
|
219
|
+
*/
|
|
220
|
+
_updateLRUOrder(viewKey) {
|
|
221
|
+
if (!this._cacheConfig.enableLRU)
|
|
222
|
+
return;
|
|
223
|
+
const index = this._lruOrder.indexOf(viewKey);
|
|
224
|
+
if (index > -1) {
|
|
225
|
+
this._lruOrder.splice(index, 1);
|
|
226
|
+
}
|
|
227
|
+
this._lruOrder.push(viewKey);
|
|
228
|
+
// 检查缓存大小限制
|
|
229
|
+
if (this._lruOrder.length > this._cacheConfig.maxSize) {
|
|
230
|
+
const oldestKey = this._lruOrder.shift();
|
|
231
|
+
const oldestNode = this._cache.get(oldestKey);
|
|
232
|
+
// 只清理未激活的视图(不在场景树中的)
|
|
233
|
+
if (oldestNode && !oldestNode.parent) {
|
|
234
|
+
this._cache.delete(oldestKey);
|
|
235
|
+
// LRU清理不需要走框架的remove机制,因为:
|
|
236
|
+
// 1. 这些节点已经不在场景树中(已通过_remove移除)
|
|
237
|
+
// 2. onExit等生命周期已在_remove中调用过
|
|
238
|
+
// 3. 直接销毁节点释放内存即可
|
|
239
|
+
oldestNode.destroy();
|
|
240
|
+
// 尝试释放对应的Prefab资源
|
|
241
|
+
this._releasePrefab(oldestKey);
|
|
242
|
+
}
|
|
124
243
|
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* 阻塞/解除输入事件
|
|
247
|
+
*/
|
|
248
|
+
_blockInput(block) {
|
|
125
249
|
if (block) {
|
|
126
|
-
|
|
127
|
-
|
|
250
|
+
// 创建并保存阻塞器引用
|
|
251
|
+
if (!this._inputBlocker) {
|
|
252
|
+
this._inputBlocker = (event) => {
|
|
253
|
+
event.propagationImmediateStopped = true;
|
|
254
|
+
};
|
|
128
255
|
}
|
|
256
|
+
// 只监听常用的触摸和鼠标事件
|
|
257
|
+
input.on(Input.EventType.TOUCH_START, this._inputBlocker, this);
|
|
258
|
+
input.on(Input.EventType.TOUCH_MOVE, this._inputBlocker, this);
|
|
259
|
+
input.on(Input.EventType.TOUCH_END, this._inputBlocker, this);
|
|
260
|
+
input.on(Input.EventType.TOUCH_CANCEL, this._inputBlocker, this);
|
|
129
261
|
}
|
|
130
262
|
else {
|
|
131
|
-
|
|
132
|
-
|
|
263
|
+
// 使用保存的引用解除监听
|
|
264
|
+
if (this._inputBlocker) {
|
|
265
|
+
input.off(Input.EventType.TOUCH_START, this._inputBlocker, this);
|
|
266
|
+
input.off(Input.EventType.TOUCH_MOVE, this._inputBlocker, this);
|
|
267
|
+
input.off(Input.EventType.TOUCH_END, this._inputBlocker, this);
|
|
268
|
+
input.off(Input.EventType.TOUCH_CANCEL, this._inputBlocker, this);
|
|
133
269
|
}
|
|
134
270
|
}
|
|
135
271
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
272
|
+
/**
|
|
273
|
+
* 设置遮罩点击处理器
|
|
274
|
+
*/
|
|
275
|
+
_setupMaskClickHandler() {
|
|
276
|
+
UIMask.on(Node.EventType.TOUCH_END, (event) => {
|
|
277
|
+
if (!this._maskOptions.clickToClose) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const view = this.getTopView();
|
|
281
|
+
if (!view) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
// 区分两种情况处理:
|
|
285
|
+
// 1. 如果视图有 __group__ 属性且不为 undefined,说明是通过 openAndPush 打开的栈式UI
|
|
286
|
+
// 2. 否则是通过 open 打开的普通 UI
|
|
287
|
+
if ('__group__' in view && view.__group__ !== undefined) {
|
|
288
|
+
// 栈式UI:调用 _internalCloseAndPop 来处理返回逻辑
|
|
289
|
+
this._internalCloseAndPop(view.__group__, false);
|
|
142
290
|
}
|
|
143
291
|
else {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const prefab = yield ResMgr.loadPrefab(prefabPath);
|
|
147
|
-
target = instantiate(prefab);
|
|
148
|
-
this._cache.set(viewType.name, target);
|
|
292
|
+
// 普通UI:直接关闭该视图
|
|
293
|
+
this._internalClose(view, false);
|
|
149
294
|
}
|
|
150
|
-
target.active = true;
|
|
151
|
-
return target.getComponent(viewType);
|
|
152
295
|
});
|
|
153
296
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
console.warn(`No cached view found for ${viewType.name}`);
|
|
162
|
-
return;
|
|
297
|
+
/**
|
|
298
|
+
* 显示等待视图
|
|
299
|
+
*/
|
|
300
|
+
_showLoading() {
|
|
301
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
302
|
+
if (!this._loadingConfig.enabled) {
|
|
303
|
+
return Promise.resolve();
|
|
163
304
|
}
|
|
164
|
-
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
305
|
+
// 如果已经显示了等待视图,直接返回
|
|
306
|
+
if (this._loadingView && this._loadingView.activeInHierarchy) {
|
|
307
|
+
return Promise.resolve();
|
|
308
|
+
}
|
|
309
|
+
// 首次加载或创建等待视图
|
|
310
|
+
if (!this._loadingView) {
|
|
311
|
+
if (this._loadingConfig.createCustomLoading) {
|
|
312
|
+
// 使用自定义创建函数
|
|
313
|
+
this._loadingView = yield this._loadingConfig.createCustomLoading();
|
|
314
|
+
}
|
|
315
|
+
else if (this._loadingConfig.prefabPath) {
|
|
316
|
+
try {
|
|
317
|
+
// 从 prefab 创建等待视图
|
|
318
|
+
this._loadingView = yield this._generateNode({ prefabPath: this._loadingConfig.prefabPath });
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
throw error;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
// 没有配置等待视图
|
|
326
|
+
return Promise.resolve();
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// 激活并添加到场景,调整到最顶层
|
|
330
|
+
this._loadingView.active = true;
|
|
331
|
+
UIRoot.parent.addChild(this._loadingView);
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* 隐藏等待视图
|
|
336
|
+
*/
|
|
337
|
+
_hideLoading() {
|
|
338
|
+
if (this._loadingView && this._loadingView.activeInHierarchy) {
|
|
339
|
+
this._loadingView.active = false;
|
|
340
|
+
if (this._loadingView.parent) {
|
|
341
|
+
this._loadingView.removeFromParent();
|
|
168
342
|
}
|
|
169
|
-
this._remove(viewInstance, destroy);
|
|
170
|
-
return;
|
|
171
343
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* 预加载视图
|
|
347
|
+
*/
|
|
348
|
+
_startPreload() {
|
|
349
|
+
if (!this._preloadConfig.views || this._preloadConfig.views.length === 0) {
|
|
350
|
+
return;
|
|
176
351
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
const ResMgr = ServiceLocator.getService('ResLoader');
|
|
189
|
-
ResMgr.release(prefabPath, Prefab);
|
|
352
|
+
setTimeout(() => __awaiter(this, void 0, void 0, function* () {
|
|
353
|
+
for (const viewKey of this._preloadConfig.views) {
|
|
354
|
+
if (!this._preloadedViews.has(viewKey)) {
|
|
355
|
+
try {
|
|
356
|
+
yield this._preloadView(viewKey);
|
|
357
|
+
this._preloadedViews.add(viewKey);
|
|
358
|
+
}
|
|
359
|
+
catch (error) {
|
|
360
|
+
console.warn(`Failed to preload view ${viewKey}:`, error);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
190
363
|
}
|
|
191
|
-
|
|
192
|
-
|
|
364
|
+
}), this._preloadConfig.delay || 1000);
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* 预加载单个视图
|
|
368
|
+
*/
|
|
369
|
+
_preloadView(viewKey) {
|
|
370
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
371
|
+
const viewType = getViewClass(viewKey);
|
|
372
|
+
if (this._cache.has(viewType.name)) {
|
|
373
|
+
return; // 已经缓存
|
|
193
374
|
}
|
|
194
|
-
|
|
375
|
+
const target = yield this._generateNode({ viewKey: viewKey });
|
|
376
|
+
target.active = false; // 预加载的视图保持不激活状态
|
|
377
|
+
this._cache.set(viewType.name, target);
|
|
378
|
+
this._updateLRUOrder(viewType.name);
|
|
379
|
+
});
|
|
195
380
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
381
|
+
/**
|
|
382
|
+
* 获取当前最顶层的视图
|
|
383
|
+
*/
|
|
384
|
+
_internalGetTopView() {
|
|
385
|
+
const activeViews = this._getActiveViews();
|
|
386
|
+
if (activeViews.length === 0) {
|
|
199
387
|
return undefined;
|
|
200
388
|
}
|
|
389
|
+
// 获取最后一个视图节点(最顶层)
|
|
390
|
+
const target = activeViews[activeViews.length - 1];
|
|
201
391
|
const comps = target.components;
|
|
202
392
|
for (let i = 0; i < comps.length; i++) {
|
|
203
393
|
const comp = comps[i];
|
|
204
|
-
if ("__isIView__" in comp) {
|
|
205
|
-
|
|
206
|
-
return comp;
|
|
207
|
-
}
|
|
394
|
+
if ("__isIView__" in comp && comp.__isIView__) {
|
|
395
|
+
return comp;
|
|
208
396
|
}
|
|
209
397
|
}
|
|
210
398
|
console.warn(`No view found in ${target.name}`);
|
|
211
399
|
return undefined;
|
|
212
400
|
}
|
|
213
|
-
|
|
401
|
+
//----------------------------------------------------------
|
|
402
|
+
_load(viewKey) {
|
|
403
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
404
|
+
// 并发控制:如果正在加载同一个视图,返回现有的Promise
|
|
405
|
+
if (this._loadingPromises.has(viewKey)) {
|
|
406
|
+
return this._loadingPromises.get(viewKey);
|
|
407
|
+
}
|
|
408
|
+
const loadPromise = this._loadInternal(viewKey);
|
|
409
|
+
this._loadingPromises.set(viewKey, loadPromise);
|
|
410
|
+
try {
|
|
411
|
+
return yield loadPromise;
|
|
412
|
+
}
|
|
413
|
+
finally {
|
|
414
|
+
this._loadingPromises.delete(viewKey);
|
|
415
|
+
}
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
_loadInternal(viewKey) {
|
|
419
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
420
|
+
const viewType = getViewClass(viewKey);
|
|
421
|
+
let target;
|
|
422
|
+
if (this._cache.has(viewType.name)) {
|
|
423
|
+
target = this._cache.get(viewType.name);
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
target = yield this._generateNode({ viewKey: viewKey });
|
|
427
|
+
this._cache.set(viewType.name, target);
|
|
428
|
+
}
|
|
429
|
+
// 更新LRU顺序
|
|
430
|
+
this._updateLRUOrder(viewType.name);
|
|
431
|
+
target.active = true;
|
|
432
|
+
return target.getComponent(viewType);
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
_internalOpen(viewKey, options) {
|
|
436
|
+
var _a;
|
|
437
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
438
|
+
const op = Object.assign(Object.assign({}, this._openOptions), options);
|
|
439
|
+
// 显示等待视图
|
|
440
|
+
if (op.showLoading) {
|
|
441
|
+
yield this._showLoading();
|
|
442
|
+
}
|
|
443
|
+
// 打开UI前,阻塞输入事件
|
|
444
|
+
this._blockInput(true);
|
|
445
|
+
try {
|
|
446
|
+
const view = yield this._load(viewKey);
|
|
447
|
+
addChild(view.node);
|
|
448
|
+
this._adjustMaskLayer();
|
|
449
|
+
// 先执行onEnter初始化,再播放动画
|
|
450
|
+
view.onEnter(op.args);
|
|
451
|
+
// 播放打开动画
|
|
452
|
+
yield ((_a = view.onEnterAnimation) === null || _a === void 0 ? void 0 : _a.call(view));
|
|
453
|
+
return view;
|
|
454
|
+
}
|
|
455
|
+
finally {
|
|
456
|
+
// 隐藏等待视图
|
|
457
|
+
if (op.showLoading) {
|
|
458
|
+
this._hideLoading();
|
|
459
|
+
}
|
|
460
|
+
// 打开UI后,解除输入事件阻塞
|
|
461
|
+
this._blockInput(false);
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
_internalClose(viewKeyOrInstance, destroy) {
|
|
214
466
|
return __awaiter(this, void 0, void 0, function* () {
|
|
215
467
|
this._blockInput(true);
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
468
|
+
try {
|
|
469
|
+
yield this._remove(viewKeyOrInstance, destroy);
|
|
470
|
+
this._adjustMaskLayer();
|
|
471
|
+
}
|
|
472
|
+
finally {
|
|
473
|
+
this._blockInput(false);
|
|
474
|
+
}
|
|
222
475
|
});
|
|
223
476
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this
|
|
477
|
+
_internalOpenAndPush(viewKey, group, options) {
|
|
478
|
+
var _a, _b;
|
|
479
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
480
|
+
const op = Object.assign(Object.assign({}, this._openOptions), options);
|
|
481
|
+
// 显示等待视图
|
|
482
|
+
if (op.showLoading) {
|
|
483
|
+
yield this._showLoading();
|
|
484
|
+
}
|
|
485
|
+
// 打开UI前,阻塞输入事件
|
|
486
|
+
this._blockInput(true);
|
|
487
|
+
try {
|
|
488
|
+
const view = yield this._load(viewKey);
|
|
489
|
+
let stack = this._groupStacks.get(group);
|
|
490
|
+
if (!stack) {
|
|
491
|
+
stack = [];
|
|
492
|
+
this._groupStacks.set(group, stack);
|
|
493
|
+
}
|
|
494
|
+
// 暂停并移除当前栈顶视图
|
|
495
|
+
const top = stack[stack.length - 1];
|
|
496
|
+
if (top) {
|
|
497
|
+
// 先执行onExit,再播放退出动画
|
|
498
|
+
yield ((_a = top.onExitAnimation) === null || _a === void 0 ? void 0 : _a.call(top));
|
|
499
|
+
top.onPause();
|
|
500
|
+
top.node.removeFromParent();
|
|
501
|
+
}
|
|
502
|
+
// 标记视图所属组并入栈
|
|
503
|
+
if ('__group__' in view) {
|
|
504
|
+
view.__group__ = group;
|
|
505
|
+
}
|
|
506
|
+
stack.push(view);
|
|
507
|
+
addChild(view.node);
|
|
508
|
+
this._adjustMaskLayer();
|
|
509
|
+
// 先执行onEnter初始化,再播放动画
|
|
510
|
+
view.onEnter(op.args);
|
|
511
|
+
// 播放打开动画
|
|
512
|
+
yield ((_b = view.onEnterAnimation) === null || _b === void 0 ? void 0 : _b.call(view));
|
|
513
|
+
return view;
|
|
514
|
+
}
|
|
515
|
+
finally {
|
|
516
|
+
// 隐藏等待视图
|
|
517
|
+
if (op.showLoading) {
|
|
518
|
+
this._hideLoading();
|
|
519
|
+
}
|
|
520
|
+
// 打开UI后,解除输入事件阻塞
|
|
521
|
+
this._blockInput(false);
|
|
522
|
+
}
|
|
523
|
+
});
|
|
227
524
|
}
|
|
228
|
-
|
|
525
|
+
_internalCloseAndPop(group, destroy) {
|
|
526
|
+
var _a;
|
|
229
527
|
return __awaiter(this, void 0, void 0, function* () {
|
|
528
|
+
const stack = this._groupStacks.get(group);
|
|
529
|
+
if (!stack || stack.length === 0) {
|
|
530
|
+
console.warn(`No stack or empty stack for group ${group}`);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
230
533
|
this._blockInput(true);
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
top
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
534
|
+
try {
|
|
535
|
+
// 移除当前栈顶视图
|
|
536
|
+
yield this._remove(stack.pop(), destroy);
|
|
537
|
+
// 恢复上一个视图
|
|
538
|
+
const top = stack[stack.length - 1];
|
|
539
|
+
if (top) {
|
|
540
|
+
top.onResume();
|
|
541
|
+
addChild(top.node);
|
|
542
|
+
// 播放恢复动画
|
|
543
|
+
yield ((_a = top.onEnterAnimation) === null || _a === void 0 ? void 0 : _a.call(top));
|
|
544
|
+
}
|
|
545
|
+
// 调整遮罩层级
|
|
546
|
+
this._adjustMaskLayer();
|
|
547
|
+
}
|
|
548
|
+
finally {
|
|
549
|
+
this._blockInput(false);
|
|
550
|
+
}
|
|
248
551
|
});
|
|
249
552
|
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
553
|
+
/**
|
|
554
|
+
* 移除视图
|
|
555
|
+
*/
|
|
556
|
+
_remove(viewKeyOrInstance, destroy, skipAnimation) {
|
|
557
|
+
var _a, _b;
|
|
558
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
559
|
+
// 如果是 string,从缓存中获取视图实例
|
|
560
|
+
if (typeof viewKeyOrInstance === 'string') {
|
|
561
|
+
const viewType = getViewClass(viewKeyOrInstance);
|
|
562
|
+
const cached = this._cache.get(viewType.name);
|
|
563
|
+
if (!cached) {
|
|
564
|
+
console.warn(`No cached view found for ${viewType.name}`);
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
const viewInstance = cached.getComponent(viewType);
|
|
568
|
+
if (!viewInstance) {
|
|
569
|
+
console.warn(`No view component found on node ${cached.name}`);
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
yield this._remove(viewInstance, destroy);
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
// 处理视图实例
|
|
576
|
+
const viewInstance = viewKeyOrInstance;
|
|
577
|
+
if ('__group__' in viewInstance) {
|
|
578
|
+
viewInstance.__group__ = undefined;
|
|
579
|
+
}
|
|
580
|
+
if (!skipAnimation) {
|
|
581
|
+
// * 播放关闭动画,使用async是必要的,因为:
|
|
582
|
+
// * 确保动画播放完成后再执行onExit和节点移除,不然还没播放动画了,UI就已经没了
|
|
583
|
+
yield ((_a = viewInstance.onExitAnimation) === null || _a === void 0 ? void 0 : _a.call(viewInstance));
|
|
584
|
+
}
|
|
585
|
+
viewInstance.onExit();
|
|
586
|
+
viewInstance.node.removeFromParent();
|
|
587
|
+
viewInstance.node.active = false;
|
|
588
|
+
if (destroy) {
|
|
589
|
+
let cacheKey = viewInstance.constructor.name;
|
|
590
|
+
(_b = this._cache.get(cacheKey)) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
591
|
+
this._cache.delete(cacheKey);
|
|
592
|
+
// 销毁被克隆出的UI后Node后,尝试释放 Prefab 资源
|
|
593
|
+
this._releasePrefab(viewInstance);
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
_releasePrefab(viewKey) {
|
|
598
|
+
try {
|
|
599
|
+
let prefabPath;
|
|
600
|
+
if (typeof viewKey === 'string') {
|
|
601
|
+
prefabPath = this._getPrefabPath(getViewClass(viewKey));
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
prefabPath = this._getPrefabPath(viewKey.constructor);
|
|
605
|
+
}
|
|
606
|
+
const ResMgr = ServiceLocator.getService('ResLoader');
|
|
607
|
+
ResMgr.release(prefabPath, Prefab);
|
|
259
608
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if (top) {
|
|
263
|
-
top.onResume();
|
|
264
|
-
addChild(top.node);
|
|
609
|
+
catch (error) {
|
|
610
|
+
console.error(`Failed to release prefab for ${viewKey}:`, error);
|
|
265
611
|
}
|
|
266
|
-
this._adjustMaskLayer();
|
|
267
612
|
}
|
|
268
|
-
|
|
269
|
-
|
|
613
|
+
_internalClearStack(group, destroy) {
|
|
614
|
+
const stack = this._groupStacks.get(group);
|
|
270
615
|
if (!stack) {
|
|
271
616
|
console.warn(`No stack found for group ${group}`);
|
|
272
617
|
return;
|
|
273
618
|
}
|
|
619
|
+
// 清空栈中所有视图,不播放动画
|
|
274
620
|
while (stack.length > 0) {
|
|
275
|
-
|
|
621
|
+
const view = stack.pop();
|
|
276
622
|
if (view) {
|
|
277
|
-
this._remove(view, destroy);
|
|
623
|
+
this._remove(view, destroy, true);
|
|
278
624
|
}
|
|
279
625
|
}
|
|
626
|
+
// 调整遮罩层级
|
|
627
|
+
this._adjustMaskLayer();
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* 关闭所有视图,不播放动画
|
|
631
|
+
*/
|
|
632
|
+
_internalCloseAll(destroy) {
|
|
633
|
+
const activeViews = this._getActiveViews();
|
|
634
|
+
for (const node of activeViews) {
|
|
635
|
+
const comps = node.components;
|
|
636
|
+
for (let i = 0; i < comps.length; i++) {
|
|
637
|
+
const comp = comps[i];
|
|
638
|
+
if ("__isIView__" in comp && comp.__isIView__) {
|
|
639
|
+
this._remove(comp, destroy, true);
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
// 清空所有栈
|
|
645
|
+
this._groupStacks.clear();
|
|
646
|
+
// 调整遮罩
|
|
647
|
+
this._adjustMaskLayer();
|
|
280
648
|
}
|
|
281
649
|
}
|
|
282
650
|
|
package/dist/mflow-tools.zip
CHANGED
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Color, Texture2D } from "cc";
|
|
1
|
+
import { Color, Texture2D, SpriteFrame } from "cc";
|
|
2
2
|
/**
|
|
3
3
|
* 图像工具
|
|
4
4
|
*/
|
|
@@ -30,4 +30,8 @@ export declare class ImageUtil {
|
|
|
30
30
|
* @param base64 Base64 字符
|
|
31
31
|
*/
|
|
32
32
|
static base64ToBlob(base64: string): Blob;
|
|
33
|
+
/**
|
|
34
|
+
* 创建纯色SpriteFrame用于遮罩
|
|
35
|
+
*/
|
|
36
|
+
static createSolidColorSpriteFrame(color?: Color): SpriteFrame;
|
|
33
37
|
}
|
package/dist/utils/ImageUtil.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Color, Texture2D } from 'cc';
|
|
1
|
+
import { Color, Texture2D, SpriteFrame, Rect } from 'cc';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* 图像工具
|
|
@@ -92,6 +92,30 @@ class ImageUtil {
|
|
|
92
92
|
}
|
|
93
93
|
return new Blob([uint8Array], { type: type });
|
|
94
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* 创建纯色SpriteFrame用于遮罩
|
|
97
|
+
*/
|
|
98
|
+
static createSolidColorSpriteFrame(color = new Color(0, 0, 0, 125)) {
|
|
99
|
+
// 创建一个1x1像素的纯色纹理
|
|
100
|
+
const texture = new Texture2D();
|
|
101
|
+
texture.reset({
|
|
102
|
+
width: 1,
|
|
103
|
+
height: 1,
|
|
104
|
+
format: Texture2D.PixelFormat.RGBA8888
|
|
105
|
+
});
|
|
106
|
+
// 设置像素数据
|
|
107
|
+
const pixelData = new Uint8Array(4);
|
|
108
|
+
pixelData[0] = color.r;
|
|
109
|
+
pixelData[1] = color.g;
|
|
110
|
+
pixelData[2] = color.b;
|
|
111
|
+
pixelData[3] = color.a;
|
|
112
|
+
texture.uploadData(pixelData);
|
|
113
|
+
// 创建SpriteFrame
|
|
114
|
+
const spriteFrame = new SpriteFrame();
|
|
115
|
+
spriteFrame.texture = texture;
|
|
116
|
+
spriteFrame.rect = new Rect(0, 0, 1, 1);
|
|
117
|
+
return spriteFrame;
|
|
118
|
+
}
|
|
95
119
|
}
|
|
96
120
|
|
|
97
121
|
export { ImageUtil };
|
package/package.json
CHANGED