cocos2dx-js 開發


本文轉載於http://lib.csdn.net/article/cocos/12811

如何學習

如何開始學習Cocos2d-JS?我我覺得比較好的方式是:

1)看測試例:測試例

2)看API文檔:

3)看源碼

另外還有大神錄制了進階視頻教程:Cocos2d-JS進階視頻教程,里面關於自學的藝術還是講的挺好的,在這里我就不累贅了。

 

如何開始

如何開始很簡單了,就兩條:

  • 搭環境

  • 編碼

如果你是做純web的游戲,搭建環境很簡單:

  • 下載引擎包

  • 下載Python2.7.6Ant,如果沒有安裝java,請去下載jdk至少1.7的版本

  • 配環境:無疑就是配置java環境、python環境、ant環境變量。基本上就是在系統環境變量path加入相關的bin目錄地址。不會的自行google吧。

  • 創建項目:cocos new -l js ProjectName

  • 開始編碼:sublime、webstorm等等用你喜歡的開發工具吧。

官網也有相關文章:用Cocos Console工作流開發網頁/原生平台游戲(JSB開發環境簡介)

 

如何編碼

如何編碼?如果團隊協作,遵循團隊的編碼規范咯。這里我只是簡單說一點點:

1. 關於project.json文件的配置信息

 
 debugMode: 0 
 //0 不顯示任何錯誤信息 
 //1 顯示cc.assert, cc.warn, cc.log
 
 showFPS : true 
 //為真會在屏幕左下方顯示游戲幀率 id: gameCanvas 
 //游戲畫布的id
 
 renderMode: 0 
 // 0 : 自動選擇渲染引擎:webGl、canvas
 // 1 : canvas 
 // 2 : WebGL(在webgl模式下drawNode會存在嚴重的鋸齒) 
 // modules : 游戲引用的模塊 
 // 模塊的名字可以在frameworks/cocos2d-html5/moduleConfig.json 
 // 里面找到,所以后期在上線的時候可以選擇自己用到的模塊引入來減少js文件的大小

2. 關於CCBoot.js

CCBoot.js是入口js文件,所以你很有必要認真看一看其中的代碼,比如其中提供一些可用的工具:

 
//創建元素 

 cc.newElement

 //監聽事件 

 cc._addEventListener

 //循環操作 

 cc.each

 //繼承 

 cc.extend 

 cc.isFunction 

 cc.isNumber 

 cc.isString 

 cc.isArray 

 cc.isUndefined 

 cc.isObject

 ...

 //原生的渲染引擎: CanvasRenderingContext2D/WebGLRenderingContext 
 cc._renderContext

 //包裹游戲畫布的外層
 div cc._gameDiv / cc.container

比如一些你可能想要修改的東西:

包裹游戲的外層div的id名字:

//大約CCBoot.js的1864行:
 localContainer.setAttribute('id', 'Cocos2dGameContainer');

初始加載的時候的畫布顏色:

//大約CCBoot.js的641行: 
 canvasNode.style.backgroundColor = "black";

frameworks/cocos2d-html5/core/platform/目錄下也有很多東西,主要是平台相關的東西。

如簡單操作dom的工具miniFramework.js里面的:

cc.$

屏幕適配相關的東西’CCEGLView.js’里面的:

cc.view.adjustViewPort

cc.view.setDesignResolutionSize

cc.view.resizeWithBrowserSize ...

文件加載相關的CCLoader.js:

Cocos2d-JS的文件加載是通過文件后綴的,如后綴名為["png", "jpg", "bmp","jpeg","gif", "ico"]的文件會通過cc._imgLoader來加載,理解了這里可以用於后期自定義文件加載插件。

還有一些編碼上面的東西放在下面再說。

 

關於音頻

音頻,在移動端上一直是個巨坑的問題。能自動播放,不能自動播放、不能循環播放、根本就不播放等等簡直就是處處都是坑啊。

