keytops-game-framework 1.0.21 → 1.0.23

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/index.js CHANGED
@@ -6832,2007 +6832,2013 @@ class Task extends EventDispatcher {
6832
6832
  }
6833
6833
  Task.Event = Event$1;
6834
6834
 
6835
- /**
6836
- * 注册进入视图时,上报的事件名称,不注册则上报视图配置的name属性
6837
- * @param analyticsName 可选参数,默认为view的name,可以指定为上报的中文名称
6838
- * @param group 分组,默认为default,方便分类统计
6839
- * @returns
6840
- */
6841
- function viewAnalytics(analyticsName, group = "default") {
6842
- return function (target, propertyKey) {
6843
- viewManager.registerAnalytics(target, analyticsName, group);
6844
- };
6845
- }
6846
- /**
6847
- * 视图管理类
6848
- */
6849
- class ViewManager {
6850
- /**
6851
- * 注册视图上报事件的视图名称,一般为中文方便数据分辨
6852
- * @param viewClass
6853
- * @param analyticsName 可选参数,默认为view的name,可以指定为上报的中文名称
6854
- * @param group 分组,可选参数,默认为default,方便分类统计
6855
- */
6856
- registerAnalytics(viewClass, analyticsName, group = "default") {
6857
- let config = this._getViewConfig(viewClass);
6858
- if (config) {
6859
- this._viewAnalyticsMap.set(config.name, { name: analyticsName || config.name, group });
6860
- }
6861
- else {
6862
- throw new Error(`未获取到视图${viewClass}的配置`);
6835
+ class EffectAudioSourceProxy {
6836
+ constructor() {
6837
+ this._audioNode = null;
6838
+ this._audioSource = null;
6839
+ this._onFinishCallBack = null;
6840
+ this._audioNode = cc.find("__audioNode__");
6841
+ if (!this._audioNode) {
6842
+ this._audioNode = new cc.Node();
6843
+ this._audioNode.name = '__audioNode__';
6844
+ cc.director.getScene().addChild(this._audioNode);
6845
+ cc.director.addPersistRootNode(this._audioNode);
6863
6846
  }
6847
+ this._audioSource = this._audioNode.addComponent(cc.AudioSource);
6864
6848
  }
6865
6849
  /**
6866
- * 注册视图类对应的视图配置
6867
- * @param viewName 视图名称
6868
- * @param config 视图配置
6850
+ * 声音大小
6869
6851
  */
6870
- registerView(viewName, config) {
6871
- if (this._viewConfigMap.has(viewName)) {
6872
- throw new Error(`已经存在名称为${viewName}的View`);
6852
+ set volume(value) {
6853
+ if (!this._audioNode || !this._audioSource) {
6854
+ return;
6873
6855
  }
6874
- this._viewConfigMap.set(viewName, config);
6856
+ this._audioSource.volume = value;
6875
6857
  }
6876
6858
  /**
6877
- * 注册closeTouch默认遮罩视图
6878
- * @param config 视图配置|Class|名称
6859
+ * 声音大小
6879
6860
  */
6880
- registerMaskView(config) {
6881
- let viewConfig = this._getViewConfig(config);
6882
- if (!viewConfig) {
6883
- throw new Error(`未获取到默认遮罩视图${config}的配置`);
6861
+ get volume() {
6862
+ if (!this._audioNode || !this._audioSource) {
6863
+ return 1;
6884
6864
  }
6885
- this._defaultMaskViewConfig = viewConfig;
6886
- }
6887
- getViewConfig(config) {
6888
- return Object.assign({}, this._getViewConfig(config));
6865
+ return this._audioSource.volume;
6889
6866
  }
6890
6867
  /**
6891
- * [内部直接获取]获取视图配置
6892
- * @param config
6868
+ * 渐进式播放
6869
+ * @param clip
6870
+ * @param volume
6871
+ * @param loop
6872
+ * @param onFinishCallBack
6893
6873
  * @returns
6894
6874
  */
6895
- _getViewConfig(config) {
6896
- if (config == null) {
6897
- console.error(`[view] 获取视图配置,参数不能为空`);
6898
- return null;
6899
- }
6900
- if (typeof config == "string") {
6901
- if (!this._viewConfigMap.has(config)) {
6902
- console.error(`[view]${config}视图配置未注册`);
6903
- return null;
6904
- }
6905
- return this._viewConfigMap.get(config);
6906
- }
6907
- if ("path" in config) {
6908
- return config;
6909
- }
6910
- let values = this._viewConfigMap.values();
6911
- while (true) {
6912
- let item = values.next();
6913
- if (!item || item.done) {
6914
- return null;
6915
- }
6916
- if (item.value['viewClass'] == config) {
6917
- return item.value;
6918
- }
6919
- }
6875
+ playfade(clip, volume, loop = false, onFinishCallBack) {
6876
+ this.play(clip, volume, loop, onFinishCallBack);
6877
+ return this.fadeIn();
6920
6878
  }
6921
- constructor() {
6922
- this._viewConfigMap = new Map();
6923
- this._viewAnalyticsMap = new Map();
6924
- this._currentStack = [];
6925
- this._viewCache = new Map();
6926
- this._touchLocks = new Set();
6927
- if (ViewManager.created) {
6928
- throw new Error("ViewManager 是单例");
6929
- }
6930
- ViewManager.created = true;
6879
+ /**
6880
+ * 播放
6881
+ * @param clip
6882
+ * @param volume
6883
+ * @param loop
6884
+ * @param onFinishCallBack
6885
+ * @returns
6886
+ */
6887
+ play(clip, volume, loop = false, onFinishCallBack) {
6888
+ this._audioSource.clip = clip;
6889
+ this._audioSource.loop = loop;
6890
+ this._audioSource.volume = volume;
6891
+ this._audioSource.playOnAwake = false;
6892
+ this._onFinishCallBack = onFinishCallBack;
6893
+ this._audioNode.on(cc.AudioSource.EventType.ENDED, this._onAudioEnded, this);
6894
+ this._audioSource.play();
6895
+ return this;
6931
6896
  }
6932
6897
  /**
6933
- * 场景启动后设置场景,不建议手动调用
6934
- * @param root
6898
+ * 渐进
6899
+ * @returns
6935
6900
  */
6936
- setRoot(root) {
6937
- this._viewCache.forEach((views) => {
6938
- for (const view of views) {
6939
- view.allDestroy();
6940
- }
6901
+ fadeIn() {
6902
+ return new Promise((resolve) => {
6903
+ const targetVolume = this.volume;
6904
+ cc.tween(this).set({ volume: 0 }).to(0.2, { volume: targetVolume }).call(() => {
6905
+ resolve(this);
6906
+ }).start();
6941
6907
  });
6942
- this._viewCache.clear();
6943
- this._currentStack.length = 0;
6944
- this.event("open", root);
6945
- this.reportViewAnalytics("open", root);
6946
- this._scene = root;
6947
- this._currentView = null;
6948
- businessCenter && businessCenter.start(RewardVideoMaskBusiness);
6949
6908
  }
6950
6909
  /**
6951
- * 切换场景
6952
- * @param config 目标场景的配置|Class|名称
6953
- * @param data
6910
+ * 渐出
6911
+ * @param stop
6954
6912
  * @returns
6955
6913
  */
6956
- switchScene(config, ...data) {
6957
- return __awaiter(this, void 0, void 0, function* () {
6958
- config = this._getViewConfig(config);
6959
- let view = this.getCacheView(config);
6960
- if (view) {
6961
- view = yield this._switchView(view, ...data);
6962
- }
6963
- else {
6964
- view = yield this._switchViewByConfig(config, ...data);
6965
- }
6966
- // this.cacheView(view);
6967
- // this._currentStack.push(config);
6968
- return view;
6914
+ fedeOut(stop = true) {
6915
+ return new Promise((resolve) => {
6916
+ cc.tween(this).to(0.2, { volume: 0 }).call(() => {
6917
+ stop && this.stop();
6918
+ resolve(this);
6919
+ }).start();
6969
6920
  });
6970
6921
  }
6971
6922
  /**
6972
- * 当前场景
6923
+ * 暂停
6973
6924
  * @returns
6974
6925
  */
6975
- scene() {
6976
- return this._scene;
6926
+ pause() {
6927
+ if (!this._audioNode || !this._audioSource) {
6928
+ return;
6929
+ }
6930
+ this._audioSource.pause();
6977
6931
  }
6978
6932
  /**
6979
- * 开启当前场景的可交互性
6980
- * @param token closeTouch返回的触摸锁令牌。传入时仅释放该锁,所有锁释放后才会真正开启触摸;不传则清空全部触摸锁并开启触摸。
6933
+ * 恢复
6934
+ * @returns
6981
6935
  */
6982
- openTouch(token) {
6983
- if (token) {
6984
- this._touchLocks.delete(token);
6985
- }
6986
- else {
6987
- this._touchLocks.clear();
6936
+ resume() {
6937
+ if (!this._audioNode || !this._audioSource) {
6938
+ return;
6988
6939
  }
6989
- this._applyTouchState();
6940
+ this._audioSource.play();
6990
6941
  }
6991
6942
  /**
6992
- * 关闭当前场景的可交互性
6993
- * @param delay 可选参数,默认200毫秒,显示maskView的延迟时间。
6994
- * @param maskViewConfig 可选参数,默认为空白,要显示的遮罩试图。
6995
- * @returns 当前触摸锁令牌,可传给openTouch精确释放本次锁。
6943
+ * 渐进停止
6944
+ * @returns
6996
6945
  */
6997
- closeTouch(delay = 200, maskViewConfig = null) {
6998
- return __awaiter(this, void 0, void 0, function* () {
6999
- const token = this._lockTouch();
7000
- let config = maskViewConfig == null ? this._defaultMaskViewConfig : this._getViewConfig(maskViewConfig);
7001
- if (!config) {
7002
- return token;
7003
- }
7004
- if (delay <= 0) {
7005
- yield this._showMaskView(config, token);
7006
- return token;
7007
- }
7008
- timer.once(delay, this, this._showMaskView, [config, token]);
7009
- return token;
7010
- });
7011
- }
7012
- _lockTouch() {
7013
- const token = Symbol("touch-lock");
7014
- this._touchLocks.add(token);
7015
- this._applyTouchState();
7016
- return token;
6946
+ stopFade() {
6947
+ return this.fedeOut(true).then(() => { });
7017
6948
  }
7018
- _applyTouchState() {
7019
- const locked = this._touchLocks.size > 0;
7020
- if (!locked) {
7021
- timer.clear(this, this._showMaskView);
7022
- if (this._currentMaskView) {
7023
- this._currentMaskView.removeFromParent(true);
7024
- this._currentMaskView = null;
7025
- }
6949
+ /**
6950
+ * 停止
6951
+ * @returns
6952
+ */
6953
+ stop() {
6954
+ if (!this._audioNode || !this._audioSource) {
6955
+ return;
7026
6956
  }
7027
- if (this._scene) {
7028
- this._scene.touchEnabled = !locked;
6957
+ this._audioSource.stop();
6958
+ this._audioNode.off(cc.AudioSource.EventType.ENDED, this._onAudioEnded, this);
6959
+ }
6960
+ _onAudioEnded(audioSource) {
6961
+ if (audioSource == this._audioSource) {
6962
+ this._onFinishCallBack && this._onFinishCallBack();
6963
+ this._audioNode.off(cc.AudioSource.EventType.ENDED, this._onAudioEnded, this);
7029
6964
  }
7030
6965
  }
7031
- _showMaskView(maskViewConfig, token) {
7032
- return __awaiter(this, void 0, void 0, function* () {
7033
- if (!this._scene || !this._touchLocks.has(token) || this._currentMaskView) {
7034
- return;
7035
- }
7036
- let view = this.getCacheView(maskViewConfig);
7037
- if (view) {
7038
- view = yield this._addView(view, this._scene);
7039
- }
7040
- else {
7041
- view = yield this._loadViewByConfig(maskViewConfig, this._scene, "显示遮罩视图错误");
7042
- this.cacheView(view);
7043
- }
7044
- if (!this._touchLocks.has(token)) {
7045
- view.removeFromParent(true);
7046
- return;
7047
- }
7048
- this._currentMaskView = view;
7049
- timer.frameNext(this, () => {
7050
- if (this._currentMaskView == view && this._touchLocks.has(token)) {
7051
- view.setData();
7052
- }
7053
- });
7054
- });
6966
+ /**
6967
+ * 是否循环
6968
+ */
6969
+ get loop() {
6970
+ return this._audioSource ? this._audioSource.loop : false;
7055
6971
  }
7056
6972
  /**
7057
- * 当前主视图
7058
- * @returns
6973
+ * 是否播放中
7059
6974
  */
7060
- view() {
7061
- return this._currentView;
6975
+ get playing() {
6976
+ return this._audioSource ? this._audioSource.playing : false;
6977
+ }
6978
+ reset() {
6979
+ this.stop();
6980
+ cc.Tween.stopAllByTarget(this);
6981
+ this._onFinishCallBack = null;
6982
+ }
6983
+ destroy() {
6984
+ this._audioSource.destroy();
6985
+ this._audioSource = null;
6986
+ this._audioNode = null;
6987
+ this._onFinishCallBack = null;
6988
+ }
6989
+ }
6990
+ class AudioManager {
6991
+ constructor() {
6992
+ this._musicVolume = null;
6993
+ this._effectVolume = null;
6994
+ this._effectPool = new Pool(EffectAudioSourceProxy); //cc.AudioSource.maxAudioChannel - 1);
7062
6995
  }
7063
6996
  /**
7064
- * 切换主视图到目标视图
7065
- * @param target 目标视图的配置|Class|名称
7066
- * @param datas 目标视图初始化参数
6997
+ * 音效音量大小
7067
6998
  */
7068
- switchTo(config, ...data) {
7069
- return __awaiter(this, void 0, void 0, function* () {
7070
- config = this._getViewConfig(config);
7071
- if (!this._scene) {
7072
- throw new Error("根View不存在");
7073
- }
7074
- let view = this.getCacheView(config);
7075
- if (view) {
7076
- view = yield this._switchView(view, ...data);
7077
- }
7078
- else {
7079
- view = yield this._switchViewByConfig(config, ...data);
7080
- }
7081
- this.cacheView(view);
7082
- this._currentStack.push(config);
7083
- return view;
7084
- });
6999
+ get effectVolume() {
7000
+ if (this._effectVolume == null) {
7001
+ this._effectVolume = storage.effectsVolume;
7002
+ }
7003
+ return this._effectVolume;
7085
7004
  }
7086
7005
  /**
7087
- * 切回当前主视图到上一个主视图。
7088
- * @param data
7089
- * @returns
7006
+ * 音效音量大小
7090
7007
  */
7091
- switchBack(...data) {
7092
- return __awaiter(this, void 0, void 0, function* () {
7093
- if (this._currentStack.length == 0) {
7094
- console.warn("上一个视图不存在");
7095
- return null;
7096
- }
7097
- //移除当前视图config
7098
- this._currentStack.pop();
7099
- //获取之前视图config
7100
- let lastViewConfig = this._currentStack.pop();
7101
- if (!lastViewConfig) {
7102
- console.warn("上个主视图为空,");
7103
- return null;
7104
- }
7105
- return yield this.switchTo(lastViewConfig, ...data);
7106
- });
7008
+ set effectVolume(value) {
7009
+ storage.effectsVolume = this._effectVolume = value;
7010
+ let usingEffect = this._effectPool['_usingArray'];
7011
+ usingEffect = usingEffect.concat();
7012
+ for (let i = 0; i < usingEffect.length; i++) {
7013
+ const effectProxy = usingEffect[i];
7014
+ effectProxy.volume = value;
7015
+ }
7107
7016
  }
7108
- _switchView(view, ...datas) {
7109
- return __awaiter(this, void 0, void 0, function* () {
7110
- this._currentView && this._currentView.removeFromParent(!this._currentView.config.cache);
7111
- this._currentView = null;
7112
- this._currentView = view;
7113
- this._scene.addView(this._currentView, view.config.zIndex);
7114
- this._currentView.setData(...datas);
7115
- return this._currentView;
7116
- });
7017
+ /**
7018
+ * BGM音量大小
7019
+ */
7020
+ get musicVolume() {
7021
+ if (this._musicVolume == null) {
7022
+ this._musicVolume = storage.musicVolume;
7023
+ }
7024
+ return this._musicVolume;
7117
7025
  }
7118
- _switchViewByConfig(viewConfig, ...datas) {
7119
- return __awaiter(this, void 0, void 0, function* () {
7120
- const touchToken = this._lockTouch();
7121
- let loader;
7122
- try {
7123
- loader = yield this.loadView(viewConfig);
7124
- }
7125
- catch (error) {
7126
- this.openTouch(touchToken);
7127
- console.error(`切换视图错误 code:${error.code}, msg:${error.msg || error.message}`);
7128
- throw new Error(`切换视图错误 code:${error.code}, msg:${error.msg || error.message}`);
7129
- }
7130
- let name = viewConfig.name || viewConfig.path.substring(viewConfig.path.lastIndexOf("/") + 1);
7131
- this._currentView && this._currentView.removeFromParent(!this._currentView.config.cache);
7132
- this._currentView = null;
7133
- let view = loader.initView(name, true);
7134
- view.name = name;
7135
- view.config = viewConfig;
7136
- view.analyticsInfo = this._viewAnalyticsMap.get(viewConfig.name);
7137
- if (view['addToBG']) {
7138
- this.setRoot(view);
7139
- }
7140
- else {
7141
- this._currentView = view;
7142
- this._scene.addView(view, viewConfig.zIndex);
7143
- }
7144
- timer.frameNext(this, () => {
7145
- this.openTouch(touchToken);
7146
- view.setData(...datas);
7147
- });
7148
- return view;
7149
- });
7026
+ /**
7027
+ * BGM音量大小
7028
+ */
7029
+ set musicVolume(value) {
7030
+ storage.musicVolume = this._musicVolume = value;
7031
+ this._music && (this._music.volume = value);
7150
7032
  }
7151
7033
  /**
7152
- * 通过视图配置添加视图
7153
- * @param viewConfig 要添加的视图配置|Class|名称
7154
- * @param parent 父级视图,默认为当前主视图,即viewMananger.current()
7155
- * @param datas 传入setData的数据
7034
+ * 播放音效
7035
+ * @param clip
7036
+ * @param loop 是否循环,默认为false。传入true,需要手动调用recoverEffect进行回收
7037
+ * @param onFinishCallBack 播放完回调
7038
+ * @param volume 当此播放的声音大小,默认为统一effect的大小
7156
7039
  */
7157
- addView(config, parent, ...datas) {
7158
- return __awaiter(this, void 0, void 0, function* () {
7159
- config = this._getViewConfig(config);
7160
- parent = parent || this._currentView;
7161
- if (!parent) {
7162
- throw new Error("当前View不存在, 且未传入父级视图");
7163
- }
7164
- let view = this.getCacheView(config);
7165
- if (view) {
7166
- view = yield this._addView(view, parent, ...datas);
7040
+ playEffect(clip, loop = false, onFinishCallBack, volume = null) {
7041
+ const effectProxy = this._effectPool.allocate();
7042
+ effectProxy.play(clip, volume == null ? this.effectVolume : volume, loop, () => {
7043
+ if (!loop) {
7044
+ this.recoverEffect(effectProxy);
7167
7045
  }
7168
7046
  else {
7169
- view = yield this._addViewbyConfig(config, parent, ...datas);
7170
- }
7171
- this.cacheView(view);
7172
- return view;
7173
- });
7174
- }
7175
- _addView(view, parent, ...datas) {
7176
- return __awaiter(this, void 0, void 0, function* () {
7177
- parent.addView(view, view.config.zIndex);
7178
- view.setData(...datas);
7179
- return view;
7180
- });
7181
- }
7182
- _addViewbyConfig(config, parent, ...datas) {
7183
- return __awaiter(this, void 0, void 0, function* () {
7184
- const touchToken = this._lockTouch();
7185
- let view;
7186
- try {
7187
- view = yield this._loadViewByConfig(config, parent, "添加视图错误");
7188
- }
7189
- catch (error) {
7190
- this.openTouch(touchToken);
7191
- throw error;
7192
- }
7193
- timer.frameNext(this, () => {
7194
- this.openTouch(touchToken);
7195
- view.setData(...datas);
7196
- });
7197
- return view;
7198
- });
7199
- }
7200
- _loadViewByConfig(config, parent, errorPrefix) {
7201
- return __awaiter(this, void 0, void 0, function* () {
7202
- let loader;
7203
- try {
7204
- loader = yield this.loadView(config);
7205
- }
7206
- catch (error) {
7207
- console.error(`${errorPrefix} code:${error.code}, msg:${error.msg || error.message}`);
7208
- throw new Error(`${errorPrefix} code:${error.code}, msg:${error.msg || error.message}`);
7047
+ console.warn("需要手动回收音效播放器");
7209
7048
  }
7210
- let name = config.name || config.path.substring(config.path.lastIndexOf("/") + 1);
7211
- let view = loader.initView(name, true);
7212
- view.name = name;
7213
- view.config = config;
7214
- view.analyticsInfo = this._viewAnalyticsMap.get(config.name);
7215
- parent.addView(view, config.zIndex);
7216
- return view;
7049
+ onFinishCallBack && onFinishCallBack();
7217
7050
  });
7051
+ return effectProxy;
7218
7052
  }
7219
7053
  /**
7220
- * 加载视图到内存,之后还需要调用loader.initView接口获取view
7221
- * @param config 要加载的视图配置|Class|名称
7222
- * @param task 任务载体
7054
+ * 回收音效播放器,用于effect多次复用后的回收
7055
+ * @param proxy
7223
7056
  */
7224
- loadView(config, task) {
7225
- return new Promise((resolve, reject) => {
7226
- config = this._getViewConfig(config);
7227
- let loader = this.impl.getLoader(config).init(config);
7228
- task = task || new Task();
7229
- task.once(Task.Event.COMPLETE, this, () => {
7230
- resolve(loader);
7231
- });
7232
- task.once(Task.Event.ERROR, this, (type, task, reason) => {
7233
- reject(reason);
7234
- });
7235
- loader.load(task);
7236
- });
7057
+ recoverEffect(proxy) {
7058
+ this._effectPool.recover(proxy);
7237
7059
  }
7238
7060
  /**
7239
- * 获取缓存视图
7240
- * @param config
7241
- * @returns
7061
+ * 停止所有音效
7242
7062
  */
7243
- getCacheView(config) {
7244
- config = this._getViewConfig(config);
7245
- let views = this._viewCache.get(config.path);
7246
- if (!views || views.length == 0) {
7247
- return null;
7248
- }
7249
- for (const view of views) {
7250
- if (!view.inStage) {
7251
- return view;
7252
- }
7063
+ stopAllEffect() {
7064
+ let usingEffect = this._effectPool['_usingArray'];
7065
+ usingEffect = usingEffect.concat();
7066
+ for (let i = 0; i < usingEffect.length; i++) {
7067
+ const effectProxy = usingEffect[i];
7068
+ effectProxy.stop();
7069
+ this.recoverEffect(effectProxy);
7253
7070
  }
7254
- return null;
7255
7071
  }
7256
7072
  /**
7257
- * 缓存视图
7258
- * @param view
7259
- * @param force 默认为false,不考虑view.config.cache的值,强制增加到缓存
7260
- * @returns
7073
+ * 播放背景音乐
7074
+ * @param clip
7075
+ * @param fade 是否渐进式播放,默认为true
7076
+ * @param loop 是否循环,默认为true
7077
+ * @param onFinishCallBack 播放完回调
7078
+ * @param volume 当此播放的声音大小,默认为统一music的大小
7261
7079
  */
7262
- cacheView(view, force = false) {
7263
- if (!view || !view.config || (!view.config.cache && !force)) {
7264
- return false;
7265
- }
7266
- let views = this._viewCache.get(view.config.path);
7267
- if (!views) {
7268
- views = [view];
7269
- this._viewCache.set(view.config.path, views);
7270
- return true;
7271
- }
7272
- else if (!views.includes(view)) {
7273
- views.push(view);
7274
- return true;
7080
+ playMusic(clip, fade = true, loop = true, onFinishCallBack, volume = null) {
7081
+ if (!this._music) {
7082
+ this._music = new EffectAudioSourceProxy();
7275
7083
  }
7276
- return false;
7084
+ this._music.playing && this._music.stop();
7085
+ volume = volume == null ? this.musicVolume : volume;
7086
+ fade ? this._music.playfade(clip, volume, loop, onFinishCallBack) : this._music.play(clip, volume, loop, onFinishCallBack);
7277
7087
  }
7278
7088
  /**
7279
- * 从缓存列表中移除视图。
7280
- * @param view
7089
+ * 暂停播放背景音乐
7281
7090
  * @returns
7282
7091
  */
7283
- cancelCache(view) {
7284
- if (!view || !view.config || !view.config.path) {
7285
- return false;
7286
- }
7287
- let views = this._viewCache.get(view.config.path);
7288
- if (!views || views.length == 0) {
7289
- return false;
7290
- }
7291
- const index = views.indexOf(view);
7292
- if (index != -1) {
7293
- views.splice(index, 1);
7294
- return true;
7092
+ pauseMusic() {
7093
+ if (!this._music) {
7094
+ return;
7295
7095
  }
7296
- return false;
7297
- }
7298
- /**
7299
- * [快捷方式]增加视图打开的事件监听
7300
- * @param targetView 目标视图Class|名称
7301
- * @param listener 响应回调
7302
- * @param caller 回调this
7303
- * @param once 是否只监听对应视图打开一次。
7304
- * @param args 其他参数
7305
- */
7306
- onViewOpen(targetView, caller, listener, once = false, ...args) {
7307
- this._viewOpenEventMap = this._viewOpenEventMap || new Map();
7308
- this._viewOpenEventMap.set(listener, { event: "open", viewConfig: this._getViewConfig(targetView), caller, listener, once, args });
7309
- this.offViewEvent("open", this, this._onViewOpen);
7310
- this.onViewEvent("open", this, this._onViewOpen);
7311
- }
7312
- _onViewOpen(viewName, ...args) {
7313
- this._viewOpenEventMap.forEach((value, key) => {
7314
- if (value.viewConfig.name == viewName) {
7315
- value.listener.apply(value.caller, value.args);
7316
- value.once && this._viewOpenEventMap.delete(key);
7317
- }
7318
- });
7096
+ this._music.pause();
7319
7097
  }
7320
7098
  /**
7321
- * [快捷方式]移除视图打开的事件监听
7322
- * @param listener
7099
+ * 恢复播放背景音乐
7323
7100
  * @returns
7324
7101
  */
7325
- offViewOpen(listener) {
7326
- return this._viewOpenEventMap && this._viewOpenEventMap.delete(listener);
7327
- }
7328
- /**
7329
- * [快捷方式]增加视图关闭的事件监听
7330
- * @param targetView 目标视图Class|名称
7331
- * @param listener 响应回调
7332
- * @param caller 回调this
7333
- * @param once 是否只监听对应视图关闭一次。
7334
- * @param args 其他参数
7335
- */
7336
- onViewClose(targetView, caller, listener, once = false, ...args) {
7337
- this._viewCloseEventMap = this._viewCloseEventMap || new Map();
7338
- this._viewCloseEventMap.set(listener, { event: "close", viewConfig: this._getViewConfig(targetView), caller, listener, once, args });
7339
- this.offViewEvent("close", this, this._onViewClose);
7340
- this.onViewEvent("close", this, this._onViewClose);
7102
+ resumeMusic() {
7103
+ if (!this._music) {
7104
+ return;
7105
+ }
7106
+ this._music.resume();
7341
7107
  }
7342
7108
  /**
7343
- * [快捷方式]移除视图关闭的事件监听
7344
- * @param listener
7109
+ * 停止背景音乐
7110
+ * @param fade 是否渐出
7345
7111
  * @returns
7346
7112
  */
7347
- offViewClose(listener) {
7348
- return this._viewCloseEventMap && this._viewCloseEventMap.delete(listener);
7349
- }
7350
- _onViewClose(viewName, ...args) {
7351
- this._viewCloseEventMap.forEach((value, key) => {
7352
- if (value.viewConfig.name == viewName) {
7353
- value.listener.apply(value.caller, value.args.concat(args));
7354
- value.once && this._viewCloseEventMap.delete(key);
7113
+ stopMusic(fade = true) {
7114
+ return __awaiter(this, void 0, void 0, function* () {
7115
+ if (!this._music) {
7116
+ return;
7355
7117
  }
7118
+ return fade ? yield this._music.stopFade() : this._music.stop();
7356
7119
  });
7357
7120
  }
7358
- /**
7359
- * 增加视图事件监听
7360
- * @param event 视图事件名称
7361
- * @param caller 回调this
7362
- * @param listener 响应回调
7363
- * @param once 是否只监听对应视图关闭一次。
7364
- * @param args 其他参数
7365
- */
7366
- onViewEvent(event, caller, listener, once = false, ...args) {
7367
- if (!this._viewEvent) {
7368
- this._viewEvent = new EventDispatcher();
7369
- }
7370
- if (once) {
7371
- this._viewEvent.once(event, caller, listener, args);
7372
- }
7373
- else {
7374
- this._viewEvent.on(event, caller, listener, args);
7375
- }
7376
- }
7377
- /**
7378
- * 移除视图事件监听
7379
- * @param event 事件类型
7380
- * @param caller 事件侦听函数的执行域。
7381
- * @param listener 事件侦听函数。
7382
- * @returns
7383
- */
7384
- offViewEvent(event, caller, listener) {
7385
- if (!this._viewEvent) {
7386
- return;
7387
- }
7388
- this._viewEvent.off(event, caller, listener);
7389
- }
7390
- /**
7391
- * 广播视图事件
7392
- * @param event 视图事件
7393
- * @param view 视图
7394
- */
7395
- event(event, view, ...args) {
7396
- if (this._viewEvent) {
7397
- args.unshift(view.name);
7398
- this._viewEvent.event(event, args);
7399
- }
7400
- }
7401
- /**
7402
- * 上报视图统计数据(open\close\...)
7403
- * @param event 事件
7404
- * @param view 视图
7405
- * @param data 上报附带信息
7406
- */
7407
- reportViewAnalytics(event, view, data) {
7408
- let maskViewconfig = this._currentMaskView && this._currentMaskView.config;
7409
- if (maskViewconfig && view.name == maskViewconfig.name) {
7410
- return;
7411
- }
7412
- let analyticsInfo = this._viewAnalyticsMap.get(view.name);
7413
- if (analyticsInfo) {
7414
- data = data || (view.getAnalyticsDataOnViewEvent && view.getAnalyticsDataOnViewEvent(event)) || {};
7415
- data.action = event;
7416
- data.name = analyticsInfo.name;
7417
- data.group = analyticsInfo.group || "default";
7418
- //open:进入时刻, close:停留时长
7419
- data.stay = Math.floor((App.durationOfLaunch - (analyticsInfo.lastOpenTime || 0)) / 1000);
7420
- analytics.base.report("view", data);
7421
- analyticsInfo.lastOpenTime = event == "open" ? App.durationOfLaunch : 0;
7422
- }
7423
- }
7424
- get impl() {
7425
- if (this._impl == null) {
7426
- this._impl = Injector.getInject(ViewManager.KEY);
7427
- }
7428
- if (this._impl == null) {
7429
- throw new Error(ViewManager.KEY + "未注入!");
7430
- }
7431
- return this._impl;
7432
- }
7433
7121
  }
7434
- ViewManager.KEY = "ViewManager";
7435
- ViewManager.created = false;
7436
7122
  /**
7437
- * 视图管理器
7123
+ * 音效管理
7438
7124
  */
7439
- const viewManager = new ViewManager();
7125
+ const audio = new AudioManager();
7440
7126
 
7441
- class INativeHelper {
7127
+ const GC_CD = 60 * 1000;
7128
+ /**
7129
+ * 资源管理
7130
+ */
7131
+ class ResourceManager {
7442
7132
  constructor() {
7443
- this._callbackCount = 0;
7444
- }
7445
- call(className, methodName, ...args) {
7446
- this.callWithReturnType(className, methodName, null, ...args);
7133
+ this._loadPromiseMap = new Map();
7134
+ this._loadBunldePromiseMap = new Map();
7135
+ this._waitToReleaseBundles = new Map();
7136
+ this._startedGC = false;
7447
7137
  }
7448
- registerCallback(caller, method, times, ...args) {
7449
- if (caller == null || method == null) {
7450
- return "";
7451
- }
7452
- ++this._callbackCount;
7453
- let callbackName = 'NativeHelper.callback' + this._callbackCount;
7454
- window[callbackName] = () => {
7455
- method.apply(caller, args);
7456
- if (--times <= 0) {
7457
- window[callbackName] = null;
7458
- }
7459
- };
7460
- return `window['${callbackName}']()`;
7138
+ preload(path, task) {
7139
+ return __awaiter(this, void 0, void 0, function* () {
7140
+ let paths = typeof path === "string" ? [path] : path;
7141
+ let pathInfoList = paths.map((value) => {
7142
+ return this.parsePath(value);
7143
+ });
7144
+ let loadPromiseList = pathInfoList.map((info) => {
7145
+ return !info.isRemote ? this._preLoadFromBundle(info, task) : this._preLoadFromRemote(info, task);
7146
+ });
7147
+ return Promise.all(loadPromiseList).then((value) => {
7148
+ return value.length == 1 ? value[0] : value;
7149
+ });
7150
+ });
7461
7151
  }
7462
- }
7463
-
7464
- /**
7465
- @name: NativeHelper
7466
- @desc: cocos 2.4.x 下nativeHelper
7467
- @author: timoo
7468
- @date: 2022/12/02
7469
- */
7470
- class CocosNativeHelper extends INativeHelper {
7471
- callWithReturnType(className, methodName, returnType, ...args) {
7472
- if (!className) {
7473
- return;
7152
+ preLoadScene(path, task) {
7153
+ return __awaiter(this, void 0, void 0, function* () {
7154
+ let paths = typeof path === "string" ? [path] : path;
7155
+ let pathInfoList = paths.map((value) => {
7156
+ let result = this.parsePath(value);
7157
+ if (result.isRemote) {
7158
+ throw new Error(`不支持从远程预加载场景${value}`);
7159
+ }
7160
+ return result;
7161
+ });
7162
+ let loadPromiseList = pathInfoList.map((info) => __awaiter(this, void 0, void 0, function* () {
7163
+ let bundle = yield this.getBundle(info.bundle);
7164
+ return new Promise((resolve) => {
7165
+ if (!bundle) {
7166
+ resolve(null);
7167
+ task && task.onError("preLoadScene", `加载${info.fullPath}时 bundle${info.bundle}加载失败`);
7168
+ }
7169
+ bundle.preloadScene(info.path, (error) => {
7170
+ if (error) {
7171
+ resolve(null);
7172
+ task && task.onError(error.name, error.message);
7173
+ }
7174
+ resolve(info.fullPath);
7175
+ task && task.onComplete();
7176
+ });
7177
+ });
7178
+ }));
7179
+ return Promise.all(loadPromiseList).then((value) => {
7180
+ return value.length > 0 ? (value.length == 1 ? value[0] : value) : null;
7181
+ });
7182
+ });
7183
+ }
7184
+ _preLoadFromRemote(info, task) {
7185
+ return __awaiter(this, void 0, void 0, function* () {
7186
+ if (!info.isRemote) {
7187
+ throw new Error(`${info.fullPath}不是远程资源地址`);
7188
+ }
7189
+ if (info.isDir) {
7190
+ throw new Error(`不支持对远程Dir的加载`);
7191
+ }
7192
+ return new Promise((resolve, reject) => {
7193
+ cc.assetManager.loadRemote(info.fullPath, (error, asset) => {
7194
+ if (error) {
7195
+ resolve(null);
7196
+ task && task.onError(error.name, error.message);
7197
+ }
7198
+ else {
7199
+ resolve(info.fullPath);
7200
+ task && task.onComplete();
7201
+ }
7202
+ });
7203
+ });
7204
+ });
7205
+ }
7206
+ _preLoadFromBundle(info, task) {
7207
+ return __awaiter(this, void 0, void 0, function* () {
7208
+ let bundle = yield this.getBundle(info.bundle);
7209
+ if (!bundle) {
7210
+ task && task.onError("_preLoadFromBundle", `加载${info.fullPath}时 bundle${info.bundle}加载失败`);
7211
+ return null;
7212
+ }
7213
+ return new Promise((resolve) => {
7214
+ let onProgress = (finish, total) => {
7215
+ task && task.onProgress(finish / total);
7216
+ };
7217
+ let onComplete = (error) => {
7218
+ if (error) {
7219
+ resolve(null);
7220
+ task && task.onError(error.name, error.message);
7221
+ }
7222
+ else {
7223
+ resolve(info.fullPath);
7224
+ task && task.onComplete();
7225
+ }
7226
+ };
7227
+ if (info.path == "") {
7228
+ onComplete(null);
7229
+ }
7230
+ else if (!info.isDir) {
7231
+ bundle.preload(info.path, onProgress, onComplete);
7232
+ }
7233
+ else {
7234
+ bundle.preloadDir(info.path, onProgress, onComplete);
7235
+ }
7236
+ });
7237
+ });
7238
+ }
7239
+ load(path, assetType, task) {
7240
+ return __awaiter(this, void 0, void 0, function* () {
7241
+ let paths = typeof path === "string" ? [path] : path;
7242
+ let pathInfoList = paths.map((value) => {
7243
+ return this.parsePath(value);
7244
+ });
7245
+ let result = [];
7246
+ for (const info of pathInfoList) {
7247
+ if (!info.isRemote) {
7248
+ let assets = yield this._loadFromBundle(info, assetType, task);
7249
+ if (assets instanceof cc.Asset) {
7250
+ result.push(assets);
7251
+ }
7252
+ else {
7253
+ result.push(...assets);
7254
+ }
7255
+ continue;
7256
+ }
7257
+ let asset = yield this._loadfromRemote(info, task);
7258
+ if (assetType === cc.SpriteFrame) {
7259
+ asset = cc.SpriteFrame.createWithImage(asset);
7260
+ }
7261
+ result.push(asset);
7262
+ }
7263
+ return typeof path === "string" ? result[0] : result;
7264
+ });
7265
+ }
7266
+ _loadFromBundle(info, type, task) {
7267
+ return __awaiter(this, void 0, void 0, function* () {
7268
+ let bundle = yield this.getBundle(info.bundle);
7269
+ if (!bundle) {
7270
+ task && task.onError("_loadFromBundle", `加载${info.fullPath}时 bundle${info.bundle}加载失败`);
7271
+ return null;
7272
+ }
7273
+ if (this._loadPromiseMap.has(info.fullPath)) {
7274
+ return this._loadPromiseMap.get(info.fullPath).then((value) => {
7275
+ task && task.onComplete();
7276
+ return value;
7277
+ });
7278
+ }
7279
+ let promise = new Promise((resolve) => {
7280
+ let onProgress = (finish, total) => {
7281
+ task && task.onProgress(finish / total);
7282
+ };
7283
+ let onComplete = (error, assets) => {
7284
+ if (error) {
7285
+ console.error("加载资源发生错误", info, error);
7286
+ resolve(null);
7287
+ task && task.onError(error.name, error.message);
7288
+ }
7289
+ else {
7290
+ resolve(assets);
7291
+ task && task.onComplete();
7292
+ }
7293
+ };
7294
+ if (info.path == "") {
7295
+ onComplete(null, null);
7296
+ }
7297
+ else if (!info.isDir) {
7298
+ bundle.load(info.path, type, onProgress, onComplete);
7299
+ }
7300
+ else {
7301
+ bundle.loadDir(info.path, type, onProgress, onComplete);
7302
+ }
7303
+ });
7304
+ this._loadPromiseMap.set(info.fullPath, promise);
7305
+ return promise;
7306
+ });
7307
+ }
7308
+ _loadfromRemote(info, task) {
7309
+ if (!info.isRemote) {
7310
+ throw new Error(`${info.fullPath}不是远程资源地址`);
7474
7311
  }
7475
- if (cc.sys.os == cc.sys.OS.ANDROID) {
7476
- let argsSig = this.getArgsSig(args, returnType);
7477
- console.log("callWithReturnType:", className, methodName, argsSig, args.join(","));
7478
- return cc.native.reflection.callStaticMethod(className, methodName, argsSig, ...args);
7312
+ if (info.isDir) {
7313
+ throw new Error(`不支持对远程Dir的加载`);
7479
7314
  }
7480
- else if (cc.sys.os == cc.sys.OS.IOS) ;
7315
+ if (this._loadPromiseMap.has(info.fullPath)) {
7316
+ return this._loadPromiseMap.get(info.fullPath);
7317
+ }
7318
+ let promise = new Promise((resolve) => {
7319
+ cc.assetManager.loadRemote(info.fullPath, (error, asset) => {
7320
+ if (error) {
7321
+ resolve(null);
7322
+ task && task.onError(error.name, error.message);
7323
+ }
7324
+ else {
7325
+ resolve(asset);
7326
+ task && task.onComplete();
7327
+ }
7328
+ });
7329
+ });
7330
+ this._loadPromiseMap.set(info.fullPath, promise);
7331
+ return promise;
7481
7332
  }
7482
- getArgsSig(args, returnType) {
7483
- let sig = ["("];
7484
- for (let i = 0; i < args.length; i++) {
7485
- const element = args[i];
7486
- sig.push(this.getValueTypeStr(element));
7333
+ /**
7334
+ * 解析资源路径成PathInfo
7335
+ * @param fullPath - 资源路径应该符合bundle://cc.path/to/resource格式
7336
+ * @returns - `PathInfo` 资源信息
7337
+ */
7338
+ parsePath(fullPath) {
7339
+ let tempPath = fullPath.split("://");
7340
+ if (tempPath.length != 2) {
7341
+ throw new Error(`资源地址${fullPath}格式错误,应该符合bundle://cc.path/to/resource格式`); //.suffix
7487
7342
  }
7488
- sig.push(")", this.getValueTypeStr(returnType));
7489
- return sig.join("");
7343
+ let isDir = fullPath.charAt(fullPath.length - 1) == "/";
7344
+ let bundle = tempPath[0];
7345
+ let isRemote = bundle === "https" || bundle === "http";
7346
+ let suffix = cc.path.extname(fullPath).toLowerCase();
7347
+ let simplePath = suffix == "" ? tempPath[1] : tempPath[1].replace(suffix, "");
7348
+ return { bundle, path: simplePath, isRemote, isDir, fullPath };
7490
7349
  }
7491
- getValueTypeStr(value) {
7492
- if (value == null || value == undefined) {
7493
- return "V";
7350
+ /**
7351
+ * 获取目标bundle的资源的完整路径
7352
+ * @param bundleName - 资源所在bundle名称
7353
+ * @param subPath - 可选参数, 相对子路径
7354
+ * @returns
7355
+ */
7356
+ getPath(bundleName, subPath = "") {
7357
+ if (subPath.includes("://")) {
7358
+ return subPath;
7494
7359
  }
7495
- let typeStr = typeof value;
7496
- if (typeStr == "number") {
7497
- if (value % 1 != 0) { //float
7498
- return "F";
7360
+ return `${bundleName}://${subPath}`;
7361
+ }
7362
+ /**
7363
+ * 获取主包下资源的完整路径
7364
+ * @param subPath 资源子路径
7365
+ * @returns
7366
+ */
7367
+ getPathInMain(subPath) {
7368
+ return this.getPath("main", subPath);
7369
+ }
7370
+ /**
7371
+ * 获取动态包下资源的完整路径
7372
+ * @param subPath 资源子路径
7373
+ * @returns
7374
+ */
7375
+ getPathInResources(subPath) {
7376
+ return this.getPath("resources", subPath);
7377
+ }
7378
+ /**
7379
+ * 加载Bundle
7380
+ * @param bundleName Bundle名称
7381
+ * @returns `Promise<cc.AssetManager.Bundle>`
7382
+ */
7383
+ getBundle(bundleName) {
7384
+ this._removeBundleFromReleaseMap(bundleName);
7385
+ this._startGC();
7386
+ if (this._loadBunldePromiseMap.has(bundleName)) {
7387
+ return this._loadBunldePromiseMap.get(bundleName);
7388
+ }
7389
+ let promise = new Promise((resolve) => {
7390
+ let bundle = cc.assetManager.getBundle(bundleName);
7391
+ if (!!bundle) {
7392
+ resolve(bundle);
7393
+ return;
7499
7394
  }
7500
- else {
7501
- return "I";
7395
+ let version = this._getBundleVersion(bundleName);
7396
+ cc.assetManager.loadBundle(bundleName, version ? { version: version } : null, (error, bundle) => {
7397
+ if (error) {
7398
+ console.error(`LoadBundle ${bundleName} Error:`, error);
7399
+ resolve(null);
7400
+ }
7401
+ else {
7402
+ resolve(bundle);
7403
+ }
7404
+ });
7405
+ });
7406
+ this._loadBunldePromiseMap.set(bundleName, promise);
7407
+ return promise;
7408
+ }
7409
+ /**
7410
+ * bundle是否被加载过,且有效
7411
+ * @param bundleName
7412
+ * @returns
7413
+ */
7414
+ isBundleValid(bundleName) {
7415
+ return __awaiter(this, void 0, void 0, function* () {
7416
+ if (this._loadBunldePromiseMap.has(bundleName)) {
7417
+ let bundle = yield this._loadBunldePromiseMap.get(bundleName);
7418
+ return bundle && cc.isValid(bundle);
7502
7419
  }
7503
- }
7504
- else if (typeStr == "boolean") {
7505
- return "Z";
7506
- }
7507
- else if (typeStr == "string") {
7508
- return "Ljava/lang/String;";
7420
+ return false;
7421
+ });
7422
+ }
7423
+ /**
7424
+ * 移除并销毁bundle,以及bundle下所有资源。
7425
+ * @param bundleName bundle名称
7426
+ * @param delay 是否延后销毁,默认为true
7427
+ * @returns
7428
+ */
7429
+ removeBundle(bundleName, delay = true) {
7430
+ return __awaiter(this, void 0, void 0, function* () {
7431
+ if (this.isBundleValid(bundleName)) {
7432
+ let bundle = yield this._loadBunldePromiseMap.get(bundleName);
7433
+ if (delay) {
7434
+ this._pushBundleToReleaseMap(bundle);
7435
+ return true;
7436
+ }
7437
+ return this._onRemoveBundle(bundle);
7438
+ }
7439
+ return false;
7440
+ });
7441
+ }
7442
+ /**
7443
+ * 推送bundle到GC队列
7444
+ * @param bundle
7445
+ */
7446
+ _pushBundleToReleaseMap(bundle) {
7447
+ if (bundle && cc.isValid(bundle)) {
7448
+ console.log(`[GCBundle] Pre Remove Bundle:${bundle.name}`);
7449
+ this._waitToReleaseBundles.set(bundle.name, bundle);
7509
7450
  }
7510
7451
  }
7511
- }
7512
-
7513
- /*
7514
- * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
7515
- * Digest Algorithm, as defined in RFC 1321.
7516
- * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
7517
- * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
7518
- * Distributed under the BSD License
7519
- * See http://pajhome.org.uk/crypt/md5 for more info.
7520
- */
7521
- /*
7522
- * Configurable variables. You may need to tweak these to be compatible with
7523
- * the server-side, but the defaults work in most cases.
7524
- */
7525
- var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
7526
- /*
7527
- * These are the functions you'll usually want to call
7528
- * They take string arguments and return either hex or base-64 encoded strings
7529
- */
7530
- function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
7531
- /*
7532
- * Calculate the MD5 of a raw string
7533
- */
7534
- function rstr_md5(s) {
7535
- return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
7536
- }
7537
- /*
7538
- * Convert a raw string to a hex string
7539
- */
7540
- function rstr2hex(input) {
7541
- try {
7542
- hexcase;
7452
+ /**
7453
+ * 删除等待销毁的bundle,如果有
7454
+ * @param bundleName
7455
+ * @returns
7456
+ */
7457
+ _removeBundleFromReleaseMap(bundleName) {
7458
+ if (this._waitToReleaseBundles.has(bundleName)) {
7459
+ console.log(`[GCBundle] Cancel Pre Remove Bundle:${bundleName}`);
7460
+ }
7461
+ return this._waitToReleaseBundles.delete(bundleName);
7543
7462
  }
7544
- catch (e) {
7545
- hexcase = 0;
7463
+ /**
7464
+ * 真正移除bundle
7465
+ * @param bundle 目标bundle
7466
+ * @returns
7467
+ */
7468
+ _onRemoveBundle(bundle) {
7469
+ if (bundle && bundle.name) {
7470
+ //删除bundle的加载promise
7471
+ this._loadBunldePromiseMap.delete(bundle.name);
7472
+ //删除bundle对应的资源缓存
7473
+ this._loadPromiseMap.forEach((value, fullPath) => {
7474
+ if (this.parsePath(fullPath).bundle == bundle.name) {
7475
+ this._loadPromiseMap.delete(fullPath);
7476
+ }
7477
+ });
7478
+ if (cc.isValid(bundle)) {
7479
+ bundle.releaseAll();
7480
+ cc.assetManager.removeBundle(bundle);
7481
+ }
7482
+ //删除等待移除的bundle
7483
+ this._waitToReleaseBundles.delete(bundle.name);
7484
+ return true;
7485
+ }
7486
+ return false;
7546
7487
  }
7547
- var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
7548
- var output = "";
7549
- var x;
7550
- for (var i = 0; i < input.length; i++) {
7551
- x = input.charCodeAt(i);
7552
- output += hex_tab.charAt((x >>> 4) & 0x0F)
7553
- + hex_tab.charAt(x & 0x0F);
7488
+ /**
7489
+ * 开始Bundle GC,只执行一次
7490
+ * @returns
7491
+ */
7492
+ _startGC() {
7493
+ if (this._startedGC) {
7494
+ return;
7495
+ }
7496
+ this._startedGC = true;
7497
+ timer.loop(GC_CD, this, this._onGC);
7554
7498
  }
7555
- return output;
7556
- }
7557
- /*
7558
- * Encode a string as utf-8.
7559
- * For efficiency, this assumes the input is valid utf-16.
7560
- */
7561
- function str2rstr_utf8(input) {
7562
- var output = "";
7563
- var i = -1;
7564
- var x, y;
7565
- while (++i < input.length) {
7566
- /* Decode utf-16 surrogate pairs */
7567
- x = input.charCodeAt(i);
7568
- y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
7569
- if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
7570
- x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
7571
- i++;
7499
+ /**
7500
+ * GC Bundle
7501
+ * @returns
7502
+ */
7503
+ _onGC() {
7504
+ const count = this._waitToReleaseBundles.size;
7505
+ if (count == 0) {
7506
+ return;
7572
7507
  }
7573
- /* Encode output as utf-8 */
7574
- if (x <= 0x7F)
7575
- output += String.fromCharCode(x);
7576
- else if (x <= 0x7FF)
7577
- output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F), 0x80 | (x & 0x3F));
7578
- else if (x <= 0xFFFF)
7579
- output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 0x80 | ((x >>> 6) & 0x3F), 0x80 | (x & 0x3F));
7580
- else if (x <= 0x1FFFFF)
7581
- output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 0x80 | ((x >>> 12) & 0x3F), 0x80 | ((x >>> 6) & 0x3F), 0x80 | (x & 0x3F));
7508
+ const now = Date.now();
7509
+ this._waitToReleaseBundles.forEach((bundle) => {
7510
+ console.log(`[GCBundle] Remove Bundle:${bundle.name}`);
7511
+ this._onRemoveBundle(bundle);
7512
+ });
7513
+ this._waitToReleaseBundles.clear();
7514
+ console.log(`[GCBundle] [${count}] cost:${Date.now() - now} milliseconds`);
7582
7515
  }
7583
- return output;
7584
- }
7585
- /*
7586
- * Convert a raw string to an array of little-endian words
7587
- * Characters >255 have their high-byte silently ignored.
7588
- */
7589
- function rstr2binl(input) {
7590
- var output = Array(input.length >> 2);
7591
- for (var i = 0; i < output.length; i++)
7592
- output[i] = 0;
7593
- for (var i = 0; i < input.length * 8; i += 8)
7594
- output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
7595
- return output;
7596
- }
7597
- /*
7598
- * Convert an array of little-endian words to a string
7599
- */
7600
- function binl2rstr(input) {
7601
- var output = "";
7602
- for (var i = 0; i < input.length * 32; i += 8)
7603
- output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
7604
- return output;
7605
- }
7606
- /*
7607
- * Calculate the MD5 of an array of little-endian words, and a bit length.
7608
- */
7609
- function binl_md5(x, len) {
7610
- /* append padding */
7611
- x[len >> 5] |= 0x80 << ((len) % 32);
7612
- x[(((len + 64) >>> 9) << 4) + 14] = len;
7613
- var a = 1732584193;
7614
- var b = -271733879;
7615
- var c = -1732584194;
7616
- var d = 271733878;
7617
- for (var i = 0; i < x.length; i += 16) {
7618
- var olda = a;
7619
- var oldb = b;
7620
- var oldc = c;
7621
- var oldd = d;
7622
- a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
7623
- d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
7624
- c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
7625
- b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
7626
- a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
7627
- d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
7628
- c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
7629
- b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
7630
- a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
7631
- d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
7632
- c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
7633
- b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
7634
- a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
7635
- d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
7636
- c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
7637
- b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
7638
- a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
7639
- d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
7640
- c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
7641
- b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
7642
- a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
7643
- d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
7644
- c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
7645
- b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
7646
- a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
7647
- d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
7648
- c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
7649
- b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
7650
- a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
7651
- d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
7652
- c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
7653
- b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
7654
- a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
7655
- d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
7656
- c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
7657
- b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
7658
- a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
7659
- d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
7660
- c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
7661
- b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
7662
- a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
7663
- d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
7664
- c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
7665
- b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
7666
- a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
7667
- d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
7668
- c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
7669
- b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
7670
- a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
7671
- d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
7672
- c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
7673
- b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
7674
- a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
7675
- d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
7676
- c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
7677
- b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
7678
- a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
7679
- d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
7680
- c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
7681
- b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
7682
- a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
7683
- d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
7684
- c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
7685
- b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
7686
- a = safe_add(a, olda);
7687
- b = safe_add(b, oldb);
7688
- c = safe_add(c, oldc);
7689
- d = safe_add(d, oldd);
7516
+ /**
7517
+ * 手动设置bundle的版本,可新增可以覆盖。
7518
+ * @param map
7519
+ */
7520
+ setBundleVersion(map) {
7521
+ this._bundleVersionMap = this._bundleVersionMap || new Map();
7522
+ map.forEach((value, key) => {
7523
+ this._bundleVersionMap.set(value, key);
7524
+ });
7525
+ }
7526
+ /**
7527
+ * 获取bundle的版本
7528
+ * @param bundleName
7529
+ * @returns
7530
+ */
7531
+ _getBundleVersion(bundleName) {
7532
+ return this._bundleVersionMap && this._bundleVersionMap.get(bundleName);
7690
7533
  }
7691
- return Array(a, b, c, d);
7692
- }
7693
- /*
7694
- * These functions implement the four basic operations the algorithm uses.
7695
- */
7696
- function md5_cmn(q, a, b, x, s, t) {
7697
- return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
7698
- }
7699
- function md5_ff(a, b, c, d, x, s, t) {
7700
- return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
7701
- }
7702
- function md5_gg(a, b, c, d, x, s, t) {
7703
- return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
7704
- }
7705
- function md5_hh(a, b, c, d, x, s, t) {
7706
- return md5_cmn(b ^ c ^ d, a, b, x, s, t);
7707
- }
7708
- function md5_ii(a, b, c, d, x, s, t) {
7709
- return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
7710
- }
7711
- /*
7712
- * Add integers, wrapping at 2^32. This uses 16-bit operations internally
7713
- * to work around bugs in some JS interpreters.
7714
- */
7715
- function safe_add(x, y) {
7716
- var lsw = (x & 0xFFFF) + (y & 0xFFFF);
7717
- var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
7718
- return (msw << 16) | (lsw & 0xFFFF);
7719
- }
7720
- /*
7721
- * Bitwise rotate a 32-bit number to the left.
7722
- */
7723
- function bit_rol(num, cnt) {
7724
- return (num << cnt) | (num >>> (32 - cnt));
7725
7534
  }
7726
- const md5 = hex_md5;
7727
-
7728
7535
  /**
7729
- * cocos下的本地持久化实现类
7536
+ * cocos资源加载和管理
7730
7537
  */
7731
- class CocosStorageUtils {
7732
- removeValue(key) {
7733
- if (!window['CC_DEBUG']) {
7734
- key = md5(key);
7735
- }
7736
- sys.localStorage.removeItem(key);
7538
+ const res = new ResourceManager();
7539
+
7540
+ var View_1;
7541
+ const { ccclass: ccclass$2, property: property$2, menu: menu$2 } = cc._decorator;
7542
+ var ViewLevel;
7543
+ (function (ViewLevel) {
7544
+ ViewLevel[ViewLevel["TOPUP"] = 500] = "TOPUP";
7545
+ ViewLevel[ViewLevel["NET"] = 400] = "NET";
7546
+ ViewLevel[ViewLevel["TUTORIAL"] = 300] = "TUTORIAL";
7547
+ ViewLevel[ViewLevel["ALERT"] = 200] = "ALERT";
7548
+ ViewLevel[ViewLevel["UI"] = 100] = "UI";
7549
+ ViewLevel[ViewLevel["BG"] = 1] = "BG";
7550
+ })(ViewLevel || (ViewLevel = {}));
7551
+ let View = View_1 = class View extends cc.Component {
7552
+ constructor() {
7553
+ super(...arguments);
7554
+ /**
7555
+ * 是否可以交互
7556
+ */
7557
+ this._touchEnabled = true;
7737
7558
  }
7738
- setValue(key, value) {
7739
- if (!window['CC_DEBUG']) {
7740
- key = md5(key);
7741
- }
7742
- try {
7743
- sys.localStorage.setItem(key, value);
7559
+ /**
7560
+ * 是否在舞台中显示着
7561
+ */
7562
+ get inStage() {
7563
+ return cc.isValid(this.node) && this.node.activeInHierarchy;
7564
+ }
7565
+ /**
7566
+ * 视图是否响应交互
7567
+ */
7568
+ get touchEnabled() {
7569
+ return this._touchEnabled;
7570
+ }
7571
+ /**
7572
+ * 视图是否响应交互
7573
+ */
7574
+ set touchEnabled(value) {
7575
+ this._touchEnabled = value;
7576
+ if (value && !this._mask) {
7577
+ return;
7744
7578
  }
7745
- catch (error) {
7746
- console.error('Failed to set storage value:', "key:", key, "value:", value, "length:", String(value).length, "error:", error);
7579
+ if (!this._mask) {
7580
+ this._mask = new cc.Node();
7581
+ this._mask.name = "mask";
7582
+ let transform = this._mask.addComponent(cc.UITransform);
7583
+ const viewSize = cc.view.getVisibleSize();
7584
+ transform.setContentSize(viewSize.width, viewSize.height);
7585
+ this._mask.addComponent(cc.BlockInputEvents);
7586
+ // this.node.addChild(this.mask);
7587
+ this.node.addChild(this._mask);
7588
+ transform.priority = ViewLevel.TOPUP;
7589
+ this._mask.layer = this.node.layer;
7747
7590
  }
7591
+ this._mask.active = !value;
7748
7592
  }
7749
- getValue(key) {
7750
- if (!window['CC_DEBUG']) {
7751
- key = md5(key);
7593
+ /**
7594
+ * 添加其他视图
7595
+ * @param view 其他视图
7596
+ * @param index 视图索引
7597
+ */
7598
+ addView(view, index) {
7599
+ view.setControl(this._control);
7600
+ view.node.parent = this.node;
7601
+ (view.node.getComponent(cc.UITransform) || view.node.addComponent(cc.UITransform)).priority = index;
7602
+ viewManager.event("open", view);
7603
+ viewManager.reportViewAnalytics("open", view);
7604
+ analytics.session.updateSessionDuration();
7605
+ }
7606
+ /**
7607
+ * 移除子视图
7608
+ * @param view 子视图
7609
+ * @param destroy 是否销毁,默认为true
7610
+ * @param closeEventParams 视图close事件的参数
7611
+ */
7612
+ removeView(view, destroy = true, ...closeEventParams) {
7613
+ if (view.parentView == this) {
7614
+ destroy && view.node.destroyAllChildren();
7615
+ view.node.removeFromParent();
7616
+ viewManager.event("close", view, ...closeEventParams);
7617
+ viewManager.reportViewAnalytics("close", view);
7618
+ analytics.session.updateSessionDuration();
7752
7619
  }
7753
- return sys.localStorage.getItem(key);
7754
7620
  }
7755
- clear() {
7756
- sys.localStorage.clear();
7621
+ /**
7622
+ * 设置主场景所创建的Control
7623
+ * @param control
7624
+ */
7625
+ setControl(control) {
7626
+ this._control = control;
7757
7627
  }
7758
- }
7759
-
7760
- /**
7761
- * 任务队列
7762
- */
7763
- class TaskSequence extends Task {
7764
- constructor() {
7765
- super();
7766
- this._taskMap = new Map();
7767
- this._index = 0;
7628
+ control() {
7629
+ return this._control;
7768
7630
  }
7769
- addQuickly(taskCaller, taskFun, ...taskData) {
7770
- let task = new Task();
7771
- taskData.unshift(task);
7772
- task.start = (data) => {
7773
- taskFun.apply(taskCaller, taskData);
7774
- };
7775
- this.addTask(task);
7776
- return task;
7631
+ /**
7632
+ * 实例化后显示到舞台之后调用,缓存视图重新显示时也会调用。
7633
+ * @param data
7634
+ */
7635
+ setData(...data) {
7777
7636
  }
7778
- addTask(value, ...data) {
7779
- if (this._taskMap.has(value)) {
7780
- throw new Error("重复添加!");
7781
- }
7782
- this._taskMap.set(value, data);
7637
+ /**
7638
+ * 上报当前视图统计数据
7639
+ * @param event open和close以外的统计名称,名称必须为全小写,无下划线之外的其他特殊字符。
7640
+ * @param data 上报附带信息,可选参数,为空时会尝试从getAnalyticsDataOnViewEvent获取附带信息。
7641
+ */
7642
+ _report(event, data) {
7643
+ if (event == "open" || event == "close")
7644
+ return;
7645
+ viewManager.reportViewAnalytics(event, this, data);
7783
7646
  }
7784
- removeTask(value) {
7785
- return this._taskMap.delete(value);
7647
+ /**
7648
+ * 播放BG音乐
7649
+ * @param path 路径 - `bundleName://path/to/asset` 目标音频地址。
7650
+ * @param fade 是否渐进式播放,默认为true
7651
+ * @param loop 是否循环 默认为true
7652
+ * @param onFinishCallBack 单次播放结束后的回调箭头函数(cocos的锅)
7653
+ * @returns 背景音乐的audioId
7654
+ *
7655
+ * @example
7656
+ * _playBG("https://server.com/game/music.mp3")
7657
+ * _playBG("main://path/to/music")
7658
+ * _playBG("resources://path/to/music")
7659
+ */
7660
+ playBG(path, fade = true, loop = true, onFinishCallBack, volume = null) {
7661
+ return __awaiter(this, void 0, void 0, function* () {
7662
+ let clip = yield res.load(path);
7663
+ if (clip) {
7664
+ audio.playMusic(clip, fade, loop, onFinishCallBack, volume);
7665
+ }
7666
+ });
7786
7667
  }
7787
- start(...data) {
7788
- this._keys = this._taskMap.keys();
7789
- this._index = 0;
7790
- this._tryNext();
7668
+ /**
7669
+ * 播放音效
7670
+ * @param path 路径 - `bundleName://path/to/asset` 目标音频地址。
7671
+ * @param onFinishCallBack 单次播放结束后的回调箭头函数(cocos的锅)
7672
+ * @returns 音效的audioId
7673
+ */
7674
+ playEffect(path, onFinishCallBack, volume = null) {
7675
+ return __awaiter(this, void 0, void 0, function* () {
7676
+ let clip = yield res.load(path);
7677
+ return new Promise((reslove) => {
7678
+ if (clip) {
7679
+ audio.playEffect(clip, false, () => {
7680
+ onFinishCallBack && onFinishCallBack();
7681
+ reslove();
7682
+ }, volume);
7683
+ }
7684
+ });
7685
+ });
7791
7686
  }
7792
- _tryNext() {
7793
- let next = this._keys.next();
7794
- if (!next.done) {
7795
- let task = next.value;
7796
- task.on(Task.Event.COMPLETE, this, this._subTaskEventHandler);
7797
- task.on(Task.Event.PROGRESS, this, this._subTaskEventHandler);
7798
- task.on(Task.Event.ERROR, this, this._subTaskEventHandler);
7799
- task.start(this._taskMap.get(task));
7687
+ /**
7688
+ * 播放激励视频的快捷方式。
7689
+ * @param from 可选参数,可以附带广告行为的来源,以及其他需要统计的信息
7690
+ * @param stopRecorder 可选参数,是否暂停录屏, 默认为true
7691
+ */
7692
+ showRewardVideo(from, stopRecorder = true) {
7693
+ return new Promise((resolve) => {
7694
+ stopRecorder && publisher.record && publisher.record.pauseRecord();
7695
+ viewManager.closeTouch();
7696
+ //解决部分平台(tt)拉起视频时,最小化视频播放不出来的bug。
7697
+ const openTouch = () => {
7698
+ viewManager.openTouch();
7699
+ App.offShow(openTouch);
7700
+ };
7701
+ App.onShow(openTouch);
7702
+ from = from || {};
7703
+ // from.name = this.analyticsInfo ? this.analyticsInfo.name : this.name;
7704
+ publisher.ad.showRewardVideo(from).then((result) => {
7705
+ openTouch();
7706
+ stopRecorder && publisher.record && publisher.record.resumeRecord();
7707
+ resolve(result);
7708
+ }).catch(() => {
7709
+ openTouch();
7710
+ stopRecorder && publisher.record && publisher.record.resumeRecord();
7711
+ resolve(false);
7712
+ });
7713
+ });
7714
+ }
7715
+ /**
7716
+ * 分享录屏视频
7717
+ * @param analyticsName 玩法发起分享的动作来源,用于统计。
7718
+ * @param title 视频标题
7719
+ * @param topics 视频话题列表
7720
+ */
7721
+ shareRecord(analyticsName, title, topics) {
7722
+ let path = publisher.record.videoPath;
7723
+ if (path) {
7724
+ return publisher.share.shareVideo(analyticsName, { "video_title": title, "videoTopics": topics, "videoPath": path });
7800
7725
  }
7801
7726
  else {
7802
- //结束
7803
- this.onComplete();
7727
+ return new Promise((resolve) => { resolve(false); });
7804
7728
  }
7805
7729
  }
7806
- _subTaskEventHandler(key, target, data) {
7807
- if (key == Task.Event.ERROR) {
7808
- this.onError(data.code, data.msg);
7809
- return;
7730
+ /**
7731
+ * 子项重写时,必须要调用super.onDestroy()
7732
+ *
7733
+ */
7734
+ onDestroy() {
7735
+ this.config.cache && viewManager.cancelCache(this);
7736
+ }
7737
+ /**
7738
+ * 父级视图
7739
+ */
7740
+ get parentView() {
7741
+ if (this.node.parent) {
7742
+ return this.node.parent.getComponent(View_1);
7810
7743
  }
7811
- if (key == Task.Event.COMPLETE) {
7812
- target.offAllEvent();
7813
- target.destroy();
7814
- this._index++;
7815
- this.onProgress(this._index / this._taskMap.size);
7816
- this._tryNext();
7744
+ return null;
7745
+ }
7746
+ /**
7747
+ * 关闭视图,根据视图是否缓存选择是否销毁视图
7748
+ * @param closeEventParams 视图close事件的参数
7749
+ */
7750
+ close(...closeEventParams) {
7751
+ this.removeFromParent(!this.config.cache, ...closeEventParams);
7752
+ }
7753
+ onClose(caller, listener, once = true, ...args) {
7754
+ const targetView = this.config.viewClassName || this.config.viewClass || View_1.type;
7755
+ viewManager.onViewClose(targetView, caller, listener, once, ...args);
7756
+ }
7757
+ /**
7758
+ * 从父级移除视图
7759
+ * @param destroy 是否销毁,默认为true
7760
+ * @param closeEventParams 视图close事件的参数
7761
+ */
7762
+ removeFromParent(destroy = true, ...closeEventParams) {
7763
+ if (this.parentView) {
7764
+ this.parentView.removeView(this, destroy, ...closeEventParams);
7817
7765
  }
7818
7766
  }
7819
- reset() {
7820
- this.offAllEvent();
7821
- this._taskMap.forEach((data, task) => {
7822
- task.reset();
7823
- });
7824
- this._taskMap.clear();
7825
- this._keys = null;
7826
- this._index = 0;
7767
+ /**
7768
+ * 直接销毁
7769
+ */
7770
+ allDestroy() {
7771
+ if (this.node && cc.isValid(this.node)) {
7772
+ this.node.parent = null;
7773
+ this.node.destroy();
7774
+ }
7827
7775
  }
7828
- destroy() {
7829
- super.destroy();
7830
- this._taskMap.forEach((data, task) => {
7831
- task.reset();
7832
- });
7833
- this._taskMap.clear();
7834
- this._keys = null;
7835
- this._index = 0;
7776
+ getAnalyticsDataOnViewEvent(event) {
7777
+ return {};
7836
7778
  }
7837
- }
7779
+ };
7780
+ /**
7781
+ * View类的Class名称
7782
+ */
7783
+ View.type = "View";
7784
+ View = View_1 = __decorate([
7785
+ ccclass$2("View"),
7786
+ menu$2("基础视图/View")
7787
+ /**
7788
+ * cocos引擎下的视图管理类
7789
+ */
7790
+ ], View);
7838
7791
 
7839
7792
  /**
7840
- * 视图加载器
7793
+ * 注册进入视图时,上报的事件名称,不注册则上报视图配置的name属性
7794
+ * @param analyticsName 可选参数,默认为view的name,可以指定为上报的中文名称
7795
+ * @param group 分组,默认为default,方便分类统计
7796
+ * @returns
7841
7797
  */
7842
- class IViewLoader extends EventDispatcher {
7843
- constructor(pool) {
7844
- super();
7845
- this._initializer = new TaskSequence();
7846
- this._pool = pool;
7847
- }
7848
- load(task) {
7849
- if (this._asset) {
7850
- task && task.onComplete();
7851
- return;
7798
+ function viewAnalytics(analyticsName, group = "default") {
7799
+ return function (target, propertyKey) {
7800
+ viewManager.registerAnalytics(target, analyticsName, group);
7801
+ };
7802
+ }
7803
+ /**
7804
+ * 视图管理类
7805
+ */
7806
+ class ViewManager {
7807
+ /**
7808
+ * 注册视图上报事件的视图名称,一般为中文方便数据分辨
7809
+ * @param viewClass
7810
+ * @param analyticsName 可选参数,默认为view的name,可以指定为上报的中文名称
7811
+ * @param group 分组,可选参数,默认为default,方便分类统计
7812
+ */
7813
+ registerAnalytics(viewClass, analyticsName, group = "default") {
7814
+ let config = this._getViewConfig(viewClass);
7815
+ if (config) {
7816
+ this._viewAnalyticsMap.set(config.name, { name: analyticsName || config.name, group });
7852
7817
  }
7853
- if (task) {
7854
- this._initializer.once(Task.Event.ERROR, this, (type, a, error) => {
7855
- task.onError(error.code, error.msg);
7856
- });
7857
- this._initializer.on(Task.Event.PROGRESS, this, (type, a, percent) => {
7858
- task.onProgress(percent);
7859
- });
7818
+ else {
7819
+ throw new Error(`未获取到视图${viewClass}的配置`);
7860
7820
  }
7861
- this._initializer.once(Task.Event.COMPLETE, this, () => {
7862
- this._initializer.reset();
7863
- task && task.onComplete();
7864
- });
7865
- this._initializer.start();
7866
7821
  }
7867
- destroy() {
7868
- this.offAll();
7869
- this._initializer.reset();
7870
- this._initializer = null;
7871
- this._asset = null;
7872
- this._pool = null;
7822
+ /**
7823
+ * 注册视图类对应的视图配置
7824
+ * @param viewName 视图名称
7825
+ * @param config 视图配置
7826
+ */
7827
+ registerView(viewName, config) {
7828
+ if (this._viewConfigMap.has(viewName)) {
7829
+ throw new Error(`已经存在名称为${viewName}的View`);
7830
+ }
7831
+ this._viewConfigMap.set(viewName, config);
7873
7832
  }
7874
- reset() {
7875
- this._initializer.reset();
7876
- this.offAll();
7877
- this._asset = null;
7833
+ /**
7834
+ * 注册closeTouch默认遮罩视图
7835
+ * @param config 视图配置|Class|名称
7836
+ */
7837
+ registerMaskView(config) {
7838
+ let viewConfig = this._getViewConfig(config);
7839
+ if (!viewConfig) {
7840
+ throw new Error(`未获取到默认遮罩视图${config}的配置`);
7841
+ }
7842
+ viewConfig.cache = true;
7843
+ viewConfig.zIndex = ViewLevel.TOPUP;
7844
+ this._defaultMaskViewConfig = viewConfig;
7878
7845
  }
7879
- recover() {
7880
- this._pool.recover(this);
7846
+ getViewConfig(config) {
7847
+ return Object.assign({}, this._getViewConfig(config));
7848
+ }
7849
+ /**
7850
+ * [内部直接获取]获取视图配置
7851
+ * @param config
7852
+ * @returns
7853
+ */
7854
+ _getViewConfig(config) {
7855
+ if (config == null) {
7856
+ console.error(`[view] 获取视图配置,参数不能为空`);
7857
+ return null;
7858
+ }
7859
+ if (typeof config == "string") {
7860
+ if (!this._viewConfigMap.has(config)) {
7861
+ console.error(`[view]${config}视图配置未注册`);
7862
+ return null;
7863
+ }
7864
+ return this._viewConfigMap.get(config);
7865
+ }
7866
+ if ("path" in config) {
7867
+ return config;
7868
+ }
7869
+ let values = this._viewConfigMap.values();
7870
+ while (true) {
7871
+ let item = values.next();
7872
+ if (!item || item.done) {
7873
+ return null;
7874
+ }
7875
+ if (item.value['viewClass'] == config) {
7876
+ return item.value;
7877
+ }
7878
+ }
7881
7879
  }
7882
- }
7883
-
7884
- const GC_CD = 60 * 1000;
7885
- /**
7886
- * 资源管理
7887
- */
7888
- class ResourceManager {
7889
7880
  constructor() {
7890
- this._loadPromiseMap = new Map();
7891
- this._loadBunldePromiseMap = new Map();
7892
- this._waitToReleaseBundles = new Map();
7893
- this._startedGC = false;
7881
+ this._viewConfigMap = new Map();
7882
+ this._viewAnalyticsMap = new Map();
7883
+ this._currentStack = [];
7884
+ this._viewCache = new Map();
7885
+ this._touchLocks = new Set();
7886
+ if (ViewManager.created) {
7887
+ throw new Error("ViewManager 是单例");
7888
+ }
7889
+ ViewManager.created = true;
7894
7890
  }
7895
- preload(path, task) {
7896
- return __awaiter(this, void 0, void 0, function* () {
7897
- let paths = typeof path === "string" ? [path] : path;
7898
- let pathInfoList = paths.map((value) => {
7899
- return this.parsePath(value);
7900
- });
7901
- let loadPromiseList = pathInfoList.map((info) => {
7902
- return !info.isRemote ? this._preLoadFromBundle(info, task) : this._preLoadFromRemote(info, task);
7903
- });
7904
- return Promise.all(loadPromiseList).then((value) => {
7905
- return value.length == 1 ? value[0] : value;
7906
- });
7891
+ /**
7892
+ * 场景启动后设置场景,不建议手动调用
7893
+ * @param root
7894
+ */
7895
+ setRoot(root) {
7896
+ this._viewCache.forEach((views) => {
7897
+ for (const view of views) {
7898
+ view.allDestroy();
7899
+ }
7907
7900
  });
7901
+ this._viewCache.clear();
7902
+ this._currentStack.length = 0;
7903
+ this.event("open", root);
7904
+ this.reportViewAnalytics("open", root);
7905
+ this._scene = root;
7906
+ this._currentView = null;
7907
+ businessCenter && businessCenter.start(RewardVideoMaskBusiness);
7908
7908
  }
7909
- preLoadScene(path, task) {
7909
+ /**
7910
+ * 切换场景
7911
+ * @param config 目标场景的配置|Class|名称
7912
+ * @param data
7913
+ * @returns
7914
+ */
7915
+ switchScene(config, ...data) {
7910
7916
  return __awaiter(this, void 0, void 0, function* () {
7911
- let paths = typeof path === "string" ? [path] : path;
7912
- let pathInfoList = paths.map((value) => {
7913
- let result = this.parsePath(value);
7914
- if (result.isRemote) {
7915
- throw new Error(`不支持从远程预加载场景${value}`);
7916
- }
7917
- return result;
7918
- });
7919
- let loadPromiseList = pathInfoList.map((info) => __awaiter(this, void 0, void 0, function* () {
7920
- let bundle = yield this.getBundle(info.bundle);
7921
- return new Promise((resolve) => {
7922
- if (!bundle) {
7923
- resolve(null);
7924
- task && task.onError("preLoadScene", `加载${info.fullPath}时 bundle${info.bundle}加载失败`);
7925
- }
7926
- bundle.preloadScene(info.path, (error) => {
7927
- if (error) {
7928
- resolve(null);
7929
- task && task.onError(error.name, error.message);
7930
- }
7931
- resolve(info.fullPath);
7932
- task && task.onComplete();
7933
- });
7934
- });
7935
- }));
7936
- return Promise.all(loadPromiseList).then((value) => {
7937
- return value.length > 0 ? (value.length == 1 ? value[0] : value) : null;
7938
- });
7917
+ config = this._getViewConfig(config);
7918
+ let view = this.getCacheView(config);
7919
+ if (view) {
7920
+ view = yield this._switchView(view, ...data);
7921
+ }
7922
+ else {
7923
+ view = yield this._switchViewByConfig(config, ...data);
7924
+ }
7925
+ // this.cacheView(view);
7926
+ // this._currentStack.push(config);
7927
+ return view;
7939
7928
  });
7940
7929
  }
7941
- _preLoadFromRemote(info, task) {
7930
+ /**
7931
+ * 当前场景
7932
+ * @returns
7933
+ */
7934
+ scene() {
7935
+ return this._scene;
7936
+ }
7937
+ /**
7938
+ * 开启当前场景的可交互性
7939
+ * @param token closeTouch返回的触摸锁令牌。传入时仅释放该锁,所有锁释放后才会真正开启触摸;不传则清空全部触摸锁并开启触摸。
7940
+ */
7941
+ openTouch(token) {
7942
+ if (token) {
7943
+ this._touchLocks.delete(token);
7944
+ }
7945
+ else {
7946
+ this._touchLocks.clear();
7947
+ }
7948
+ this._applyTouchState();
7949
+ }
7950
+ /**
7951
+ * 关闭当前场景的可交互性
7952
+ * @param delay 可选参数,默认200毫秒,显示maskView的延迟时间。
7953
+ * @param maskViewConfig 可选参数,默认为空白,要显示的遮罩试图。
7954
+ * @returns 当前触摸锁令牌,可传给openTouch精确释放本次锁。
7955
+ */
7956
+ closeTouch(delay = 200, maskViewConfig = null) {
7942
7957
  return __awaiter(this, void 0, void 0, function* () {
7943
- if (!info.isRemote) {
7944
- throw new Error(`${info.fullPath}不是远程资源地址`);
7958
+ const token = this._lockTouch();
7959
+ let config = maskViewConfig == null ? this._defaultMaskViewConfig : this._getViewConfig(maskViewConfig);
7960
+ if (!config) {
7961
+ return token;
7945
7962
  }
7946
- if (info.isDir) {
7947
- throw new Error(`不支持对远程Dir的加载`);
7963
+ if (delay <= 0) {
7964
+ yield this._showMaskView(config, token);
7965
+ return token;
7948
7966
  }
7949
- return new Promise((resolve, reject) => {
7950
- cc.assetManager.loadRemote(info.fullPath, (error, asset) => {
7951
- if (error) {
7952
- resolve(null);
7953
- task && task.onError(error.name, error.message);
7954
- }
7955
- else {
7956
- resolve(info.fullPath);
7957
- task && task.onComplete();
7958
- }
7959
- });
7960
- });
7967
+ timer.once(delay, this, this._showMaskView, [config, token]);
7968
+ return token;
7961
7969
  });
7962
7970
  }
7963
- _preLoadFromBundle(info, task) {
7971
+ _lockTouch() {
7972
+ const token = Symbol("touch-lock");
7973
+ this._touchLocks.add(token);
7974
+ this._applyTouchState();
7975
+ return token;
7976
+ }
7977
+ _applyTouchState() {
7978
+ const locked = this._touchLocks.size > 0;
7979
+ if (!locked) {
7980
+ timer.clear(this, this._showMaskView);
7981
+ if (this._currentMaskView) {
7982
+ this._currentMaskView.removeFromParent(true);
7983
+ this._currentMaskView = null;
7984
+ }
7985
+ }
7986
+ if (this._scene) {
7987
+ this._scene.touchEnabled = !locked;
7988
+ }
7989
+ }
7990
+ _showMaskView(maskViewConfig, token) {
7964
7991
  return __awaiter(this, void 0, void 0, function* () {
7965
- let bundle = yield this.getBundle(info.bundle);
7966
- if (!bundle) {
7967
- task && task.onError("_preLoadFromBundle", `加载${info.fullPath}时 bundle${info.bundle}加载失败`);
7968
- return null;
7992
+ if (!this._scene || !this._touchLocks.has(token) || this._currentMaskView) {
7993
+ return;
7969
7994
  }
7970
- return new Promise((resolve) => {
7971
- let onProgress = (finish, total) => {
7972
- task && task.onProgress(finish / total);
7973
- };
7974
- let onComplete = (error) => {
7975
- if (error) {
7976
- resolve(null);
7977
- task && task.onError(error.name, error.message);
7978
- }
7979
- else {
7980
- resolve(info.fullPath);
7981
- task && task.onComplete();
7982
- }
7983
- };
7984
- if (info.path == "") {
7985
- onComplete(null);
7986
- }
7987
- else if (!info.isDir) {
7988
- bundle.preload(info.path, onProgress, onComplete);
7989
- }
7990
- else {
7991
- bundle.preloadDir(info.path, onProgress, onComplete);
7995
+ let view = this.getCacheView(maskViewConfig);
7996
+ if (view) {
7997
+ view = yield this._addView(view, this._scene);
7998
+ }
7999
+ else {
8000
+ view = yield this._loadViewByConfig(maskViewConfig, this._scene, "显示遮罩视图错误");
8001
+ this.cacheView(view);
8002
+ }
8003
+ if (!this._touchLocks.has(token)) {
8004
+ view.removeFromParent(true);
8005
+ return;
8006
+ }
8007
+ this._currentMaskView = view;
8008
+ timer.frameNext(this, () => {
8009
+ if (this._currentMaskView == view && this._touchLocks.has(token)) {
8010
+ view.setData();
7992
8011
  }
7993
8012
  });
7994
8013
  });
7995
8014
  }
7996
- load(path, assetType, task) {
8015
+ /**
8016
+ * 当前主视图
8017
+ * @returns
8018
+ */
8019
+ view() {
8020
+ return this._currentView;
8021
+ }
8022
+ /**
8023
+ * 切换主视图到目标视图
8024
+ * @param target 目标视图的配置|Class|名称
8025
+ * @param datas 目标视图初始化参数
8026
+ */
8027
+ switchTo(config, ...data) {
7997
8028
  return __awaiter(this, void 0, void 0, function* () {
7998
- let paths = typeof path === "string" ? [path] : path;
7999
- let pathInfoList = paths.map((value) => {
8000
- return this.parsePath(value);
8001
- });
8002
- let result = [];
8003
- for (const info of pathInfoList) {
8004
- if (!info.isRemote) {
8005
- let assets = yield this._loadFromBundle(info, assetType, task);
8006
- if (assets instanceof cc.Asset) {
8007
- result.push(assets);
8008
- }
8009
- else {
8010
- result.push(...assets);
8011
- }
8012
- continue;
8013
- }
8014
- let asset = yield this._loadfromRemote(info, task);
8015
- if (assetType === cc.SpriteFrame) {
8016
- asset = cc.SpriteFrame.createWithImage(asset);
8017
- }
8018
- result.push(asset);
8029
+ config = this._getViewConfig(config);
8030
+ if (!this._scene) {
8031
+ throw new Error("根View不存在");
8019
8032
  }
8020
- return typeof path === "string" ? result[0] : result;
8033
+ let view = this.getCacheView(config);
8034
+ if (view) {
8035
+ view = yield this._switchView(view, ...data);
8036
+ }
8037
+ else {
8038
+ view = yield this._switchViewByConfig(config, ...data);
8039
+ }
8040
+ this.cacheView(view);
8041
+ this._currentStack.push(config);
8042
+ return view;
8021
8043
  });
8022
8044
  }
8023
- _loadFromBundle(info, type, task) {
8045
+ /**
8046
+ * 切回当前主视图到上一个主视图。
8047
+ * @param data
8048
+ * @returns
8049
+ */
8050
+ switchBack(...data) {
8024
8051
  return __awaiter(this, void 0, void 0, function* () {
8025
- let bundle = yield this.getBundle(info.bundle);
8026
- if (!bundle) {
8027
- task && task.onError("_loadFromBundle", `加载${info.fullPath}时 bundle${info.bundle}加载失败`);
8052
+ if (this._currentStack.length == 0) {
8053
+ console.warn("上一个视图不存在");
8028
8054
  return null;
8029
8055
  }
8030
- if (this._loadPromiseMap.has(info.fullPath)) {
8031
- return this._loadPromiseMap.get(info.fullPath).then((value) => {
8032
- task && task.onComplete();
8033
- return value;
8034
- });
8056
+ //移除当前视图config
8057
+ this._currentStack.pop();
8058
+ //获取之前视图config
8059
+ let lastViewConfig = this._currentStack.pop();
8060
+ if (!lastViewConfig) {
8061
+ console.warn("上个主视图为空,");
8062
+ return null;
8035
8063
  }
8036
- let promise = new Promise((resolve) => {
8037
- let onProgress = (finish, total) => {
8038
- task && task.onProgress(finish / total);
8039
- };
8040
- let onComplete = (error, assets) => {
8041
- if (error) {
8042
- console.error("加载资源发生错误", info, error);
8043
- resolve(null);
8044
- task && task.onError(error.name, error.message);
8045
- }
8046
- else {
8047
- resolve(assets);
8048
- task && task.onComplete();
8049
- }
8050
- };
8051
- if (info.path == "") {
8052
- onComplete(null, null);
8053
- }
8054
- else if (!info.isDir) {
8055
- bundle.load(info.path, type, onProgress, onComplete);
8056
- }
8057
- else {
8058
- bundle.loadDir(info.path, type, onProgress, onComplete);
8059
- }
8060
- });
8061
- this._loadPromiseMap.set(info.fullPath, promise);
8062
- return promise;
8064
+ return yield this.switchTo(lastViewConfig, ...data);
8063
8065
  });
8064
8066
  }
8065
- _loadfromRemote(info, task) {
8066
- if (!info.isRemote) {
8067
- throw new Error(`${info.fullPath}不是远程资源地址`);
8068
- }
8069
- if (info.isDir) {
8070
- throw new Error(`不支持对远程Dir的加载`);
8071
- }
8072
- if (this._loadPromiseMap.has(info.fullPath)) {
8073
- return this._loadPromiseMap.get(info.fullPath);
8074
- }
8075
- let promise = new Promise((resolve) => {
8076
- cc.assetManager.loadRemote(info.fullPath, (error, asset) => {
8077
- if (error) {
8078
- resolve(null);
8079
- task && task.onError(error.name, error.message);
8080
- }
8081
- else {
8082
- resolve(asset);
8083
- task && task.onComplete();
8084
- }
8067
+ _switchView(view, ...datas) {
8068
+ return __awaiter(this, void 0, void 0, function* () {
8069
+ this._currentView && this._currentView.removeFromParent(!this._currentView.config.cache);
8070
+ this._currentView = null;
8071
+ this._currentView = view;
8072
+ this._scene.addView(this._currentView, view.config.zIndex);
8073
+ this._currentView.setData(...datas);
8074
+ return this._currentView;
8075
+ });
8076
+ }
8077
+ _switchViewByConfig(viewConfig, ...datas) {
8078
+ return __awaiter(this, void 0, void 0, function* () {
8079
+ const touchToken = this._lockTouch();
8080
+ let loader;
8081
+ try {
8082
+ loader = yield this.loadView(viewConfig);
8083
+ }
8084
+ catch (error) {
8085
+ this.openTouch(touchToken);
8086
+ console.error(`切换视图错误 code:${error.code}, msg:${error.msg || error.message}`);
8087
+ throw new Error(`切换视图错误 code:${error.code}, msg:${error.msg || error.message}`);
8088
+ }
8089
+ let name = viewConfig.name || viewConfig.path.substring(viewConfig.path.lastIndexOf("/") + 1);
8090
+ this._currentView && this._currentView.removeFromParent(!this._currentView.config.cache);
8091
+ this._currentView = null;
8092
+ let view = loader.initView(name, true);
8093
+ view.name = name;
8094
+ view.config = viewConfig;
8095
+ view.analyticsInfo = this._viewAnalyticsMap.get(viewConfig.name);
8096
+ if (view['addToBG']) {
8097
+ this.setRoot(view);
8098
+ }
8099
+ else {
8100
+ this._currentView = view;
8101
+ this._scene.addView(view, viewConfig.zIndex);
8102
+ }
8103
+ timer.frameNext(this, () => {
8104
+ this.openTouch(touchToken);
8105
+ view.setData(...datas);
8085
8106
  });
8107
+ return view;
8086
8108
  });
8087
- this._loadPromiseMap.set(info.fullPath, promise);
8088
- return promise;
8089
- }
8090
- /**
8091
- * 解析资源路径成PathInfo
8092
- * @param fullPath - 资源路径应该符合bundle://cc.path/to/resource格式
8093
- * @returns - `PathInfo` 资源信息
8094
- */
8095
- parsePath(fullPath) {
8096
- let tempPath = fullPath.split("://");
8097
- if (tempPath.length != 2) {
8098
- throw new Error(`资源地址${fullPath}格式错误,应该符合bundle://cc.path/to/resource格式`); //.suffix
8099
- }
8100
- let isDir = fullPath.charAt(fullPath.length - 1) == "/";
8101
- let bundle = tempPath[0];
8102
- let isRemote = bundle === "https" || bundle === "http";
8103
- let suffix = cc.path.extname(fullPath).toLowerCase();
8104
- let simplePath = suffix == "" ? tempPath[1] : tempPath[1].replace(suffix, "");
8105
- return { bundle, path: simplePath, isRemote, isDir, fullPath };
8106
- }
8107
- /**
8108
- * 获取目标bundle的资源的完整路径
8109
- * @param bundleName - 资源所在bundle名称
8110
- * @param subPath - 可选参数, 相对子路径
8111
- * @returns
8112
- */
8113
- getPath(bundleName, subPath = "") {
8114
- if (subPath.includes("://")) {
8115
- return subPath;
8116
- }
8117
- return `${bundleName}://${subPath}`;
8118
8109
  }
8119
8110
  /**
8120
- * 获取主包下资源的完整路径
8121
- * @param subPath 资源子路径
8122
- * @returns
8111
+ * 通过视图配置添加视图
8112
+ * @param viewConfig 要添加的视图配置|Class|名称
8113
+ * @param parent 父级视图,默认为当前主视图,即viewMananger.current()
8114
+ * @param datas 传入setData的数据
8123
8115
  */
8124
- getPathInMain(subPath) {
8125
- return this.getPath("main", subPath);
8116
+ addView(config, parent, ...datas) {
8117
+ return __awaiter(this, void 0, void 0, function* () {
8118
+ config = this._getViewConfig(config);
8119
+ parent = parent || this._currentView;
8120
+ if (!parent) {
8121
+ throw new Error("当前View不存在, 且未传入父级视图");
8122
+ }
8123
+ let view = this.getCacheView(config);
8124
+ if (view) {
8125
+ view = yield this._addView(view, parent, ...datas);
8126
+ }
8127
+ else {
8128
+ view = yield this._addViewbyConfig(config, parent, ...datas);
8129
+ }
8130
+ this.cacheView(view);
8131
+ return view;
8132
+ });
8126
8133
  }
8127
- /**
8128
- * 获取动态包下资源的完整路径
8129
- * @param subPath 资源子路径
8130
- * @returns
8131
- */
8132
- getPathInResources(subPath) {
8133
- return this.getPath("resources", subPath);
8134
+ _addView(view, parent, ...datas) {
8135
+ return __awaiter(this, void 0, void 0, function* () {
8136
+ parent.addView(view, view.config.zIndex);
8137
+ view.setData(...datas);
8138
+ return view;
8139
+ });
8134
8140
  }
8135
- /**
8136
- * 加载Bundle
8137
- * @param bundleName Bundle名称
8138
- * @returns `Promise<cc.AssetManager.Bundle>`
8139
- */
8140
- getBundle(bundleName) {
8141
- this._removeBundleFromReleaseMap(bundleName);
8142
- this._startGC();
8143
- if (this._loadBunldePromiseMap.has(bundleName)) {
8144
- return this._loadBunldePromiseMap.get(bundleName);
8145
- }
8146
- let promise = new Promise((resolve) => {
8147
- let bundle = cc.assetManager.getBundle(bundleName);
8148
- if (!!bundle) {
8149
- resolve(bundle);
8150
- return;
8141
+ _addViewbyConfig(config, parent, ...datas) {
8142
+ return __awaiter(this, void 0, void 0, function* () {
8143
+ const touchToken = this._lockTouch();
8144
+ let view;
8145
+ try {
8146
+ view = yield this._loadViewByConfig(config, parent, "添加视图错误");
8151
8147
  }
8152
- let version = this._getBundleVersion(bundleName);
8153
- cc.assetManager.loadBundle(bundleName, version ? { version: version } : null, (error, bundle) => {
8154
- if (error) {
8155
- console.error(`LoadBundle ${bundleName} Error:`, error);
8156
- resolve(null);
8157
- }
8158
- else {
8159
- resolve(bundle);
8160
- }
8148
+ catch (error) {
8149
+ this.openTouch(touchToken);
8150
+ throw error;
8151
+ }
8152
+ timer.frameNext(this, () => {
8153
+ this.openTouch(touchToken);
8154
+ view.setData(...datas);
8161
8155
  });
8156
+ return view;
8162
8157
  });
8163
- this._loadBunldePromiseMap.set(bundleName, promise);
8164
- return promise;
8165
8158
  }
8166
- /**
8167
- * bundle是否被加载过,且有效
8168
- * @param bundleName
8169
- * @returns
8170
- */
8171
- isBundleValid(bundleName) {
8159
+ _loadViewByConfig(config, parent, errorPrefix) {
8172
8160
  return __awaiter(this, void 0, void 0, function* () {
8173
- if (this._loadBunldePromiseMap.has(bundleName)) {
8174
- let bundle = yield this._loadBunldePromiseMap.get(bundleName);
8175
- return bundle && cc.isValid(bundle);
8161
+ let loader;
8162
+ try {
8163
+ loader = yield this.loadView(config);
8176
8164
  }
8177
- return false;
8165
+ catch (error) {
8166
+ console.error(`${errorPrefix} code:${error.code}, msg:${error.msg || error.message}`);
8167
+ throw new Error(`${errorPrefix} code:${error.code}, msg:${error.msg || error.message}`);
8168
+ }
8169
+ let name = config.name || config.path.substring(config.path.lastIndexOf("/") + 1);
8170
+ let view = loader.initView(name, true);
8171
+ view.name = name;
8172
+ view.config = config;
8173
+ view.analyticsInfo = this._viewAnalyticsMap.get(config.name);
8174
+ parent.addView(view, config.zIndex);
8175
+ return view;
8178
8176
  });
8179
8177
  }
8180
8178
  /**
8181
- * 移除并销毁bundle,以及bundle下所有资源。
8182
- * @param bundleName bundle名称
8183
- * @param delay 是否延后销毁,默认为true
8184
- * @returns
8179
+ * 加载视图到内存,之后还需要调用loader.initView接口获取view
8180
+ * @param config 要加载的视图配置|Class|名称
8181
+ * @param task 任务载体
8185
8182
  */
8186
- removeBundle(bundleName, delay = true) {
8187
- return __awaiter(this, void 0, void 0, function* () {
8188
- if (this.isBundleValid(bundleName)) {
8189
- let bundle = yield this._loadBunldePromiseMap.get(bundleName);
8190
- if (delay) {
8191
- this._pushBundleToReleaseMap(bundle);
8192
- return true;
8193
- }
8194
- return this._onRemoveBundle(bundle);
8195
- }
8196
- return false;
8183
+ loadView(config, task) {
8184
+ return new Promise((resolve, reject) => {
8185
+ config = this._getViewConfig(config);
8186
+ let loader = this.impl.getLoader(config).init(config);
8187
+ task = task || new Task();
8188
+ task.once(Task.Event.COMPLETE, this, () => {
8189
+ resolve(loader);
8190
+ });
8191
+ task.once(Task.Event.ERROR, this, (type, task, reason) => {
8192
+ reject(reason);
8193
+ });
8194
+ loader.load(task);
8197
8195
  });
8198
8196
  }
8199
8197
  /**
8200
- * 推送bundle到GC队列
8201
- * @param bundle
8198
+ * 获取缓存视图
8199
+ * @param config
8200
+ * @returns
8202
8201
  */
8203
- _pushBundleToReleaseMap(bundle) {
8204
- if (bundle && cc.isValid(bundle)) {
8205
- console.log(`[GCBundle] Pre Remove Bundle:${bundle.name}`);
8206
- this._waitToReleaseBundles.set(bundle.name, bundle);
8202
+ getCacheView(config) {
8203
+ config = this._getViewConfig(config);
8204
+ let views = this._viewCache.get(config.path);
8205
+ if (!views || views.length == 0) {
8206
+ return null;
8207
+ }
8208
+ for (const view of views) {
8209
+ if (!view.inStage) {
8210
+ return view;
8211
+ }
8207
8212
  }
8213
+ return null;
8208
8214
  }
8209
8215
  /**
8210
- * 删除等待销毁的bundle,如果有
8211
- * @param bundleName
8216
+ * 缓存视图
8217
+ * @param view
8218
+ * @param force 默认为false,不考虑view.config.cache的值,强制增加到缓存
8212
8219
  * @returns
8213
8220
  */
8214
- _removeBundleFromReleaseMap(bundleName) {
8215
- if (this._waitToReleaseBundles.has(bundleName)) {
8216
- console.log(`[GCBundle] Cancel Pre Remove Bundle:${bundleName}`);
8221
+ cacheView(view, force = false) {
8222
+ if (!view || !view.config || (!view.config.cache && !force)) {
8223
+ return false;
8217
8224
  }
8218
- return this._waitToReleaseBundles.delete(bundleName);
8225
+ let views = this._viewCache.get(view.config.path);
8226
+ if (!views) {
8227
+ views = [view];
8228
+ this._viewCache.set(view.config.path, views);
8229
+ return true;
8230
+ }
8231
+ else if (!views.includes(view)) {
8232
+ views.push(view);
8233
+ return true;
8234
+ }
8235
+ return false;
8219
8236
  }
8220
8237
  /**
8221
- * 真正移除bundle
8222
- * @param bundle 目标bundle
8238
+ * 从缓存列表中移除视图。
8239
+ * @param view
8223
8240
  * @returns
8224
8241
  */
8225
- _onRemoveBundle(bundle) {
8226
- if (bundle && bundle.name) {
8227
- //删除bundle的加载promise
8228
- this._loadBunldePromiseMap.delete(bundle.name);
8229
- //删除bundle对应的资源缓存
8230
- this._loadPromiseMap.forEach((value, fullPath) => {
8231
- if (this.parsePath(fullPath).bundle == bundle.name) {
8232
- this._loadPromiseMap.delete(fullPath);
8233
- }
8234
- });
8235
- if (cc.isValid(bundle)) {
8236
- bundle.releaseAll();
8237
- cc.assetManager.removeBundle(bundle);
8238
- }
8239
- //删除等待移除的bundle
8240
- this._waitToReleaseBundles.delete(bundle.name);
8242
+ cancelCache(view) {
8243
+ if (!view || !view.config || !view.config.path) {
8244
+ return false;
8245
+ }
8246
+ let views = this._viewCache.get(view.config.path);
8247
+ if (!views || views.length == 0) {
8248
+ return false;
8249
+ }
8250
+ const index = views.indexOf(view);
8251
+ if (index != -1) {
8252
+ views.splice(index, 1);
8241
8253
  return true;
8242
8254
  }
8243
8255
  return false;
8244
8256
  }
8245
8257
  /**
8246
- * 开始Bundle GC,只执行一次
8247
- * @returns
8258
+ * [快捷方式]增加视图打开的事件监听
8259
+ * @param targetView 目标视图Class|名称
8260
+ * @param listener 响应回调
8261
+ * @param caller 回调this
8262
+ * @param once 是否只监听对应视图打开一次。
8263
+ * @param args 其他参数
8248
8264
  */
8249
- _startGC() {
8250
- if (this._startedGC) {
8251
- return;
8252
- }
8253
- this._startedGC = true;
8254
- timer.loop(GC_CD, this, this._onGC);
8265
+ onViewOpen(targetView, caller, listener, once = false, ...args) {
8266
+ this._viewOpenEventMap = this._viewOpenEventMap || new Map();
8267
+ this._viewOpenEventMap.set(listener, { event: "open", viewConfig: this._getViewConfig(targetView), caller, listener, once, args });
8268
+ this.offViewEvent("open", this, this._onViewOpen);
8269
+ this.onViewEvent("open", this, this._onViewOpen);
8270
+ }
8271
+ _onViewOpen(viewName, ...args) {
8272
+ this._viewOpenEventMap.forEach((value, key) => {
8273
+ if (value.viewConfig.name == viewName) {
8274
+ value.listener.apply(value.caller, value.args);
8275
+ value.once && this._viewOpenEventMap.delete(key);
8276
+ }
8277
+ });
8255
8278
  }
8256
8279
  /**
8257
- * GC Bundle
8280
+ * [快捷方式]移除视图打开的事件监听
8281
+ * @param listener
8258
8282
  * @returns
8259
8283
  */
8260
- _onGC() {
8261
- const count = this._waitToReleaseBundles.size;
8262
- if (count == 0) {
8263
- return;
8264
- }
8265
- const now = Date.now();
8266
- this._waitToReleaseBundles.forEach((bundle) => {
8267
- console.log(`[GCBundle] Remove Bundle:${bundle.name}`);
8268
- this._onRemoveBundle(bundle);
8269
- });
8270
- this._waitToReleaseBundles.clear();
8271
- console.log(`[GCBundle] [${count}] cost:${Date.now() - now} milliseconds`);
8284
+ offViewOpen(listener) {
8285
+ return this._viewOpenEventMap && this._viewOpenEventMap.delete(listener);
8272
8286
  }
8273
8287
  /**
8274
- * 手动设置bundle的版本,可新增可以覆盖。
8275
- * @param map
8288
+ * [快捷方式]增加视图关闭的事件监听
8289
+ * @param targetView 目标视图Class|名称
8290
+ * @param listener 响应回调
8291
+ * @param caller 回调this
8292
+ * @param once 是否只监听对应视图关闭一次。
8293
+ * @param args 其他参数
8276
8294
  */
8277
- setBundleVersion(map) {
8278
- this._bundleVersionMap = this._bundleVersionMap || new Map();
8279
- map.forEach((value, key) => {
8280
- this._bundleVersionMap.set(value, key);
8281
- });
8295
+ onViewClose(targetView, caller, listener, once = false, ...args) {
8296
+ this._viewCloseEventMap = this._viewCloseEventMap || new Map();
8297
+ this._viewCloseEventMap.set(listener, { event: "close", viewConfig: this._getViewConfig(targetView), caller, listener, once, args });
8298
+ this.offViewEvent("close", this, this._onViewClose);
8299
+ this.onViewEvent("close", this, this._onViewClose);
8282
8300
  }
8283
8301
  /**
8284
- * 获取bundle的版本
8285
- * @param bundleName
8302
+ * [快捷方式]移除视图关闭的事件监听
8303
+ * @param listener
8286
8304
  * @returns
8287
8305
  */
8288
- _getBundleVersion(bundleName) {
8289
- return this._bundleVersionMap && this._bundleVersionMap.get(bundleName);
8306
+ offViewClose(listener) {
8307
+ return this._viewCloseEventMap && this._viewCloseEventMap.delete(listener);
8290
8308
  }
8291
- }
8292
- /**
8293
- * cocos资源加载和管理
8294
- */
8295
- const res = new ResourceManager();
8296
-
8297
- class EffectAudioSourceProxy {
8298
- constructor() {
8299
- this._audioNode = null;
8300
- this._audioSource = null;
8301
- this._onFinishCallBack = null;
8302
- this._audioNode = cc.find("__audioNode__");
8303
- if (!this._audioNode) {
8304
- this._audioNode = new cc.Node();
8305
- this._audioNode.name = '__audioNode__';
8306
- cc.director.getScene().addChild(this._audioNode);
8307
- cc.director.addPersistRootNode(this._audioNode);
8308
- }
8309
- this._audioSource = this._audioNode.addComponent(cc.AudioSource);
8309
+ _onViewClose(viewName, ...args) {
8310
+ this._viewCloseEventMap.forEach((value, key) => {
8311
+ if (value.viewConfig.name == viewName) {
8312
+ value.listener.apply(value.caller, value.args.concat(args));
8313
+ value.once && this._viewCloseEventMap.delete(key);
8314
+ }
8315
+ });
8310
8316
  }
8311
8317
  /**
8312
- * 声音大小
8318
+ * 增加视图事件监听
8319
+ * @param event 视图事件名称
8320
+ * @param caller 回调this
8321
+ * @param listener 响应回调
8322
+ * @param once 是否只监听对应视图关闭一次。
8323
+ * @param args 其他参数
8313
8324
  */
8314
- set volume(value) {
8315
- if (!this._audioNode || !this._audioSource) {
8316
- return;
8325
+ onViewEvent(event, caller, listener, once = true, ...args) {
8326
+ if (!this._viewEvent) {
8327
+ this._viewEvent = new EventDispatcher();
8317
8328
  }
8318
- this._audioSource.volume = value;
8319
- }
8320
- /**
8321
- * 声音大小
8322
- */
8323
- get volume() {
8324
- if (!this._audioNode || !this._audioSource) {
8325
- return 1;
8329
+ if (once) {
8330
+ this._viewEvent.once(event, caller, listener, args);
8331
+ }
8332
+ else {
8333
+ this._viewEvent.on(event, caller, listener, args);
8326
8334
  }
8327
- return this._audioSource.volume;
8328
8335
  }
8329
8336
  /**
8330
- * 渐进式播放
8331
- * @param clip
8332
- * @param volume
8333
- * @param loop
8334
- * @param onFinishCallBack
8337
+ * 移除视图事件监听
8338
+ * @param event 事件类型
8339
+ * @param caller 事件侦听函数的执行域。
8340
+ * @param listener 事件侦听函数。
8335
8341
  * @returns
8336
8342
  */
8337
- playfade(clip, volume, loop = false, onFinishCallBack) {
8338
- this.play(clip, volume, loop, onFinishCallBack);
8339
- return this.fadeIn();
8343
+ offViewEvent(event, caller, listener) {
8344
+ if (!this._viewEvent) {
8345
+ return;
8346
+ }
8347
+ this._viewEvent.off(event, caller, listener);
8340
8348
  }
8341
8349
  /**
8342
- * 播放
8343
- * @param clip
8344
- * @param volume
8345
- * @param loop
8346
- * @param onFinishCallBack
8347
- * @returns
8350
+ * 广播视图事件
8351
+ * @param event 视图事件
8352
+ * @param view 视图
8348
8353
  */
8349
- play(clip, volume, loop = false, onFinishCallBack) {
8350
- this._audioSource.clip = clip;
8351
- this._audioSource.loop = loop;
8352
- this._audioSource.volume = volume;
8353
- this._audioSource.playOnAwake = false;
8354
- this._onFinishCallBack = onFinishCallBack;
8355
- this._audioNode.on(cc.AudioSource.EventType.ENDED, this._onAudioEnded, this);
8356
- this._audioSource.play();
8357
- return this;
8354
+ event(event, view, ...args) {
8355
+ if (this._viewEvent) {
8356
+ args.unshift(view.name);
8357
+ this._viewEvent.event(event, args);
8358
+ }
8358
8359
  }
8359
8360
  /**
8360
- * 渐进
8361
- * @returns
8361
+ * 上报视图统计数据(open\close\...)
8362
+ * @param event 事件
8363
+ * @param view 视图
8364
+ * @param data 上报附带信息
8362
8365
  */
8363
- fadeIn() {
8364
- return new Promise((resolve) => {
8365
- const targetVolume = this.volume;
8366
- cc.tween(this).set({ volume: 0 }).to(0.2, { volume: targetVolume }).call(() => {
8367
- resolve(this);
8368
- }).start();
8369
- });
8366
+ reportViewAnalytics(event, view, data) {
8367
+ let maskViewconfig = this._currentMaskView && this._currentMaskView.config;
8368
+ if (maskViewconfig && view.name == maskViewconfig.name) {
8369
+ return;
8370
+ }
8371
+ let analyticsInfo = this._viewAnalyticsMap.get(view.name);
8372
+ if (analyticsInfo) {
8373
+ data = data || (view.getAnalyticsDataOnViewEvent && view.getAnalyticsDataOnViewEvent(event)) || {};
8374
+ data.action = event;
8375
+ data.name = analyticsInfo.name;
8376
+ data.group = analyticsInfo.group || "default";
8377
+ //open:进入时刻, close:停留时长
8378
+ data.stay = Math.floor((App.durationOfLaunch - (analyticsInfo.lastOpenTime || 0)) / 1000);
8379
+ analytics.base.report("view", data);
8380
+ analyticsInfo.lastOpenTime = event == "open" ? App.durationOfLaunch : 0;
8381
+ }
8370
8382
  }
8371
- /**
8372
- * 渐出
8373
- * @param stop
8374
- * @returns
8375
- */
8376
- fedeOut(stop = true) {
8377
- return new Promise((resolve) => {
8378
- cc.tween(this).to(0.2, { volume: 0 }).call(() => {
8379
- stop && this.stop();
8380
- resolve(this);
8381
- }).start();
8382
- });
8383
+ get impl() {
8384
+ if (this._impl == null) {
8385
+ this._impl = Injector.getInject(ViewManager.KEY);
8386
+ }
8387
+ if (this._impl == null) {
8388
+ throw new Error(ViewManager.KEY + "未注入!");
8389
+ }
8390
+ return this._impl;
8383
8391
  }
8384
- /**
8385
- * 暂停
8386
- * @returns
8387
- */
8388
- pause() {
8389
- if (!this._audioNode || !this._audioSource) {
8392
+ }
8393
+ ViewManager.KEY = "ViewManager";
8394
+ ViewManager.created = false;
8395
+ /**
8396
+ * 视图管理器
8397
+ */
8398
+ const viewManager = new ViewManager();
8399
+
8400
+ class INativeHelper {
8401
+ constructor() {
8402
+ this._callbackCount = 0;
8403
+ }
8404
+ call(className, methodName, ...args) {
8405
+ this.callWithReturnType(className, methodName, null, ...args);
8406
+ }
8407
+ registerCallback(caller, method, times, ...args) {
8408
+ if (caller == null || method == null) {
8409
+ return "";
8410
+ }
8411
+ ++this._callbackCount;
8412
+ let callbackName = 'NativeHelper.callback' + this._callbackCount;
8413
+ window[callbackName] = () => {
8414
+ method.apply(caller, args);
8415
+ if (--times <= 0) {
8416
+ window[callbackName] = null;
8417
+ }
8418
+ };
8419
+ return `window['${callbackName}']()`;
8420
+ }
8421
+ }
8422
+
8423
+ /**
8424
+ @name: NativeHelper
8425
+ @desc: cocos 2.4.x 下nativeHelper
8426
+ @author: timoo
8427
+ @date: 2022/12/02
8428
+ */
8429
+ class CocosNativeHelper extends INativeHelper {
8430
+ callWithReturnType(className, methodName, returnType, ...args) {
8431
+ if (!className) {
8390
8432
  return;
8391
8433
  }
8392
- this._audioSource.pause();
8393
- }
8394
- /**
8395
- * 恢复
8396
- * @returns
8397
- */
8398
- resume() {
8399
- if (!this._audioNode || !this._audioSource) {
8400
- return;
8434
+ if (cc.sys.os == cc.sys.OS.ANDROID) {
8435
+ let argsSig = this.getArgsSig(args, returnType);
8436
+ console.log("callWithReturnType:", className, methodName, argsSig, args.join(","));
8437
+ return cc.native.reflection.callStaticMethod(className, methodName, argsSig, ...args);
8401
8438
  }
8402
- this._audioSource.play();
8403
- }
8404
- /**
8405
- * 渐进停止
8406
- * @returns
8407
- */
8408
- stopFade() {
8409
- return this.fedeOut(true).then(() => { });
8439
+ else if (cc.sys.os == cc.sys.OS.IOS) ;
8410
8440
  }
8411
- /**
8412
- * 停止
8413
- * @returns
8414
- */
8415
- stop() {
8416
- if (!this._audioNode || !this._audioSource) {
8417
- return;
8441
+ getArgsSig(args, returnType) {
8442
+ let sig = ["("];
8443
+ for (let i = 0; i < args.length; i++) {
8444
+ const element = args[i];
8445
+ sig.push(this.getValueTypeStr(element));
8418
8446
  }
8419
- this._audioSource.stop();
8420
- this._audioNode.off(cc.AudioSource.EventType.ENDED, this._onAudioEnded, this);
8447
+ sig.push(")", this.getValueTypeStr(returnType));
8448
+ return sig.join("");
8421
8449
  }
8422
- _onAudioEnded(audioSource) {
8423
- if (audioSource == this._audioSource) {
8424
- this._onFinishCallBack && this._onFinishCallBack();
8425
- this._audioNode.off(cc.AudioSource.EventType.ENDED, this._onAudioEnded, this);
8450
+ getValueTypeStr(value) {
8451
+ if (value == null || value == undefined) {
8452
+ return "V";
8453
+ }
8454
+ let typeStr = typeof value;
8455
+ if (typeStr == "number") {
8456
+ if (value % 1 != 0) { //float
8457
+ return "F";
8458
+ }
8459
+ else {
8460
+ return "I";
8461
+ }
8462
+ }
8463
+ else if (typeStr == "boolean") {
8464
+ return "Z";
8465
+ }
8466
+ else if (typeStr == "string") {
8467
+ return "Ljava/lang/String;";
8426
8468
  }
8427
8469
  }
8428
- /**
8429
- * 是否循环
8430
- */
8431
- get loop() {
8432
- return this._audioSource ? this._audioSource.loop : false;
8470
+ }
8471
+
8472
+ /*
8473
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
8474
+ * Digest Algorithm, as defined in RFC 1321.
8475
+ * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
8476
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
8477
+ * Distributed under the BSD License
8478
+ * See http://pajhome.org.uk/crypt/md5 for more info.
8479
+ */
8480
+ /*
8481
+ * Configurable variables. You may need to tweak these to be compatible with
8482
+ * the server-side, but the defaults work in most cases.
8483
+ */
8484
+ var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
8485
+ /*
8486
+ * These are the functions you'll usually want to call
8487
+ * They take string arguments and return either hex or base-64 encoded strings
8488
+ */
8489
+ function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
8490
+ /*
8491
+ * Calculate the MD5 of a raw string
8492
+ */
8493
+ function rstr_md5(s) {
8494
+ return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
8495
+ }
8496
+ /*
8497
+ * Convert a raw string to a hex string
8498
+ */
8499
+ function rstr2hex(input) {
8500
+ try {
8501
+ hexcase;
8433
8502
  }
8434
- /**
8435
- * 是否播放中
8436
- */
8437
- get playing() {
8438
- return this._audioSource ? this._audioSource.playing : false;
8503
+ catch (e) {
8504
+ hexcase = 0;
8439
8505
  }
8440
- reset() {
8441
- this.stop();
8442
- cc.Tween.stopAllByTarget(this);
8443
- this._onFinishCallBack = null;
8506
+ var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
8507
+ var output = "";
8508
+ var x;
8509
+ for (var i = 0; i < input.length; i++) {
8510
+ x = input.charCodeAt(i);
8511
+ output += hex_tab.charAt((x >>> 4) & 0x0F)
8512
+ + hex_tab.charAt(x & 0x0F);
8444
8513
  }
8445
- destroy() {
8446
- this._audioSource.destroy();
8447
- this._audioSource = null;
8448
- this._audioNode = null;
8449
- this._onFinishCallBack = null;
8514
+ return output;
8515
+ }
8516
+ /*
8517
+ * Encode a string as utf-8.
8518
+ * For efficiency, this assumes the input is valid utf-16.
8519
+ */
8520
+ function str2rstr_utf8(input) {
8521
+ var output = "";
8522
+ var i = -1;
8523
+ var x, y;
8524
+ while (++i < input.length) {
8525
+ /* Decode utf-16 surrogate pairs */
8526
+ x = input.charCodeAt(i);
8527
+ y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
8528
+ if (0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) {
8529
+ x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
8530
+ i++;
8531
+ }
8532
+ /* Encode output as utf-8 */
8533
+ if (x <= 0x7F)
8534
+ output += String.fromCharCode(x);
8535
+ else if (x <= 0x7FF)
8536
+ output += String.fromCharCode(0xC0 | ((x >>> 6) & 0x1F), 0x80 | (x & 0x3F));
8537
+ else if (x <= 0xFFFF)
8538
+ output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 0x80 | ((x >>> 6) & 0x3F), 0x80 | (x & 0x3F));
8539
+ else if (x <= 0x1FFFFF)
8540
+ output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 0x80 | ((x >>> 12) & 0x3F), 0x80 | ((x >>> 6) & 0x3F), 0x80 | (x & 0x3F));
8541
+ }
8542
+ return output;
8543
+ }
8544
+ /*
8545
+ * Convert a raw string to an array of little-endian words
8546
+ * Characters >255 have their high-byte silently ignored.
8547
+ */
8548
+ function rstr2binl(input) {
8549
+ var output = Array(input.length >> 2);
8550
+ for (var i = 0; i < output.length; i++)
8551
+ output[i] = 0;
8552
+ for (var i = 0; i < input.length * 8; i += 8)
8553
+ output[i >> 5] |= (input.charCodeAt(i / 8) & 0xFF) << (i % 32);
8554
+ return output;
8555
+ }
8556
+ /*
8557
+ * Convert an array of little-endian words to a string
8558
+ */
8559
+ function binl2rstr(input) {
8560
+ var output = "";
8561
+ for (var i = 0; i < input.length * 32; i += 8)
8562
+ output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xFF);
8563
+ return output;
8564
+ }
8565
+ /*
8566
+ * Calculate the MD5 of an array of little-endian words, and a bit length.
8567
+ */
8568
+ function binl_md5(x, len) {
8569
+ /* append padding */
8570
+ x[len >> 5] |= 0x80 << ((len) % 32);
8571
+ x[(((len + 64) >>> 9) << 4) + 14] = len;
8572
+ var a = 1732584193;
8573
+ var b = -271733879;
8574
+ var c = -1732584194;
8575
+ var d = 271733878;
8576
+ for (var i = 0; i < x.length; i += 16) {
8577
+ var olda = a;
8578
+ var oldb = b;
8579
+ var oldc = c;
8580
+ var oldd = d;
8581
+ a = md5_ff(a, b, c, d, x[i + 0], 7, -680876936);
8582
+ d = md5_ff(d, a, b, c, x[i + 1], 12, -389564586);
8583
+ c = md5_ff(c, d, a, b, x[i + 2], 17, 606105819);
8584
+ b = md5_ff(b, c, d, a, x[i + 3], 22, -1044525330);
8585
+ a = md5_ff(a, b, c, d, x[i + 4], 7, -176418897);
8586
+ d = md5_ff(d, a, b, c, x[i + 5], 12, 1200080426);
8587
+ c = md5_ff(c, d, a, b, x[i + 6], 17, -1473231341);
8588
+ b = md5_ff(b, c, d, a, x[i + 7], 22, -45705983);
8589
+ a = md5_ff(a, b, c, d, x[i + 8], 7, 1770035416);
8590
+ d = md5_ff(d, a, b, c, x[i + 9], 12, -1958414417);
8591
+ c = md5_ff(c, d, a, b, x[i + 10], 17, -42063);
8592
+ b = md5_ff(b, c, d, a, x[i + 11], 22, -1990404162);
8593
+ a = md5_ff(a, b, c, d, x[i + 12], 7, 1804603682);
8594
+ d = md5_ff(d, a, b, c, x[i + 13], 12, -40341101);
8595
+ c = md5_ff(c, d, a, b, x[i + 14], 17, -1502002290);
8596
+ b = md5_ff(b, c, d, a, x[i + 15], 22, 1236535329);
8597
+ a = md5_gg(a, b, c, d, x[i + 1], 5, -165796510);
8598
+ d = md5_gg(d, a, b, c, x[i + 6], 9, -1069501632);
8599
+ c = md5_gg(c, d, a, b, x[i + 11], 14, 643717713);
8600
+ b = md5_gg(b, c, d, a, x[i + 0], 20, -373897302);
8601
+ a = md5_gg(a, b, c, d, x[i + 5], 5, -701558691);
8602
+ d = md5_gg(d, a, b, c, x[i + 10], 9, 38016083);
8603
+ c = md5_gg(c, d, a, b, x[i + 15], 14, -660478335);
8604
+ b = md5_gg(b, c, d, a, x[i + 4], 20, -405537848);
8605
+ a = md5_gg(a, b, c, d, x[i + 9], 5, 568446438);
8606
+ d = md5_gg(d, a, b, c, x[i + 14], 9, -1019803690);
8607
+ c = md5_gg(c, d, a, b, x[i + 3], 14, -187363961);
8608
+ b = md5_gg(b, c, d, a, x[i + 8], 20, 1163531501);
8609
+ a = md5_gg(a, b, c, d, x[i + 13], 5, -1444681467);
8610
+ d = md5_gg(d, a, b, c, x[i + 2], 9, -51403784);
8611
+ c = md5_gg(c, d, a, b, x[i + 7], 14, 1735328473);
8612
+ b = md5_gg(b, c, d, a, x[i + 12], 20, -1926607734);
8613
+ a = md5_hh(a, b, c, d, x[i + 5], 4, -378558);
8614
+ d = md5_hh(d, a, b, c, x[i + 8], 11, -2022574463);
8615
+ c = md5_hh(c, d, a, b, x[i + 11], 16, 1839030562);
8616
+ b = md5_hh(b, c, d, a, x[i + 14], 23, -35309556);
8617
+ a = md5_hh(a, b, c, d, x[i + 1], 4, -1530992060);
8618
+ d = md5_hh(d, a, b, c, x[i + 4], 11, 1272893353);
8619
+ c = md5_hh(c, d, a, b, x[i + 7], 16, -155497632);
8620
+ b = md5_hh(b, c, d, a, x[i + 10], 23, -1094730640);
8621
+ a = md5_hh(a, b, c, d, x[i + 13], 4, 681279174);
8622
+ d = md5_hh(d, a, b, c, x[i + 0], 11, -358537222);
8623
+ c = md5_hh(c, d, a, b, x[i + 3], 16, -722521979);
8624
+ b = md5_hh(b, c, d, a, x[i + 6], 23, 76029189);
8625
+ a = md5_hh(a, b, c, d, x[i + 9], 4, -640364487);
8626
+ d = md5_hh(d, a, b, c, x[i + 12], 11, -421815835);
8627
+ c = md5_hh(c, d, a, b, x[i + 15], 16, 530742520);
8628
+ b = md5_hh(b, c, d, a, x[i + 2], 23, -995338651);
8629
+ a = md5_ii(a, b, c, d, x[i + 0], 6, -198630844);
8630
+ d = md5_ii(d, a, b, c, x[i + 7], 10, 1126891415);
8631
+ c = md5_ii(c, d, a, b, x[i + 14], 15, -1416354905);
8632
+ b = md5_ii(b, c, d, a, x[i + 5], 21, -57434055);
8633
+ a = md5_ii(a, b, c, d, x[i + 12], 6, 1700485571);
8634
+ d = md5_ii(d, a, b, c, x[i + 3], 10, -1894986606);
8635
+ c = md5_ii(c, d, a, b, x[i + 10], 15, -1051523);
8636
+ b = md5_ii(b, c, d, a, x[i + 1], 21, -2054922799);
8637
+ a = md5_ii(a, b, c, d, x[i + 8], 6, 1873313359);
8638
+ d = md5_ii(d, a, b, c, x[i + 15], 10, -30611744);
8639
+ c = md5_ii(c, d, a, b, x[i + 6], 15, -1560198380);
8640
+ b = md5_ii(b, c, d, a, x[i + 13], 21, 1309151649);
8641
+ a = md5_ii(a, b, c, d, x[i + 4], 6, -145523070);
8642
+ d = md5_ii(d, a, b, c, x[i + 11], 10, -1120210379);
8643
+ c = md5_ii(c, d, a, b, x[i + 2], 15, 718787259);
8644
+ b = md5_ii(b, c, d, a, x[i + 9], 21, -343485551);
8645
+ a = safe_add(a, olda);
8646
+ b = safe_add(b, oldb);
8647
+ c = safe_add(c, oldc);
8648
+ d = safe_add(d, oldd);
8450
8649
  }
8650
+ return Array(a, b, c, d);
8451
8651
  }
8452
- class AudioManager {
8453
- constructor() {
8454
- this._musicVolume = null;
8455
- this._effectVolume = null;
8456
- this._effectPool = new Pool(EffectAudioSourceProxy); //cc.AudioSource.maxAudioChannel - 1);
8457
- }
8458
- /**
8459
- * 音效音量大小
8460
- */
8461
- get effectVolume() {
8462
- if (this._effectVolume == null) {
8463
- this._effectVolume = storage.effectsVolume;
8464
- }
8465
- return this._effectVolume;
8466
- }
8467
- /**
8468
- * 音效音量大小
8469
- */
8470
- set effectVolume(value) {
8471
- storage.effectsVolume = this._effectVolume = value;
8472
- let usingEffect = this._effectPool['_usingArray'];
8473
- usingEffect = usingEffect.concat();
8474
- for (let i = 0; i < usingEffect.length; i++) {
8475
- const effectProxy = usingEffect[i];
8476
- effectProxy.volume = value;
8477
- }
8478
- }
8479
- /**
8480
- * BGM音量大小
8481
- */
8482
- get musicVolume() {
8483
- if (this._musicVolume == null) {
8484
- this._musicVolume = storage.musicVolume;
8652
+ /*
8653
+ * These functions implement the four basic operations the algorithm uses.
8654
+ */
8655
+ function md5_cmn(q, a, b, x, s, t) {
8656
+ return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s), b);
8657
+ }
8658
+ function md5_ff(a, b, c, d, x, s, t) {
8659
+ return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
8660
+ }
8661
+ function md5_gg(a, b, c, d, x, s, t) {
8662
+ return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
8663
+ }
8664
+ function md5_hh(a, b, c, d, x, s, t) {
8665
+ return md5_cmn(b ^ c ^ d, a, b, x, s, t);
8666
+ }
8667
+ function md5_ii(a, b, c, d, x, s, t) {
8668
+ return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
8669
+ }
8670
+ /*
8671
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
8672
+ * to work around bugs in some JS interpreters.
8673
+ */
8674
+ function safe_add(x, y) {
8675
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
8676
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
8677
+ return (msw << 16) | (lsw & 0xFFFF);
8678
+ }
8679
+ /*
8680
+ * Bitwise rotate a 32-bit number to the left.
8681
+ */
8682
+ function bit_rol(num, cnt) {
8683
+ return (num << cnt) | (num >>> (32 - cnt));
8684
+ }
8685
+ const md5 = hex_md5;
8686
+
8687
+ /**
8688
+ * cocos下的本地持久化实现类
8689
+ */
8690
+ class CocosStorageUtils {
8691
+ removeValue(key) {
8692
+ if (!window['CC_DEBUG']) {
8693
+ key = md5(key);
8485
8694
  }
8486
- return this._musicVolume;
8487
- }
8488
- /**
8489
- * BGM音量大小
8490
- */
8491
- set musicVolume(value) {
8492
- storage.musicVolume = this._musicVolume = value;
8493
- this._music && (this._music.volume = value);
8494
- }
8495
- /**
8496
- * 播放音效
8497
- * @param clip
8498
- * @param loop 是否循环,默认为false。传入true,需要手动调用recoverEffect进行回收
8499
- * @param onFinishCallBack 播放完回调
8500
- * @param volume 当此播放的声音大小,默认为统一effect的大小
8501
- */
8502
- playEffect(clip, loop = false, onFinishCallBack, volume = null) {
8503
- const effectProxy = this._effectPool.allocate();
8504
- effectProxy.play(clip, volume == null ? this.effectVolume : volume, loop, () => {
8505
- if (!loop) {
8506
- this.recoverEffect(effectProxy);
8507
- }
8508
- else {
8509
- console.warn("需要手动回收音效播放器");
8510
- }
8511
- onFinishCallBack && onFinishCallBack();
8512
- });
8513
- return effectProxy;
8514
- }
8515
- /**
8516
- * 回收音效播放器,用于effect多次复用后的回收
8517
- * @param proxy
8518
- */
8519
- recoverEffect(proxy) {
8520
- this._effectPool.recover(proxy);
8695
+ sys.localStorage.removeItem(key);
8521
8696
  }
8522
- /**
8523
- * 停止所有音效
8524
- */
8525
- stopAllEffect() {
8526
- let usingEffect = this._effectPool['_usingArray'];
8527
- usingEffect = usingEffect.concat();
8528
- for (let i = 0; i < usingEffect.length; i++) {
8529
- const effectProxy = usingEffect[i];
8530
- effectProxy.stop();
8531
- this.recoverEffect(effectProxy);
8697
+ setValue(key, value) {
8698
+ if (!window['CC_DEBUG']) {
8699
+ key = md5(key);
8532
8700
  }
8533
- }
8534
- /**
8535
- * 播放背景音乐
8536
- * @param clip
8537
- * @param fade 是否渐进式播放,默认为true
8538
- * @param loop 是否循环,默认为true
8539
- * @param onFinishCallBack 播放完回调
8540
- * @param volume 当此播放的声音大小,默认为统一music的大小
8541
- */
8542
- playMusic(clip, fade = true, loop = true, onFinishCallBack, volume = null) {
8543
- if (!this._music) {
8544
- this._music = new EffectAudioSourceProxy();
8701
+ try {
8702
+ sys.localStorage.setItem(key, value);
8545
8703
  }
8546
- this._music.playing && this._music.stop();
8547
- volume = volume == null ? this.musicVolume : volume;
8548
- fade ? this._music.playfade(clip, volume, loop, onFinishCallBack) : this._music.play(clip, volume, loop, onFinishCallBack);
8549
- }
8550
- /**
8551
- * 暂停播放背景音乐
8552
- * @returns
8553
- */
8554
- pauseMusic() {
8555
- if (!this._music) {
8556
- return;
8704
+ catch (error) {
8705
+ console.error('Failed to set storage value:', "key:", key, "value:", value, "length:", String(value).length, "error:", error);
8557
8706
  }
8558
- this._music.pause();
8559
8707
  }
8560
- /**
8561
- * 恢复播放背景音乐
8562
- * @returns
8563
- */
8564
- resumeMusic() {
8565
- if (!this._music) {
8566
- return;
8708
+ getValue(key) {
8709
+ if (!window['CC_DEBUG']) {
8710
+ key = md5(key);
8567
8711
  }
8568
- this._music.resume();
8712
+ return sys.localStorage.getItem(key);
8569
8713
  }
8570
- /**
8571
- * 停止背景音乐
8572
- * @param fade 是否渐出
8573
- * @returns
8574
- */
8575
- stopMusic(fade = true) {
8576
- return __awaiter(this, void 0, void 0, function* () {
8577
- if (!this._music) {
8578
- return;
8579
- }
8580
- return fade ? yield this._music.stopFade() : this._music.stop();
8581
- });
8714
+ clear() {
8715
+ sys.localStorage.clear();
8582
8716
  }
8583
8717
  }
8718
+
8584
8719
  /**
8585
- * 音效管理
8720
+ * 任务队列
8586
8721
  */
8587
- const audio = new AudioManager();
8588
-
8589
- var View_1;
8590
- const { ccclass: ccclass$2, property: property$2, menu: menu$2 } = cc._decorator;
8591
- var ViewLevel;
8592
- (function (ViewLevel) {
8593
- ViewLevel[ViewLevel["TOPUP"] = 500] = "TOPUP";
8594
- ViewLevel[ViewLevel["NET"] = 400] = "NET";
8595
- ViewLevel[ViewLevel["TUTORIAL"] = 300] = "TUTORIAL";
8596
- ViewLevel[ViewLevel["ALERT"] = 200] = "ALERT";
8597
- ViewLevel[ViewLevel["UI"] = 100] = "UI";
8598
- ViewLevel[ViewLevel["BG"] = 1] = "BG";
8599
- })(ViewLevel || (ViewLevel = {}));
8600
- let View = View_1 = class View extends cc.Component {
8722
+ class TaskSequence extends Task {
8601
8723
  constructor() {
8602
- super(...arguments);
8603
- /**
8604
- * 是否可以交互
8605
- */
8606
- this._touchEnabled = true;
8607
- }
8608
- /**
8609
- * 是否在舞台中显示着
8610
- */
8611
- get inStage() {
8612
- return cc.isValid(this.node) && this.node.activeInHierarchy;
8613
- }
8614
- /**
8615
- * 视图是否响应交互
8616
- */
8617
- get touchEnabled() {
8618
- return this._touchEnabled;
8619
- }
8620
- /**
8621
- * 视图是否响应交互
8622
- */
8623
- set touchEnabled(value) {
8624
- this._touchEnabled = value;
8625
- if (value && !this._mask) {
8626
- return;
8627
- }
8628
- if (!this._mask) {
8629
- this._mask = new cc.Node();
8630
- this._mask.name = "mask";
8631
- let transform = this._mask.addComponent(cc.UITransform);
8632
- const viewSize = cc.view.getVisibleSize();
8633
- transform.setContentSize(viewSize.width, viewSize.height);
8634
- this._mask.addComponent(cc.BlockInputEvents);
8635
- // this.node.addChild(this.mask);
8636
- this.node.addChild(this._mask);
8637
- transform.priority = ViewLevel.TOPUP;
8638
- this._mask.layer = this.node.layer;
8639
- }
8640
- this._mask.active = !value;
8724
+ super();
8725
+ this._taskMap = new Map();
8726
+ this._index = 0;
8641
8727
  }
8642
- /**
8643
- * 添加其他视图
8644
- * @param view 其他视图
8645
- * @param index 视图索引
8646
- */
8647
- addView(view, index) {
8648
- view.setControl(this._control);
8649
- view.node.parent = this.node;
8650
- (view.node.getComponent(cc.UITransform) || view.node.addComponent(cc.UITransform)).priority = index;
8651
- viewManager.event("open", view);
8652
- viewManager.reportViewAnalytics("open", view);
8653
- analytics.session.updateSessionDuration();
8728
+ addQuickly(taskCaller, taskFun, ...taskData) {
8729
+ let task = new Task();
8730
+ taskData.unshift(task);
8731
+ task.start = (data) => {
8732
+ taskFun.apply(taskCaller, taskData);
8733
+ };
8734
+ this.addTask(task);
8735
+ return task;
8654
8736
  }
8655
- /**
8656
- * 移除子视图
8657
- * @param view 子视图
8658
- * @param destroy 是否销毁,默认为true
8659
- * @param closeEventParams 视图close事件的参数
8660
- */
8661
- removeView(view, destroy = true, ...closeEventParams) {
8662
- if (view.parentView == this) {
8663
- destroy && view.node.destroyAllChildren();
8664
- view.node.removeFromParent();
8665
- viewManager.event("close", view, ...closeEventParams);
8666
- viewManager.reportViewAnalytics("close", view);
8667
- analytics.session.updateSessionDuration();
8737
+ addTask(value, ...data) {
8738
+ if (this._taskMap.has(value)) {
8739
+ throw new Error("重复添加!");
8668
8740
  }
8741
+ this._taskMap.set(value, data);
8669
8742
  }
8670
- /**
8671
- * 设置主场景所创建的Control
8672
- * @param control
8673
- */
8674
- setControl(control) {
8675
- this._control = control;
8743
+ removeTask(value) {
8744
+ return this._taskMap.delete(value);
8676
8745
  }
8677
- control() {
8678
- return this._control;
8746
+ start(...data) {
8747
+ this._keys = this._taskMap.keys();
8748
+ this._index = 0;
8749
+ this._tryNext();
8679
8750
  }
8680
- /**
8681
- * 实例化后显示到舞台之后调用,缓存视图重新显示时也会调用。
8682
- * @param data
8683
- */
8684
- setData(...data) {
8751
+ _tryNext() {
8752
+ let next = this._keys.next();
8753
+ if (!next.done) {
8754
+ let task = next.value;
8755
+ task.on(Task.Event.COMPLETE, this, this._subTaskEventHandler);
8756
+ task.on(Task.Event.PROGRESS, this, this._subTaskEventHandler);
8757
+ task.on(Task.Event.ERROR, this, this._subTaskEventHandler);
8758
+ task.start(this._taskMap.get(task));
8759
+ }
8760
+ else {
8761
+ //结束
8762
+ this.onComplete();
8763
+ }
8685
8764
  }
8686
- /**
8687
- * 上报当前视图统计数据
8688
- * @param event open和close以外的统计名称,名称必须为全小写,无下划线之外的其他特殊字符。
8689
- * @param data 上报附带信息,可选参数,为空时会尝试从getAnalyticsDataOnViewEvent获取附带信息。
8690
- */
8691
- _report(event, data) {
8692
- if (event == "open" || event == "close")
8765
+ _subTaskEventHandler(key, target, data) {
8766
+ if (key == Task.Event.ERROR) {
8767
+ this.onError(data.code, data.msg);
8693
8768
  return;
8694
- viewManager.reportViewAnalytics(event, this, data);
8769
+ }
8770
+ if (key == Task.Event.COMPLETE) {
8771
+ target.offAllEvent();
8772
+ target.destroy();
8773
+ this._index++;
8774
+ this.onProgress(this._index / this._taskMap.size);
8775
+ this._tryNext();
8776
+ }
8695
8777
  }
8696
- /**
8697
- * 播放BG音乐
8698
- * @param path 路径 - `bundleName://path/to/asset` 目标音频地址。
8699
- * @param fade 是否渐进式播放,默认为true
8700
- * @param loop 是否循环 默认为true
8701
- * @param onFinishCallBack 单次播放结束后的回调箭头函数(cocos的锅)
8702
- * @returns 背景音乐的audioId
8703
- *
8704
- * @example
8705
- * _playBG("https://server.com/game/music.mp3")
8706
- * _playBG("main://path/to/music")
8707
- * _playBG("resources://path/to/music")
8708
- */
8709
- playBG(path, fade = true, loop = true, onFinishCallBack, volume = null) {
8710
- return __awaiter(this, void 0, void 0, function* () {
8711
- let clip = yield res.load(path);
8712
- if (clip) {
8713
- audio.playMusic(clip, fade, loop, onFinishCallBack, volume);
8714
- }
8778
+ reset() {
8779
+ this.offAllEvent();
8780
+ this._taskMap.forEach((data, task) => {
8781
+ task.reset();
8715
8782
  });
8783
+ this._taskMap.clear();
8784
+ this._keys = null;
8785
+ this._index = 0;
8716
8786
  }
8717
- /**
8718
- * 播放音效
8719
- * @param path 路径 - `bundleName://path/to/asset` 目标音频地址。
8720
- * @param onFinishCallBack 单次播放结束后的回调箭头函数(cocos的锅)
8721
- * @returns 音效的audioId
8722
- */
8723
- playEffect(path, onFinishCallBack, volume = null) {
8724
- return __awaiter(this, void 0, void 0, function* () {
8725
- let clip = yield res.load(path);
8726
- return new Promise((reslove) => {
8727
- if (clip) {
8728
- audio.playEffect(clip, false, () => {
8729
- onFinishCallBack && onFinishCallBack();
8730
- reslove();
8731
- }, volume);
8732
- }
8733
- });
8787
+ destroy() {
8788
+ super.destroy();
8789
+ this._taskMap.forEach((data, task) => {
8790
+ task.reset();
8734
8791
  });
8792
+ this._taskMap.clear();
8793
+ this._keys = null;
8794
+ this._index = 0;
8735
8795
  }
8736
- /**
8737
- * 播放激励视频的快捷方式。
8738
- * @param from 可选参数,可以附带广告行为的来源,以及其他需要统计的信息
8739
- * @param stopRecorder 可选参数,是否暂停录屏, 默认为true
8740
- */
8741
- showRewardVideo(from, stopRecorder = true) {
8742
- return new Promise((resolve) => {
8743
- stopRecorder && publisher.record && publisher.record.pauseRecord();
8744
- viewManager.closeTouch();
8745
- //解决部分平台(tt)拉起视频时,最小化视频播放不出来的bug。
8746
- const openTouch = () => {
8747
- viewManager.openTouch();
8748
- App.offShow(openTouch);
8749
- };
8750
- App.onShow(openTouch);
8751
- from = from || {};
8752
- // from.name = this.analyticsInfo ? this.analyticsInfo.name : this.name;
8753
- publisher.ad.showRewardVideo(from).then((result) => {
8754
- openTouch();
8755
- stopRecorder && publisher.record && publisher.record.resumeRecord();
8756
- resolve(result);
8757
- }).catch(() => {
8758
- openTouch();
8759
- stopRecorder && publisher.record && publisher.record.resumeRecord();
8760
- resolve(false);
8761
- });
8762
- });
8796
+ }
8797
+
8798
+ /**
8799
+ * 视图加载器
8800
+ */
8801
+ class IViewLoader extends EventDispatcher {
8802
+ constructor(pool) {
8803
+ super();
8804
+ this._initializer = new TaskSequence();
8805
+ this._pool = pool;
8763
8806
  }
8764
- /**
8765
- * 分享录屏视频
8766
- * @param analyticsName 玩法发起分享的动作来源,用于统计。
8767
- * @param title 视频标题
8768
- * @param topics 视频话题列表
8769
- */
8770
- shareRecord(analyticsName, title, topics) {
8771
- let path = publisher.record.videoPath;
8772
- if (path) {
8773
- return publisher.share.shareVideo(analyticsName, { "video_title": title, "videoTopics": topics, "videoPath": path });
8774
- }
8775
- else {
8776
- return new Promise((resolve) => { resolve(false); });
8807
+ load(task) {
8808
+ if (this._asset) {
8809
+ task && task.onComplete();
8810
+ return;
8777
8811
  }
8778
- }
8779
- /**
8780
- * 子项重写时,必须要调用super.onDestroy()
8781
- *
8782
- */
8783
- onDestroy() {
8784
- this.config.cache && viewManager.cancelCache(this);
8785
- }
8786
- /**
8787
- * 父级视图
8788
- */
8789
- get parentView() {
8790
- if (this.node.parent) {
8791
- return this.node.parent.getComponent(View_1);
8812
+ if (task) {
8813
+ this._initializer.once(Task.Event.ERROR, this, (type, a, error) => {
8814
+ task.onError(error.code, error.msg);
8815
+ });
8816
+ this._initializer.on(Task.Event.PROGRESS, this, (type, a, percent) => {
8817
+ task.onProgress(percent);
8818
+ });
8792
8819
  }
8793
- return null;
8794
- }
8795
- /**
8796
- * 关闭视图,根据视图是否缓存选择是否销毁视图
8797
- * @param closeEventParams 视图close事件的参数
8798
- */
8799
- close(...closeEventParams) {
8800
- this.removeFromParent(!this.config.cache, ...closeEventParams);
8820
+ this._initializer.once(Task.Event.COMPLETE, this, () => {
8821
+ this._initializer.reset();
8822
+ task && task.onComplete();
8823
+ });
8824
+ this._initializer.start();
8801
8825
  }
8802
- /**
8803
- * 从父级移除视图
8804
- * @param destroy 是否销毁,默认为true
8805
- * @param closeEventParams 视图close事件的参数
8806
- */
8807
- removeFromParent(destroy = true, ...closeEventParams) {
8808
- if (this.parentView) {
8809
- this.parentView.removeView(this, destroy, ...closeEventParams);
8810
- }
8826
+ destroy() {
8827
+ this.offAll();
8828
+ this._initializer.reset();
8829
+ this._initializer = null;
8830
+ this._asset = null;
8831
+ this._pool = null;
8811
8832
  }
8812
- /**
8813
- * 直接销毁
8814
- */
8815
- allDestroy() {
8816
- if (this.node && cc.isValid(this.node)) {
8817
- this.node.parent = null;
8818
- this.node.destroy();
8819
- }
8833
+ reset() {
8834
+ this._initializer.reset();
8835
+ this.offAll();
8836
+ this._asset = null;
8820
8837
  }
8821
- getAnalyticsDataOnViewEvent(event) {
8822
- return {};
8838
+ recover() {
8839
+ this._pool.recover(this);
8823
8840
  }
8824
- };
8825
- /**
8826
- * View类的Class名称
8827
- */
8828
- View.type = "View";
8829
- View = View_1 = __decorate([
8830
- ccclass$2("View"),
8831
- menu$2("基础视图/View")
8832
- /**
8833
- * cocos引擎下的视图管理类
8834
- */
8835
- ], View);
8841
+ }
8836
8842
 
8837
8843
  /**
8838
8844
  * 注册View配置的装饰器(CCPViewLoader)
@@ -8878,14 +8884,10 @@ function viewClass(data) {
8878
8884
  }
8879
8885
  /**
8880
8886
  * 注册closeTouch默认遮罩视图
8881
- * @param data 视图配置
8882
8887
  */
8883
- function maskView(data) {
8888
+ function maskView() {
8884
8889
  return function (target, propertyKey) {
8885
- data.cache = true;
8886
- data.zIndex = ViewLevel.TOPUP;
8887
- viewClass(data)(target, propertyKey);
8888
- viewManager.registerMaskView(data);
8890
+ viewManager.registerMaskView(target);
8889
8891
  };
8890
8892
  }
8891
8893
  /**