123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- 'use strict';
- const logger = require('Logger').getLogger('Navigator.js');
- /**
- * 导航模式
- * @type {{New: number, Back: number, Refresh: number}}
- */
- const navigatorMode = {
- New: 0,
- Back: 1,
- Refresh: 2
- };
- /**
- * Navigator介绍:
- * ------------------------
- * 提供一个支持导航栈的Navigator类,支持以下特性
- * 0,记录场景切换的导航栈。
- * 1,场景之间可以传递参数,比如场景A要传个字符串给场景B。
- * 2,多个场景进入同一场景后,从场景返回前一个场景,不需要再判断前一个场景,可以直接goBack返回。
- * 3,支持场景返回后页面数据恢复,比如场景A界面,输入框输入了一段文字,然后进入场景B,
- * 从场景B返回后可以恢复输入框文字(需要在场景A脚本实现固定接口支持)。
- *
- * Navigator使用方法:
- * ------------------------
- * a)在场景A向前加载新场景B[带参数][带回调]
- * /// 默认
- * navigator.navigate('B');
- *
- * /// [带参数]
- * let parameter = {};
- * parameter.title = 'i am wang ronghui';
- * navigator.navigate('B', parameter);
- *
- * /// [带回调]
- * navigator.navigate('B', function(scene){
- * /// 切换成功处理
- * });
- *
- * /// [带参数] + [带回调]
- * let parameter = {};
- * parameter.title = 'i am wang ronghui';
- * navigator.navigate('B', parameter, function(scene){
- * /// 切换成功处理
- * });
- *
- * ~如果有传递parameter需在相应B.js内部实现loadState(navigatorMode, parameter, state)函数接收参数parameter。
- * ~如果要存储当前UI状态则实现saveState(state){ //将UI状态存储在参数state中,后续在loadState里恢复state }。
- *
- * c)场景B向后返回前一个场景A
- * /// 默认
- * navigator.goBack();
- *
- * /// [带参数]
- * let parameter = {};
- * parameter.title = 'i am wang ronghui';
- * navigator.goBack(parameter);
- *
- * d)场景B向后返回指定名字场景A
- * /// 默认
- * navigator.goBackToScene('A');
- *
- * /// [带参数]
- * let parameter = {};
- * parameter.title = 'i am wang ronghui';
- * navigator.goBackToScene('A', parameter);
- *
- * e)场景B向后返回根场景
- * /// 默认
- * navigator.goBackToRootScene();
- *
- * /// [带参数]
- * let parameter = {};
- * parameter.title = 'i am wang ronghui';
- * navigator.goBackToRootScene(parameter);
- *
- * 注意事项:
- * ------------------------
- * 挂载到场景的Canvas的自定义脚本的名字,必须要和场景文件的名字一致,否则无法调用到loadState或者saveState
- *
- */
- class Navigator
- {
- /**
- * 构造方法
- */
- constructor(){
- logger.info('constructor');
- this._allState = new Map();
- this._scenesStack = [];
- this._sceneLaunchHandle = false;
- /*
- * 支持外部使用cc.director.loadScene直接导航,记录下导航栈
- */
- cc.director.on(cc.Director.EVENT_AFTER_SCENE_LAUNCH, function (eventCustom) {
- /// 内部处理了这里就忽略,这里仅为支持监听外部导航。
- if(this._sceneLaunchHandle) {
- this._sceneLaunchHandle = false;
- return;
- }
- /// 获取当前场景
- let sceneName = eventCustom.name;
- logger.info('EVENT_AFTER_SCENE_LAUNCH sceneName = ' + sceneName);
- /// 先检查下导航栈有没有该场景,如果有,则回退到相应场景,防止出现场景循环
- let level = this.sceneStackLevel(sceneName);
- if(level !== -1){
- this.goBackToSceneStackLevel(level, null);
- return;
- }
- this.handleForward(sceneName, null);
- }.bind(this));
- }
- /**
- * 向前加载sceneName场景
- * @param {string} sceneName -场景名字
- * @param {object} [parameter] -参数对象
- * @param {function()} [onSceneLaunched] -新场景运行成功后回调
- */
- navigate(sceneName, parameter, onSceneLaunched){
- logger.info('navigate sceneName = ' + sceneName);
- logger.log('navigate parameter = ' + parameter);
- logger.log('navigate onSceneLaunched = ' + onSceneLaunched);
- /// 可能parameter和onSceneLaunched只传了某一个
- let argsLength = arguments.length;
- if(argsLength === 2) {
- if (typeof parameter === 'function') {
- onSceneLaunched = parameter;
- parameter = undefined;
- }
- }
- /// 先检查下导航栈有没有该场景,如果有,则回退到相应场景,防止出现场景循环
- let level = this.sceneStackLevel(sceneName);
- if(level !== -1){
- this.goBackToSceneStackLevel(level, parameter);
- return;
- }
- let readyToLeaveSceneJS = this.getCurrentSceneJS();
- if(readyToLeaveSceneJS){
- let sceneKey = 'Scene-' + this._scenesStack.length;
- let sceneState = this._allState.get(sceneKey);
- let state = {};
- sceneState.state = state;
- if(typeof readyToLeaveSceneJS.saveState === 'function'){
- readyToLeaveSceneJS.saveState.call(readyToLeaveSceneJS, state);
- }
- }
- cc.director.loadScene(sceneName, function () {
- /// 加载新场景成功处理
- logger.log('navigate loadScene complete sceneName = ' + sceneName);
- this._sceneLaunchHandle = true;
- this.handleForward(sceneName, parameter);
- /// 回调通知场景切换成功
- if(onSceneLaunched){
- onSceneLaunched();
- }
- }.bind(this));
- logger.log('navigate end');
- }
- /**
- * 向后返回前一个场景
- * @param {object} [parameter] -参数对象
- */
- goBack(parameter){
- logger.log('goBack');
- /// 当前Scene出导航栈
- this._scenesStack.pop();
- /// 加载栈顶Scene
- let sceneName = this._scenesStack[this._scenesStack.length - 1];
- logger.info('goBack to sceneName = ' + sceneName);
- cc.director.loadScene(sceneName, function () {
- logger.log('goBack loadScene complete sceneName = ' + sceneName);
- this._sceneLaunchHandle = true;
- this.handleBack(parameter);
- }.bind(this));
- }
- /**
- * 向后返回前根场景
- * @param {object} [parameter] -参数对象
- */
- goBackToRootScene(parameter){
- logger.log('goBackToRootScene');
- this.goBackToSceneStackLevel(1, parameter);
- }
- /**
- * 向后返回指定场景
- * @param {string} sceneName -场景名字
- * @param {object} [parameter] -参数对象
- */
- goBackToScene(sceneName, parameter){
- logger.log('goBackToScene sceneName = ' + sceneName);
- let level = this.sceneStackLevel(sceneName);
- if(level !== -1){
- this.goBackToSceneStackLevel(level, parameter);
- }
- }
- /*-------------------------私有方法begin-------------------------*/
- /**
- * 前进页面处理,加入导航栈,分配state
- * @param {string} sceneName -场景名字
- * @param {object} [parameter] -参数对象
- */
- handleForward(sceneName, parameter){
- logger.info('handleForward sceneName = ' + sceneName);
- logger.info('handleForward parameter = ' + parameter);
- /// 0,入导航栈
- if(sceneName){
- this._scenesStack.push(sceneName);
- }
- /// 加载新场景成功处理
- let enterSceneJS = this.getCurrentSceneJS();
- if(enterSceneJS){
- /// 向前导航时只有parameter,没有页面状态,所以页面状态为null
- if(typeof enterSceneJS.loadState === 'function'){
- enterSceneJS.loadState.call(enterSceneJS, navigatorMode.New, parameter, null);
- }
- /// 1,由于后退时不清理状态,在这里将当前页面以及向前所有的状态清除
- let nextSceneKey = 'Scene-' + this._scenesStack.length;
- let nextSceneIndex = this._scenesStack.length;
- while (this._allState.delete(nextSceneKey))
- {
- nextSceneIndex ++;
- nextSceneKey = 'Scene-' + nextSceneIndex;
- }
- /// 2,设置个state给当前Scene
- let sceneState = {};
- let sceneKey = 'Scene-' + this._scenesStack.length;
- logger.log('handleForward sceneKey = ' + sceneKey);
- this._allState.set(sceneKey, sceneState);
- /// 3,记录下参数
- parameter = parameter || {};
- sceneState.parameter = parameter;
- sceneState.state = {};
- }
- }
- /**
- * 后退页面处理,恢复场景
- * @param {object} [parameter] -参数对象
- */
- handleBack(parameter){
- logger.info('handleBack');
- /// 加载新场景成功处理
- let enterSceneJS = this.getCurrentSceneJS();
- if(enterSceneJS){
- let sceneKey = 'Scene-' + this._scenesStack.length;
- logger.log('handleBack sceneKey = ' + sceneKey);
- let sceneState = this._allState.get(sceneKey);
- /// 获取参数和页面状态,传入场景js,用于场景页面恢复
- if(typeof enterSceneJS.loadState === 'function'){
- /// 如果Back有带参数,优先使用参数,否则使用保留参数。
- parameter = parameter || sceneState.parameter;
- enterSceneJS.loadState.call(enterSceneJS, navigatorMode.Back, parameter, sceneState.state);
- }
- }
- }
- /**
- * 刷新页面处理
- * @param {object} [parameter] -参数对象
- */
- handleRefresh(parameter){
- logger.info('handleRefresh');
- let enterSceneJS = this.getCurrentSceneJS();
- if(enterSceneJS){
- if(typeof enterSceneJS.loadState === 'function'){
- enterSceneJS.loadState.call(enterSceneJS, navigatorMode.Refresh, parameter, null);
- }
- }
- }
- /**
- * 获取当前场景脚本类
- */
- getCurrentSceneJS(){
- let currentScene = cc.director.getScene();
- if(currentScene){
- let currentCanvas = currentScene.getChildByName('Canvas');
- if(currentCanvas){
- let currentCustomJS = currentCanvas.getComponent(currentScene.name);
- if(currentCustomJS){
- return currentCustomJS;
- }
- }
- }
- return null;
- }
- /**
- * 返回到固定Level的场景
- * @param {number} level -层级,比如1代表第一层
- * @param {object} [parameter] -参数对象
- */
- goBackToSceneStackLevel(level, parameter){
- logger.info('goBackToSceneStackLevel');
- let locScenesStack = this._scenesStack;
- let c = locScenesStack.length;
- if (c === 0) {
- return;
- }
- // current level or lower -> nothing
- if (level > c)
- return;
- // pop stack until reaching desired level
- while (c > level) {
- let current = locScenesStack.pop();
- c--;
- }
- let sceneName = locScenesStack[locScenesStack.length - 1];
- logger.info('goBackToSceneStackLevel sceneName = ' + sceneName);
- let currentSceneName = cc.director.getScene().name;
- logger.info('goBackToSceneStackLevel currentSceneName = ' + currentSceneName);
- if(currentSceneName !== sceneName){
- /// 加载栈顶Scene
- cc.director.loadScene(sceneName, function () {
- logger.log('goBackToSceneStackLevel loadScene complete sceneName = ' + sceneName);
- this._sceneLaunchHandle = true;
- this.handleBack(parameter);
- }.bind(this));
- }else {
- this.handleRefresh(parameter);
- }
- }
- /**
- * 获取指定scene名字的导航栈层级
- * @param {string} sceneName -场景名字
- */
- sceneStackLevel(sceneName){
- logger.log('sceneStackLevel sceneName = ' + sceneName);
- let locScenesStack = this._scenesStack;
- let i = locScenesStack.length-1;
- let exist = false;
- for(; i>=0; --i){
- if(locScenesStack[i] === sceneName){
- exist = true;
- break;
- }
- }
- logger.log('sceneStackLevel i = ' + i);
- if(exist){
- return i+1;
- }
- return -1;
- }
- /*-------------------------私有方法end-------------------------*/
- }
- module.exports = {
- /**
- * 导航模式
- */
- NavigatorMode: navigatorMode,
- /**
- * get Navigator
- * @returns {Navigator}
- */
- getNavigator: function(){
- return new Navigator();
- }
- };
|