開始游戲中的音頻用的是cocos里面的封裝的音頻函數,會對音頻文件進行預加載、預處理。但是這里有個嚴重的問題,那就是加載的時候很容易卡在音頻那里,而且循環播放也有問題。於是決定用會原生的audio標簽。

<audio id="gameAudio" src="res/gameMusic.mp3" loop="loop" ></audio>

看起來應該是不錯的樣子,但是當我們在iphone4s微信內置瀏覽器里面測試的時候發現這個audio標簽居然占據頁面空間(不是說加了controls這個屬性才會占空間么,坑啊!),於是自然而然想到的解決方案就是none掉:

<audio style="display:none" id="gameAudio" src="res/gameMusic.mp3" loop="loop" ></audio>

感覺好像輕易解決了這個問題,於是開始編寫一堆音頻處理的代碼。編寫完畢之后,拿起iphone試一試,我去啊iphone上音頻不能循環播放了啊,能不能再坑點!這個問題困擾了好久不知道什么原因,拿着代碼去找師傅去吧!師傅只有一句話:ios上面音頻如果不顯示或者在屏幕之外有可能就會出問題哦!終於把display:none去掉一切又好了。孩子你還是太年輕了啊!對於占據空間的問題我們現在只能把音頻標簽作為body最后一個child節點,依然沒有很好的解決方案。

對於音頻的自動播放,目前只發現在iphone6 plus是可以的(iphone 6沒有所以沒試過),所以只能通過用戶觸摸事件來加載。如果在HTML標記中使用了autoplay屬性,將會忽略這個屬性,並且不會在加載頁面時播放此文件,對於 preload 屬性,同樣會忽略。唯一能解決的就是用戶進入頁面是,讓用戶觸發 touch 事件,並且只能在touch事件的回調里面加載才有效。

var $audio = document.getElementById('gameAudio');
//用戶點擊屏幕的任何一個地方就去加載音頻
document.addEventListener('touchstart', function(e){    
    $audio.load();
},false);
 
//在某個cc.MenuItemImage/cc.MenuItemSprite/onTouchBegan的回調里面執行播放操作
try{    
    $audio.play();
}catch(e){}

音頻的暫停:

try{    
    $audio.pause();
}catch(e){}

音頻的停止:

try{    
    $audio.currentTime = 0;    
    $audio.pause();
}catch(e){}

如果是多個音頻文件,可以使用音頻audio sprite,所有的音頻綜合到一個單音頻流中,然后播放此流的各個部分。

 
var audioData = {
    bg: {
        start: 0,        
        length: 1
    },    
    run: {
        start: 1.3,        
        length: 1.5
    }
};

要播放bg這段聲音:

try{    
    $audio.currentTime = audioData.bg.start;   
    $audio.play();
}catch(e){}

當播放結束時:

$audio.addEventListener('timeupdate', function()
{    
    if (this.currentTime >= audioData.bg.start + audioData.bg.length) 
    {        
        this.pause();
    }
}, false);

需要注意的是,更改 currentTime 並不是百分百正確的。假設 currentTime 設為 3.2,而實際得到的卻是 3.4。每個 audio sprite 之間需要少量的空間,以避免尋找到另一個 sprite 的頭部。

 

一些工具

相當好用的精靈圖制作工具,雖然收費,但是可以申請免費的密鑰,具體申請地址自己查吧。

用它制作精靈圖的時候注意一個地方就可以了:

TexturePacke.jpg

勾上這個Reduce border artifacts,它會讓你的精靈圖邊緣沒有鋸齒,其它的配置地方我基本上都是默認的。

位圖字體其實是一張圖片,然后記錄每個文字的位置和大小,因此字體的大小在生成的時候已經定義了,所以在用的時候最好不要改變字體的大小,如果改變了大小會對圖片進行縮放,可能會出現鋸齒。

