论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改成的tsajax

//使用
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

点击事件被拦截:设置grouptouchThroughtrue

如果希望group超出部分被遮挡,就应该设置scrollEnabletrue

最坑的是,自定义控件设置宽高为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;
}

7. Tween坑(背锅)

这个坑呢是在10.13日发现的,移动端的动画在手机上抖动得非常厉害,具体原因请看文章: 为何 Canvas 内元素动画总是在颤抖?

所以这其实不是Tween的坑,不过解决方案上诉文章已经给出来了,不过呢。。。

并不是所有场景都适合上诉方法,今天我发现了一种新的方法(很简单):只要将图片缩小的足够小就行了,具体原因嘛,读者可以自己思考一下

还有一点,就是计算动画轨迹的问题,尤其是用到除法还有三角函数的时候,最好每一步都要保留精度,以确保最后的结果不会有偏差,这个很重要,如果公式出现问题,图片再怎么缩放也于事无补,具体计算精度方法如下:

var a = Math.floor(a*1000)/1000

同时,能够是整数的值最好取整一下,比如,时间取整:

var b = Math.floor(new Date().getTime() / 1000);

这个问题这几天一直困扰这我,特此记录一下

8. 第三方库引入

浪费了我整整一天,刚创建的项目无法引入第三方库,即使你把egretProperties.json删掉也没有报错,瞬间就懵了。用什么egret build也没有用。
最后用egret clean才起作用,这个答案在网上根本找不到答案,不过现在有了,呵呵

9. 资源加载坑

资源加载本身是没有坑的,但是游戏或者网站升级有有坑了,因此有必要在资源路径后面加个随机数参数:

main.js

let theme = new eui.Theme("resource/default.thm.json?r=" + 随机数, this.stage);

default.res.json(这个是发布的时候再加)

{
...
    "resources": [
        {
            "url": "assets/CheckBox/checkbox_select_disabled.png?r=随机数",
            "type": "image",
            "name": "checkbox_select_disabled_png"
        },
...

使用脚本的形式加参数:

BIN_RES_DIR="bin-release/web/${file}/resource/"
list=`ls ${BIN_RES_DIR}/*.res.json`
for l in ${list}; do
    resfile=${l}
    tm=$(date "+%Y%m%d%H%M%S")
    sed -i "" "s/^\(.*\"url\": \".*\)\"/\1?v=${tm}\"/g" ${resfile}
done

同时可以再次优化,随机数可以替换为md5,这样就不是每次加载都要刷新了,只有文件发生变化是才会重新请求资源,md5命令:

tm=$(md5 <文件> | awk -F " " '{print $4}')

10. 黑屏坑

在手机上部分手机部分会出现黑块、黑色背景

原因:

  1. 不支持eui.Rect设置透明度,若用其当做背景,如果透明度设置不为1,背景颜色设置为黑色,可能显示为完全黑色。解决方法:用半透明的图片替代eui.Rect作为背景

  2. 滚动文章背景全黑,原因:eui.Label文字长度超过1024。解决方法:用多个eui.Label切分字符串

11. 缩放坑

先看代码:

protected createChildren() {
  super.createChildren();
  this.button.addEventListener(egret.TouchEvent.TOUCH_TAP, this.handleTouch, this);
  ...
}


public handleTouch(e: egret.TouchEvent) {
  var x = e.stageX;
  var y = e.stageY;
  console.log(e.stageX, e.stageY);
  ...
}

关于点击事件,貌似这样没有什么问题。但是如果你在this.button的父节点或者当前界面设置了scale,那么要小心了,因为或许你获得的stageXstageY会发生严重偏移,这时候怎么样才能得到真正的点击位置呢。

  var x = e.stageX / scaleX;
  var y = e.stageY / scaleY;

这里的scaleXscaleY就是this.button的父节点或者当前界面设置得缩放值。

结束


以下是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);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,680评论 2 59
  • ✨读白:很久以前,在一个小岛上,每日黄昏,有位老人总看见一个小男孩在海滩上,不断弯下腰去捡东西丢向大海。老人每晚来...
    小义子_正版阅读 646评论 0 0
  • 从折多塘开始,每天住民宿,驼包都是由下一个客栈帮忙从上一个客栈接走,我们每天只轻装上阵,才三天就习惯了。今天再次驼...
    Erick的骑行之旅阅读 674评论 1 6
  • 今天是我参加无戒365挑战营的第三十天,先来说一下前29天的成绩。 >>>>>日更29天的数据 10月26日至11...
    妈小咪阅读 626评论 23 39