論egret的坑
1. Tween
坑
功能:實現點選播放動畫,播放動畫後跳轉到新標籤頁面
this.addEventListener(egret.TouchEvent.TOUCH,()=>{ var tw = egret.Tween.get(... tw.to({...},150) .call(()=>{ window.open("http://www.xx/xxx.html", '_blank'); }); } , this);
這樣是會被認為不是使用者手動觸發的,是會被瀏覽器預設攔截的,不要寫在 call
裡邊,但是可以寫在 settimeout
裡邊
setTimeout(function () { window.open("http://www.xx/xxx.html", '_blank'); })
擴充套件,像這類需要使用者手動觸發的,不要寫在事件分發裡邊,比如
let handler: egret.EventDispatcher = new egret.EventDispatcher(); handler.addEventListener(type,()=>{ ... }, this);
同樣,聲音播放也是需要使用者手動觸發的,如果是遊戲的話,可以加一個開始按鈕,然使用者點選開始按鈕再加入遊戲,其他情況也一樣,可以搞個什麼彈窗或者提示之類的誘導使用者點選,然後在點選事件裡邊加上聲音播放
2. default.res.json
坑
比如要實現多語言功能,可以實現配置多個 res
檔案,然後根據不同的語言選擇匯入不同的res檔案。
看上去很正常,可是如果要實現語言動態切換,那效果真的是相當的不理想。
比如:
多語言的圖片,程式碼如下:
var parant = this.parent; await RES.loadGroup("loading"); const loadingView = new LoadingUI(); parant.stage.addChild(loadingView); this.removeSelf(); RES.destroyRes(ResMap[oldLan], true); RES.destroyRes("ui_t_bin", true); RES.destroyRes("btn_lang_json", true); await RES.loadConfig(ResMap[language], "resource/"); await RES.loadGroup("lang_text", 0, loadingView); //初始化多語言文字 var game = initGame(); parant && parant.addChild(game); loadingView.parent && loadingView.parent.removeChild(loadingView); RES.destroyRes("loading");
本地測試載入還好,能夠正常使用,可是線上的情況並不怎麼好用,切換一次還行,再切換就不行了
解決方法:
第一種:暴力解決,直接重新整理頁面,同時加上多語言引數
這種方法不推薦,也就不多講了
第二種:秒切換,但是費時費力
首先:所有資源共用一個資源配置檔案: default.res.json
,但這時候存在相同鍵值對就會有警告,其實不礙事
其次,介面配置多狀態:

多狀態
可以根據不同語言定製不同介面,但是這種方法比較費時費力,擴充套件相對不易,但是呈現的效果確是最好的
同時,之前一些禁忌的東西就要使用了,程式碼如下:
<e:Button name="btn_1" label="" x="105" y="14" includeIn="en"> <e:skinName> <e:Skin states="up,down,disabled"> <e:Image width="100%" height="100%" source="btn_en_json.btn_1_png" source.down="btn_en_json.btn_1_png" source.disabled="btn_en_json.btn_1_png" /> <e:Label id="labelDisplay" horizontalCenter="0" verticalCenter="0" /> </e:Skin> </e:skinName> </e:Button>
這裡加了 btn_en_json
字首,這是精靈圖片的方式,開發的時候最好不要用這種方式,但是有時候確是比較好的方式, includeIn
可以保證不同狀態下顯示不同的按鈕
由於不同介面的按鈕不能夠共用,這時候就會涉及到多個 id
的問題,由於 id
不能重複,所以只能用 name
。
獲取元件的方式:
this.btn_1 = <eui.Button>this.grp_1.getChildByName("btn_1");
可以看出來,必須在button外邊包一層 group
,這也是比較坑的地方
擴充套件:其實多狀態還有很多用途,涉及到不同條件需要展示不同效果的,都可以用多狀態來實現
3. 多狀態的坑
多狀態介面設計本省就很坑了,比如某些不熟悉的人,喜歡這樣定義狀態:
··· states="[zh],[en],[zh-Hant]">
那麼坑就很明顯了,如 [zh].visible="true"
是會有問題的
還有設計的時候所有狀態下最好所有元件都到位,不然容易出現什麼時候介面上的某個東東就不見了的詭異情況
但是,但是這樣就是坑嗎?
最最最坑的是:
與 eui.ItemRenderer
的重用的情況:
export class Screen extends eui.ItemRenderer { ... public constructor(skinName?: string) { super(); this.skinName = skinName; } protected dataChanged(): void {} ... }
看上去,程式碼好好的。。。可是可是!!!!
滑鼠點選之後會還原到預設狀態!!!!坑!!!
這時候只能在外面再巢狀一層,也就是寫兩個介面,其中多狀態的那個介面以控制元件的方式引入到第二個介面,同時 eui.ItemRenderer
也由第二個介面繼承,這個坑讓我心情糟透了,就不在這裡舉例程式碼了
4. http
重連坑
伺服器用的 golang
,可是每次請求都會發送兩篇,實在是坑爹至極,可是這個問題居然沒有能夠解決,失落。。。
為了彌補小小的失落,還是決定自己寫一套吧(程式碼放在最底部),下面的就是摘自網上的,然後又 js
改成的 ts
版 ajax
:
//使用 ajax({ url: url, method: "GET", type: "text", async: true, timeout: 1000, onSuccess: function (data) { //請求成功 }, onError: function (_error) {} }).getText();
有點是比egret穩定,脫離egret環境也可以使用哦
5. removeChild
坑
提供的 DisplayObject 必須是呼叫者的子級
看了這句話,其實內心是崩潰的。。。因為很多時候,你根本不知道哪裡錯了,這時候還得一個個去查詢用了 removeChild
的地方
解決方法:
/** * 移除自身 */ public removeSelf() { this.parent && this.parent.removeChild(this); }
這樣貌似就可以了。。。但是最好不要寫在方法的區域性函式裡邊,這樣很容易出問題,比如非同步執行、事件呼叫這些,即使你用了parent可能也防不住錯誤,然後只能讓崩潰來得更加猛烈些了。。。
6. exml
坑
複製貼上: id
自增不算坑,但是帶字串的控制元件複製貼上元件後字串裡邊的 \n
就沒了
list
:由於 list
所有項都是等寬高的,所以如果需要設定不同高度的列表,就需要用 DataGroup
點選事件被攔截:設定 group
的 touchThrough
為 true
如果希望 group
超出部分被遮擋,就應該設定 scrollEnable
為 true
最坑的是,自定義控制元件設定寬高為 100%
,編譯後可能報錯,這時候應該用 left="0" right="0" top=0" bottom="0"
替代
自定義控制元件屬性警告,雖然能成功但是很不爽:
[warning] EXML解析錯誤 ScreenSkin: 節點上不存在名為'preview'的屬性,或者該屬性沒有初始值:... [warning] EXML解析錯誤 ScreenSkin: 無法將'string'型別的值賦給屬性:'preview:undefined'
在 exml
是這樣寫的:
<e:Skin class="ScreenSkin" xmlns:e="http://ns.egret.com/eui" xmlns:w="http://ns.egret.com/wing" xmlns:game="ex.*"> <ex:BaseScreen preview="true"/> </e:Skin>
在 BaseScreen
中如下定義:
export class BaseScreen{ private preview:boolean = false; }
結束
以下是 Ajax.ts
的程式碼:
namespace http { const doc = window.document; const OBJ: string = "OBJ"; const STR: string = "string"; class AjaxError { private name: string; private msg: string | null; public constructor(msg: string | null) { this.name = "Ajax錯誤"; this.msg = msg; } public getName(): string { return this.name; } public getMsg(): string | null { return this.msg; } } export class Ajax { private option; private request; private to: any[]; public constructor(option) { this.option = { url: option.url || "",//資料來源地址 method: option.method || "GET",//請求方法[POST、HEAD...] data: option.data || null,//要傳送給伺服器的資料 async: option.async || true,//是否是非同步請求 type: option.type || "text",//返回資料後,將資料轉換為指定的型別.(text,js,xml,html) timeout: option.timeout || 10000,//請求超時,預設為十秒 cache: option.cache || false,//是否從快取中取資料(如果瀏覽器已快取) onSuccess: option.onSuccess || function (result) { },//請求成功後執行的函式(處理返回結果) onError: option.onError || function () { },//請求出錯呼叫的函式 onComplete: option.onComplete || function () { },//請求完成後(無論成功與否)都執行的函式 showStatus: option.showStatus || function () { }//顯示請求狀態 }; this.to = []; this.request = this.create(); this.fix(this.option); } private create() { return new XMLHttpRequest(); } private parseToQueryString(obj) {//將陣列或物件序列化 if (typeof obj === STR) return obj; var s: Array<any> = []; if (obj instanceof Array) {//假定為陣列 let arr: Array<any> = <Array<any>>obj; for (var i = 0; i < obj.length; i++) s.push(arr[i].name || i + "=" + arr[i]); } else { for (var j in obj) s.push(j + "=" + obj[j]); } return s.join("&"); }; private parseToObject(str: string) {//將查詢字串轉化成物件 if (typeof str == OBJ) return str; var set = {}; var strArr: string[] = str.split("&"); var item: string[]; for (var i = 0; i < strArr.length; i++) { if (strArr[i].indexOf("=") > 0) { item = strArr[i].split("="); set[item[0]] = item[1]; } } return set; }; private append(url: string, param: string): string { if (url.indexOf("?") < 0) { return url + "?" + param; } else { if (/\?$/.test(url)) { return url + param; } else { return url + "&" + param; } } } private fix(p) { if (p.data) { p.data = this.parseToQueryString(p.data); } if (p.method.toUpperCase() == "GET" && p.data) { p.url = this.append(p.url, p.data); } if (!p.cache) { p.url = this.append(p.url, "abkjfjk=" + (new Date().getTime()) + "jrejhjdd"); } } private isOK(r) { try { return !r.status && location.protocol == "file:" || (r.status >= 200 && r.status < 300) || r.status == 304 || navigator.userAgent.indexOf("Safari") >= 0 && r.status == undefined; } catch (e) { } return false; } private httpData(r, type: string) { var res = type; if (!res) { var ct = r.getResponseHeader("Content-Type"); if (/xml/i.test(ct)) res = "xml"; else if (/JavaScript/i.test(ct)) res = "js"; else res = ""; } switch (res) { case "xml": return r.responseXML.documentElement; case "js": return eval("(" + r.responseText + ")"); default: return r.responseText; } } private getObj(id) { return typeof id === OBJ ? id : doc.getElementById(id); } public appendTo(target) {//將返回的結果加到指定的目標[id或DOM物件] if (!this.to) this.to = []; this.to.push({ "target": this.getObj(target), "type": this.option.type }); return this; } public open() { try { this.request["open"](this.option.method, this.option.url, this.option.async); if (/POST/i.test(this.option.method)) { this.request["setRequestHeader"]("Content-Type", "application/x-www-form-urlencoded"); if (this.request["overrideMimeType"]) this.request["setRequestHeader"]("Connection", "close"); } } catch (e) { throw new AjaxError(e.message) } return this; } public send() { try { this.request["send"](this.option.data); } catch (e) { throw new AjaxError(e.message); } return this; } public stop() { try { this.request["abort"](); } catch (e) { throw new AjaxError(e.message) } return this; } public getText(fn?) { return this.exe({ "onSuccess": fn, "type": "text" }); }//fn可選 public getXML(fn?) { return this.exe({ "onSuccess": fn, "type": "xml" }); } public getScript(fn?) { return this.exe({ "onSuccess": fn, "type": "js" }); } public getHTML(fn?) { return this.exe({ "onSuccess": fn, "type": "html" }); } public exe(options) { if (options.onSuccess) this.option.onSuccess = options.onSuccess; if (options.onError) this.option.onError = options.onError; if (options.onComplete) this.option.onComplete = options.onComplete; if (options.showStatus) this.option.showStatus = options.showStatus; if (options.type) this.option.type = options.type; try { var isTimeout = false, cur = this.open(); var timer = setTimeout(function () { isTimeout = true; cur.stop(); cur.option.onError(new AjaxError("請求超時")); }, cur.option.timeout); var self = this; this.request["onreadystatechange"] = function () { cur.option.showStatus(cur.request["readyState"]); if (cur.request["readyState"] == 4 && !isTimeout) { try { if (self.isOK(cur.request)) {//成功完成 var t = self.httpData(cur.request, cur.option.type); if (cur.to && cur.to.length > 0) { for (var i = 0; i < cur.to.length; i++) { if (cur.to[i].type && cur.to[i].type == "html") cur.to[i].target.innerHTML += t; else cur.to[i].target.appendChild(doc.createTextNode(t)); } } cur.option.onSuccess(t); } else { cur.option.onError(new AjaxError("請求未成功完成")); } } catch (et) { cur.option.onError(new AjaxError(et.message)); } finally { cur.option.onComplete(); // cur.request = null; clearTimeout(timer); } } }; this.send(); } catch (e) { console.error(e); this.option.onError(new AjaxError(e.message)); } finally { return this; } } } } function ajax(option) { return new http.Ajax(option); }