Bitmap Font Generator這個軟件制作的字體會生成一個png文件和一個fnt文件。選擇左上方的Options->Font settings

 

bmfont.jpg

設置字體格式和字體大小。其它基本上都是默認設置就好了。

可以在主界面上面一個個點擊選擇需要制作的文字,也可以把要選擇的字體放在一個txt文件里面(txt文件編碼為Utf-8),然后選擇菜單欄的Edit->Select chars from file來選擇要制作的文字。

Bitmap Font Generator還支持從image文件生成字體紋理圖,選擇菜單欄的Edit->Open Image Manager->Image->Import image(注意路徑里面不要有中文)

bmfonti.jpg

Id那里填寫對應文字的ASCII碼,如我上傳的圖片里面的文字是2,其對應的ASCII碼是50。如果不知道某個文字的ASCII碼,可以去這里查:ASCII碼對照表

選擇好之后點擊菜單欄Options->Export options

bmfonte.jpg

 

為什么需要加載字體?因為有時候頁面上會有不同大小的特殊字體,用位圖又比較麻煩,加載整個字體庫又太大了,於是我找到了這個軟件。

選擇菜單欄File->New Project,填寫你的字體名字和字體樣式。然后選擇菜單欄File->Open,打開你要精簡的字體。(在新建項目的時候有一個Include outlines/Don’n include outlines的選項。如果選擇Include outlines它會默認幫你添加一些字符)

fontc.jpg

如果是可以直接看得到的字符,我可以直接在原有的字體文件中選中這個字符,然后復制粘貼到我的項目里面對應的位置上去,如果不是的話,我們可以通過下面的方法添加進去。

ctrl+f鍵查找某個字符,如我們要添加的字是“我”,然后選擇Glyph Properties

fontc1.jpg

復制Codepoints里面的參數,然后選中自己的項目,選擇菜單欄Insert->Charaacters,把剛才復制的Codepoints粘貼到對應的位置。

fontc2.jpg

這時你會發現在你項目字符的最后會添加一個灰色方塊,回到原始字體那里,選中那個字直接復制粘貼到剛才添加的那個方塊里面就可以了:

fontc3.jpg

在上面的圖里面你可以看到有很多灰色的方塊,看看里面顯示的字符,如果你用不到你可以直接點Delete鍵把它刪掉。

除了用這個軟件外,最近發現一個工具也可以用來精簡壓縮字體,感興趣的可以試試:字蛛——中文字體自動化壓縮工具

 

這個其實在我們的項目中沒有用到,所以在這里提一下而已,有用到的可以自己去琢磨一下。


項目發布

項目發布意味着什么?簡單來說就意味着資源壓縮、代碼合並。首先來說下資源壓縮吧。

 

讓你愛恨交加的closure compiler

不是說Cocos2d-JS么?怎么會扯到closure compiler。如果你想要發布你的Cocos2d游戲,我覺得就不得不說說closure compiler這玩意,這也是為何安裝jdk的時候要求要jdk7以上的版本了。

在cocos項目發布的時候,有一個命令是cocos compile -p web -m release --advanced,后面加了一個advanced的參數,這種模式下面使用的是closure compiler的高級js壓縮模式,壓縮比例驚人,同時會優化js的執行,因此壓縮之后的js性能也會得到一定提升。但是想要用這個高級壓縮模式可是有坑的,很可能你會發現你的代碼經過這種壓縮模式壓縮之后就報錯了。

所以你最好去closure compiler官網(google出品,所以需要翻牆)看一看,這里我只提簡單的幾點。

需要保留變量名的,要么用@expose關鍵字申明,要么使用[]來引用,如:

 
/** @expose */
window.MM;
/** @expose */
M.add;
/** @expose */
M.add.Pos;
 
//or
window['MM'];
MM['add'];
MM['add']['Pos']

比如在ajax請求數據的時候的請求參數和響應參數,你要寫成這樣:

//request data
//加引號
{'name': 'dddd', 'id': '123'}
//response data
//通過數組的方式取值
data['list'];
data['list']['userName']

記住,任何你需要讓它在壓縮之后能保持原樣輸出的你都應該這樣寫。如果要了解更多你可以去它官網看看,或者看一看cocos2d-js的源碼,里面有很多應對closure compiler高級壓縮模式的注解。

 

plist文件合並

一次請求是比較耗費資源的,所以我們把plist文件合並在一個文件里面來加載,同時也對plist進行了壓縮,下面是加載解析合並后的plist文件的插件源碼:

/**
 * 將plist合並成一個加載
 */
cc._pjsonLoader = {
    KEY : {
        frames : 0,
        rect : 0, size : 1, offset : 2, rotated : 3, aliases : 4,
        meta : 1,
        image : 0
    },
    _parse : function(data){
        var KEY = this.KEY;
        var frames = {}, meta = data[KEY.meta] ? {image : data[KEY.meta][KEY.image]} : {};
        var tempFrames = data[KEY.frames];
        for (var frameName in tempFrames) {
            var f = tempFrames[frameName];
            var rect = f[KEY.rect];
            var size = f[KEY.size];
            var offset = f[KEY.offset];
            frames[frameName] = {
                rect : {x : rect[0], y : rect[1], width : rect[2], height : rect[3]},
                size : {width : size[0], height : size[1]},
                offset : {x : offset[0], y : offset[1]},
                rotated : f[KEY.rotated],
                aliases : f[KEY.aliases]
            }
        }
        return {_inited : true, frames : frames, meta : meta};
    },
    load : function(realUrl, url, res, cb){
        var self = this, locLoader = cc.loader, cache = locLoader.cache;
        locLoader.loadJson(realUrl, function(err, pkg){
            if(err) return cb(err);
            var dir = cc.path.dirname(url);
            for (var key in pkg) {
                var filePath = cc.path.join(dir, key);
                cache[filePath] = self._parse(pkg[key]);
            }
            cb(null, true);
        });
    }
};
//注冊這個插件,前面說過cocos文件加載是通過文件后綴名來判斷使用哪個加載器來加載的,所以這里會加載后綴名為.pjson的文件
cc.loader.register(["pjson"], cc._pjsonLoader);

那么如何來生成這個.pjson文件呢?下面是我用nodejs寫的一個腳本,可以讀取目錄下面所有的.plist文件,然后生成一個.pjson文件:

 
//pjson.js
var fs = require('fs');
var plist = require('plist');
var pjson = {};
 
//var src = "res/*.plist";
//node pjson
fs.readdir('./res', function(err, files) {
    if (err) {
        throw err;
    }
    for (var i = files.length - 1; i >= 0; i--) {
        var file = files[i],
            ext = file.split('.')[1];
        if(ext === 'plist'){
            pjson[file] = [];
            pjson[file][0] = {};
            var data = fs.readFileSync('./res/'+file, 'UTF-8');
            var frames = plist.parse(data.toString()).frames;
            var fileName = Object.keys(frames);
            for(var j=0; j< fileName.length; j++){
                var dat = pjson[file][0][fileName[j]] = [];
                var frame = frames[fileName[j]];
                dat[0] = frame.frame.replace(/{|}/g, '').split(',');
                dat[1] = frame.sourceSize.replace(/{|}/g, '').split(',');
                dat[2] = frame.offset.replace(/{|}/g, '').split(',');
                if(frame.rotated)  dat[3] = 1;
            }
        }
    };
    fs.writeFile('./res/plists.pjson', JSON.stringify(pjson), function (err) {
          if (err) throw err;
          console.log('done!');
    });
});

所以你需要在nodejs命令行里面運行:

//第一次運行請先安裝依賴包
npm install plist
 
//生成pjson文件
node pjson.js

那么如何使用這個文件呢?很簡單,你只需要在resource.jsg_resources數組里面刪除plist相關的,然后添加這個plists.pjson的路徑就可以了,其它不用作任何改動。

 

圖片資源壓縮

關於圖片和其他靜態資源,我用了一個前端自動化任務工具gulp,不懂的可以看我上篇博文:Gulp上手,下面是我針對項目配置的gulp任務gulpfile.js

var gulp = require('gulp'),
    imagemin = require('gulp-imagemin'),
    pngquant = require('imagemin-pngquant'),
    cache = require('gulp-cache'),
    autoprefixer = require('gulp-autoprefixer'),
    minifycss = require('gulp-minify-css'),
    rev = require('gulp-rev'),
    revReplace = require('gulp-rev-replace'),
    useref = require('gulp-useref');
 
var basePath = 'publish/html5/';
 
gulp.task('image', function () {
    return gulp.src(basePath+'res/**/*.png')
        .pipe(cache(imagemin({
            optimizationLevel: 7,
            use: [pngquant({ quality: '60-80', speed: 1 })]
        })))
        .pipe(gulp.dest(basePath+'res'));
});
 
gulp.task('style', function () {
    return gulp.src('css/**/*.css')
        .pipe(autoprefixer('Android', 'BlackBerry', 'iOS', 'OperaMobile', 'ChromeAndroid', 'FirefoxAndroid', 'ExplorerMobile'))
        .pipe(minifycss())
        .pipe(gulp.dest(basePath+'css'));
});
 
gulp.task('static', function () {
    var userefAssets = useref.assets();
    return gulp.src(basePath+'index.html')
        .pipe(userefAssets)
        .pipe(rev()) 
        .pipe(userefAssets.restore())
        .pipe(useref())
        .pipe(revReplace())
        .pipe(gulp.dest(basePath));
});
 
gulp.task('default', ['image', 'style', 'static']);

因為其中用到了對每次生成的js、css文件進行md5命名和合並操作,所以你需要在你的html文件里面作如下配置:

<!-- build:css css/main.css -->
<link rel="stylesheet" href="css/a.css">
<link rel="stylesheet" href="css/b.css">
 
<!-- endbuild --><!-- build:js game.js -->
<script src="frameworks/cocos2d-html5/CCBoot.js"></script>
<script src="main.js"></script>
<!-- endbuild -->

整個項目發布

接着我寫了一個整個項目的發布腳本build.js

var exec = require('child_process').exec;
 
var buildProcess = exec('cocos compile -p web -m release --advanced', {});
buildProcess.on('close', function () {
    console.log('start gulp task');
    var nextProcess = exec('gulp default', {});
    nextProcess.on('close', function () {
        console.log('gulp task end');
    });
    nextProcess.stdout.setEncoding('utf-8');
    nextProcess.stdout.on('data', function (data) {
        console.log(data);
    });
    nextProcess.stderr.setEncoding('utf-8');
    nextProcess.stderr.on('data', function (data) {
        throw new Error(data);
    });
});
buildProcess.stdout.setEncoding('utf-8');
buildProcess.stdout.on('data', function (data) {
    console.log(data);
});
buildProcess.stderr.setEncoding('utf-8');
buildProcess.stderr.on('data', function (data) {
    throw new Error(data);
});

所以每次項目發布的時候,你只需要打開nodejs命令行,然后執行一下一個命令就完事:

node build.js

一些可能對你有用的代碼

ajax簡單封裝

Utils.ArrayProto = Array.prototype;
Utils.slice = Utils.ArrayProto.slice;
 
Utils.decode = decodeURIComponent;
Utils.encode = encodeURIComponent;
 
Utils.defaults = function(obj){
    cc.each(Utils.slice.call(arguments, 1), function(o){
        for(var k in o){
            if (obj[k] == null) obj[k] = o[k];
        }    
    });
    return obj;
};
 
Utils.formData = function(o) {
    var kvps = [], regEx = /%20/g;
    for (var k in o) kvps.push(Utils.encode(k).replace(regEx, "+") + "=" + Utils.encode(o[k].toString()).replace(regEx, "+"));
    return kvps.join('&');
};
 
Utils.ajax = function(o){
    var xhr = cc.loader.getXMLHttpRequest();
    o = Utils.defaults(o, {type: "GET", data: null, dataType: 'json', progress: null, contentType: "application/x-www-form-urlencoded"});
    //ajax進度的
    //if(o.progress) Utils.Progress.start(o.progress);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4){
            if (xhr.status < 300){
                var res;
                if(o.dataType == 'json'){
                    res = window.JSON ? window.JSON.parse(xhr.responseText): eval(xhr.responseText);
                }else{
                    res = xhr.responseText;
                }
                if(!!res) o.success(res);
                ////ajax進度的
                //if(o.progress) Utils.Progress.done();
            }else{
                if(o.error) o.error(xhr, xhr.status, xhr.statusText); 
            }
        }
    };
    //是否需要帶cookie的跨域
    //if("withCredentials" in xhr) xhr.withCredentials = true;
    var url = o.url, data = null;
    var isPost = o.type == "POST" || o.type == "PUT";
    if( o.data && typeof o.data == 'object' ){
        data = Utils.formData(o.data);
    }
    if (!isPost && data) {
        url += "?" + data;
        data = null;
    }
    xhr.open(o.type, url, true);
    if (isPost) {
        xhr.setRequestHeader("Content-Type", o.contentType);
    }
    xhr.send(data);
    return xhr;
};
 
Utils.get = function(url, data, success){
    if(cc.isFunction(data)){
        success = data;
        data = null;
    }
    Utils.ajax({url: url, type: "GET", data: data, success: success});
};
 
Utils.post = function(url, data, success){
    if(cc.isFunction(data)){
        success = data;
        data = null;
    }
    Utils.ajax({url: url, type: "POST", data: data, success: success});
};

用法和jquery的ajax用法類似


LoaderScene重定義

下面是我的loaderScene的一部分代碼:

var LoaderScene = cc.Scene.extend({
    _interval : null,
    _label : null,
    _className:"LoaderScene",
    numberSprites: [],
 
    init : function(){
        var self = this;
 
        // bg
        var bgLayer = self._bgLayer = new cc.LayerColor(cc.color(216, 216, 216));
        bgLayer.setPosition(cc.visibleRect.bottomLeft);
        self.addChild(bgLayer, 0);
 
        //you codes
 
        return true;
    },
    onEnter: function () {
        cc.Node.prototype.onEnter.call(this);
        var loader = (this.loadType == 'resource') ? this._startLoading : this._startAjax;
        this.schedule(loader, 0.3);
    },
    onExit: function () {
        cc.Node.prototype.onExit.call(this);
        //you codes
    },
 
    initWithResources: function (resources, cb) {
        if(cc.isString(resources))
            resources = [resources];
        this.resources = resources || [];
        this.cb = cb;
        this.loadType = 'resource';
    },
    _startLoading: function () {
        var self = this;
        self.unschedule(self._startLoading);
        var res = self.resources;
        cc.loader.load(res, self._loadProgress.bind(self), function () {
            if (self.cb) self.cb();
        });
    },
    initAjax: function(ajaxSetting){
        this.ajaxSetting = ajaxSetting;
        this.loadType = 'ajax';
    },
    _startAjax: function(){
        var self = this,
            preNum = 0;
        self.unschedule(self._startAjax);
        if(!this.ajaxSetting.progress){
            this.ajaxSetting.progress = function(n){
                n = n.toFixed(2);
                if(preNum === n) return;
                preNum = n;
                self._loadProgress(null, 1, n);
            };
        }
        Utils.ajax(this.ajaxSetting);
    },
    _loadProgress: function(result, count, loadedCount){
        var percent = (loadedCount / count * 100) | 0;
        percent = Math.min(percent, 100);
        console.log(percent);
    }
});
 
LoaderScene.preload = function(resources, cb){
    var _cc = cc;
    if(!_cc.loaderScene) {
        _cc.loaderScene = new LoaderScene();
        _cc.loaderScene.init();
    }
    _cc.loaderScene.initWithResources(resources, cb);
    cc.director.runScene(_cc.loaderScene);
    return _cc.loaderScene;
};
 
LoaderScene.ajaxLoad = function(ajaxSetting){
    var _cc = cc;
    if(!_cc.loaderScene) {
        _cc.loaderScene = new LoaderScene();
        _cc.loaderScene.init();
    }
    _cc.loaderScene.initAjax(ajaxSetting);
    cc.director.runScene(_cc.loaderScene);
    return _cc.loaderScene;
};

如果是普通的資源加載調用LoaderScene.preload,如果是ajax加載定調用LoaderScene.ajaxLoad,配合上面的ajax封裝來用:

LoaderScene.preload(g_resources, function () {
    console.log('loaded!!!');
});
 
LoaderScene.ajaxLoad({
    url: 'test.json',
    data: {'name': 'ddd'}
    success: function(data){
        console.log(data);
    },
    error: function(xhr, status, msg){
 
    }
});

其中有個顯示ajax加載百分比的方法,需要用到一個生成加載百分比的方法,同時要把ajax封裝里面的if(o.progress)的注釋打開:

Utils.noop = function(){};
 
Utils.clamp = function(n, min, max) {
    if (n < min) return min;
    if (n > max) return max;
    return n;
};
 
Utils.Progress = {};
//設置最小值、更新頻率和速度
Utils.Progress.settings = {
    minimum: 0.1,
    trickle: true,
    trickleRate: 0.3,
    trickleSpeed: 100
};
 
Utils.Progress.status = null;
 
Utils.Progress.set = function(n) {
    var progress = Utils.Progress;
    n = Utils.clamp (n, progress.settings.minimum, 1);
    progress.status = n;
    progress.cb(progress.status);
    return this;
};
 
Utils.Progress.inc = function(amount){
    var progress = Utils.Progress,
        n = progress.status;
    if (!n) {
        return progress.start();
    }else{
        amount = (1 - n) * Utils.clamp(Math.random() * n, 0.1, 0.95);
        n = Utils.clamp(n + amount, 0, 0.994);
        return progress.set(n);
    }
};
 
Utils.Progress.trickle = function() {
    var progress = Utils.Progress;
    return progress.inc(Math.random() * progress.settings.trickleRate);
};
 
Utils.Progress.start = function(cb) {
    var progress = Utils.Progress;
    progress.cb = cb || Utils.noop;
    if (!progress.status) progress.set(0);
 
    var timer = function(){
        if (progress.status === 1) {
            clearTimeout(timer);
            timer = null;
            return;
        }
        progress.trickle();
        work();
    };
 
    var work = function() {
      setTimeout(timer, progress.settings.trickleSpeed);
    };
    if (progress.settings.trickle) work();
    return this;
};
 
Utils.Progress.done = function() {
    var progress = Utils.Progress;
    return progress.inc(0.3 + 0.5 * Math.random()).set(1);
};

LoaderLayer的定義

LoaderLayer是在加載的時候在當前場景上面添加一個遮罩層,這里主要是ajax加載的,代碼如下:

Config.winSize = cc.size(720, 1134);
Config.w =  Config.winSize.width;
Config.h =  Config.winSize.height;
Config.w_2 = Config.w / 2;
Config.h_2 = Config.h / 2;
 
var LoaderLayer = cc.LayerColor.extend({
    count: 0,
    ctor:function () {
        this._super(cc.color(0, 0, 0, 160));
        var draw = new cc.DrawNode();
        draw.x = Config.w_2 - 50;
        draw.y = 260;
 
        for(var i=0; i<3; i++){
            draw.drawRect(cc.p(40*i, 0), cc.p(40*i+20, 20), cc.color(181, 181, 181), 1, cc.color(181, 181, 181));
        }
        var draw2 = this.draw = new cc.DrawNode();
        draw2.x = Config.w_2 - 50;
        draw2.y = 260;
        draw2.drawRect(cc.p(0, 0), cc.p(20, 20), cc.color(239, 178, 82), 1, cc.color(239, 178, 82));
        this.addChild(draw);
        this.addChild(draw2);
        this.schedule(this.updateLoad, 0.2);
        return true;
    },
    updateLoad: function(){
        var draw = this.draw;
        draw.clear();
        if(this.count >3){
            this.count = 0;
        }
        for(var i=0; i< this.count; i++){
            draw.drawRect(cc.p(40*i, 0), cc.p(40*i+20, 20), cc.color(239, 178, 82), 1, cc.color(239, 178, 82));
        }
        this.count++;
    },
    onRemove: function(){
        var parent = this.parent;
        cc.eventManager.resumeTarget(parent,true);
        parent.resume();
        parent.removeChild(this, true);
    }
});
 
LoaderLayer.preload = function(url, data, cb, parent){
    var loader = cc.LoaderLayer;
    if(!loader) {
        loader = new LoaderLayer();
    }
    //parent.pause();
    cc.eventManager.pauseTarget(parent,true);
    parent.addChild(loader, 99);
       Utils.ajax({
           url: url,
           type: "POST",
           data: data,
           success: function(data){
               loader.onRemove();
               cb(data);
           },
           error: function(xhr, status, msg){
               loader.onRemove();
               alert(msg);
           }
       });
};

使用也很簡單:

//這里默認使用了ajax的post方法,parent一般都是this。
LoaderLayer.preload(url, data, cb, parent);

 

H5離線緩存

HTML5離線存儲Application Cache可以讓用戶在離線狀態下瀏覽網站內容,緩存之后的內容不會再次從服務器獲取,除非緩存文件改變了。如何使用呢?這需要打開你的html文件在里面:

 
<!DOCTYPE html>
<html lang="en" manifest="manifest.appcache">
<head>...

 

接着在html文件的同級目錄下面新建manifest.appcache,並加入如下內容:

//manifest.appcache
CACHE MANIFEST#
version 1.0
CACHE:
    css/main.css
    img/test.png
NETWORK:   
    *

關於manifest.appcache文件,基本格式為三段: CACHE, NETWORK,與 FALLBACK,其中NETWORK和FALLBACK為可選項,而第一行CACHE MANIFEST為固定格式,必須寫在前面。

CACHE是必須參數,標識出哪些文件需要緩存,可以是相對路徑也可以是絕對路徑

NETWORK是可選參數,這一部分是要繞過緩存直接讀取的文件,可以使用通配符*,也就是說除了上面的cache文件,剩下的文件每次都要重新拉取。

FALLBACK也是可選參數,指定了一個后備頁面,當資源無法訪問時,瀏覽器會使用該頁面。該段落的每條記錄都列出兩個 URI—第一個表示資源,第二個表示后備頁面。兩個 URI 都必須使用相對路徑並且與清單文件同源。可以使用通配符。

有了上面兩個文件之后還要配置服務器的mime.types類型,找大盤apache的mime.types文件,添加

text/cache-manifest .appcache

上面文件配置完成之后,application cache就可以運行了。

更新緩存的方式有三種:

可以修改一下manifest文件,把version改為1.1,然后刷新頁面。

js添加一個監聽事件:

window.applicationCache.addEventListener('updateready', function()
{    
    console.log('updateready!');    
    window.applicationCache.swapCache();
});

站點離線存儲的容量限制是5M;如果manifest文件,或者內部列舉的某一個文件不能正常下載,整個更新過程將視為失敗,瀏覽器繼續全部使用老的緩存。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM