做有态度的前端团队

网易FEG前端团队

游戏内嵌项目小结

在做率土之滨的游戏内嵌官网时,花了不少时间,也踩了不少坑,本文来总结下,有些经验可以做为此类型项目的借鉴。后面有彩蛋!

前言

内嵌页面,是指在某个app(游戏)里显示的网页,主要是通过app里面的webview(可以理解为一个浏览器)来展示我们做的网页。理论上内嵌页面和普通页面没多大区别,但是不同的app以及不同系统的app,内置的webview可能会不同,相当于不同的浏览器,所以也会出现一些兼容问题,而这里更头疼的是功能兼容问题,反而样式兼容变得更轻松点,因为不需要考虑各种浏览器(如果该页面只在app里面跑)。

开发过程

关于适配

内嵌页面和普通项目明显不同的表现形式就是横屏展示,竖屏时在游戏打开内嵌页面时是不会发生旋转的,所以不需要担心这个。这里可以分两种情况考虑,一种就是这个内嵌页面不止是在游戏里面跑,也可以在普通浏览器跑,那么适配就需要考虑横屏和竖屏两种情况,我们博客之前已经有相关文章分享了:【横屏页面注意事项】;另一种情况是,这个内嵌页面需要和游戏做数据打通,比如需要拿到游戏id等信息来展示,那么这个页面只能在游戏里面跑,这种情况我们可以偷懒,只需要适配横屏就行了。
因为率土之滨属于第二种情况,所以结合rem的适配方法,只需要稍微改一下根字体的计算:

var w = document.documentElement.clientWidth;
$("html").css("font-size", w/568*312.5 + "%");//568是iPhone5的横屏宽度,因为率土之滨的游戏还是采用以iPhone5的尺寸来适配的,所以页面设计稿也是这个尺寸

关于适配,内嵌页面还需要考虑ipad这类型的设备,目前很多游戏的ipad版都是在手机版的基础下做放大适配的,所以内嵌页面也可以采用这种放大适配而不会很突兀。适配计算和上面一样,但是这里需要考虑到pad和手机的尺寸比例是不一样的,手机是长方形,pad是比较接近正方形,也就是说在横屏下,即使是同比例放大,pad的高度比手机可以展示更多的内容。所以适配的时候,在设置高度时要注意这点。

gameinline01.jpg

关于功能

率土之滨的内嵌官网的功能主要有:资讯展示、信息提交(发起话题、发表评论)、图片上传。前面说过,内嵌的兼容难点主要是在功能上,所以这里列出了一些踩过的坑。

数据打通

这里所谓的数据打通,主要是指在所有内嵌页里面拿到游戏那边传来的token。然后页面所有功能接口都需要传这个token参数,这样接口就能返回游戏id等信息给前端。在游戏里面,只有一个资讯按钮入口,通过这个入口打开webview进入到内嵌首页,所以token这个数据是在打开入口的时候通过url传给首页,这点没什么疑问的。进入到首页后,拿到token得想办法保存下来,因为内嵌里面还有很多其它页面,有一些页面还是已经在线上跑的了。第一个想到的就是cookie,打开首页是通过url拿到token用cookie直接种到163一级域名,这样其它页面就能用了。这时候坑来了,在ios下的webview不支持跨域cookie访问的,貌似Safari也是。比如我在test.nie.163.com下种下顶级域名163.com的cookie,然后在stzb.163.com是无法成功访问的。虽然说页面上线都是在stzb.163.com这个域名,但是测试阶段不能用很麻烦。
后来问到大神了另外一种方式,因为内嵌页都是在当前窗口打开的,所以可以通过window.name来愉快得传递token,它还可以解决跨域传输的问题。

//首页获取token
var token = params(location.search, "token");
if(token){
    window.name = token;
}

//其它页面直接使用window.name即可
var t = window.name

资讯展示

资讯主要来源与新闻系统,这里主要涉及一个滚动到底部翻页加载。因为担心swiper的模拟滚动在webview里面会出现各种诡异的bug,而且考虑到滚动加载的方式,所以这里直接使用原生的div内部滚动,然后滚动到底部用ajax请求加载下一页内容。这一部分挺顺利,没有遇到奇怪的bug。

信息提交(发起话题、发表评论)

内嵌页里面有个话题讨论功能,可以给玩家输入标题、正文以及图片上传(图片上传下面单独讲),类似一个小小论坛那样。然后还有个评论,可以评论玩家发布的话题也可以评论普通的资讯文章。这两个功能都涉及到有内容输入的操作,所以放到一起讲。

1、首先第一个就是XSS攻击(跨站脚本攻击)问题。因为有用户输入的地方,所以需要考虑在显示时过滤一些非法输入,比如用户直接输入js植入代码。这里直接使用现成的组件:https://github.com/leizongmin/js-xss,直接过滤下就行了filterXSS(string)。这块和内嵌没什么关系,做普通网友也要考虑。

2、$.trim()方法不支持,这里忘记是在ios还是在安卓了。因为在提交输入内容时,要阻止单纯空格的情况,贪图方便想直接用trim方法,结果不支持,最后直接用正则来replace:replace(/\s/ig,'')

3、键盘事件的坑。先来看下游戏里面的键盘布局图:

360722558729738618.jpg

结果悲剧发现,一些键盘绑定事件比如keyup、keydown在ios的webview(安卓的不知是否同样存在这个问题)里面,当用户操作上图的左边键盘是无法触发监听的。本来想做实时监控输入的字数的,后来只好去掉了这个功能。

图片上传

上面提到,玩家发起话题是可以上传图片的。这个功能的遇到的坑最难搞了。下面一一道来:

1、委托在document上的事件在ios的webview失效
图片上传功能里有个用户选择图片后可以点击右上角删除选图的功能,一开始把这个点击事件委托在document上:

$(document).on("click",".u.btn_remove",function(e){
//do something....
})

结果在ios里点击无效(安卓没测),后来把事件委托在按钮的最近的父层就行了。本身委托在document上也不是最好的选择。

2、ios webview下点击input来调图册出现闪退问题
通过测试排查,发现在横屏下,默认情况下ios的webview调图册会出现这个问题,这个和前端这边无关,需要app开发那边做修改。

3、安卓webview默认不支持input调图册
默认下,安卓的webview是不支持input调图册的,所以会出现点击input没有任何反应,这个也是需要app开发那边做修改。

4、安卓webview默认不支持alert方法
这个真的有点坑,因为这种内嵌的页面,调试是最麻烦的,基本都是只能通过原始的alert断点调试。一开始不知道安卓居然不支持alert,所以总以为是加入的调试代码在安卓下没有更新,一直没有弹出alert信息,一度以为是缓存问题。后来通过修改样式,排除了缓存的问题,这时候才去怀疑alert的支持性。后来app那边排查到权限问题,需要app开启。

5、某些安卓webview不支持new Blob()
这个问题困扰最久,从定位问题到最终解决也花了最长的调试时间。
先交代下这里图片上传的整个逻辑:通过input调用图册,用户选图后用canvas展示选图,当用户点击发送时再触发图片上传,图片上传是通过压缩然后转blob二进制数据。下面是转换方法:

if ((!_isIE||_BrowserVersion>9)&&!HTMLCanvasElement.prototype.toBlob) {
    Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', {
        value: function (callback, type, quality) {
            var binStr = atob( this.toDataURL(type, quality).split(',')[1] ),
            len = binStr.length,
            arr = new Uint8Array(len);

            for (var i=0; i<len; i++ ) {
                arr[i] = binStr.charCodeAt(i);
            }
            callback( new Blob( [arr], {type: type || 'image/jpeg'} ) );
        }
    });
}

发现问题:在一台三星galaxy note3 lite(SM-N7508V,系统是4.3)的机子的webview里面,无论选图册的任何一张图,页面都弹出是File Type Invalid,图片类型不对,一开始还真以为是这台机子的图片有特殊不支持,结果通过该机子的自带浏览器尝试了上传功能,同样的代码同样的图片是正常的。换一个思路,接口返回后是不是代码哪里写错了,这是需要出动抓包来看请求。本来手机抓包就比较麻烦,这里还要抓远程测试机(这个问题是在一个在线测试机子发现的)的包,这里折腾了不少时间。因为测试机子连的是内网wifi,所以最后通过手机链接的wifi设置代理为我的电脑,然后再通过fiddler在电脑抓包(如果测试机连的不是内网还真不好搞)。
通过抓包发现,图片接口的确返回的File Type Invalid,然后还发现请求头传的图片居然是个HTMLCanvasElement(没记错应该是这个,没截图保存大概忘了),接口自然识别到不是图片。出现这种情况只能拿上传组件(我们的图片上传已经做成一个公用的组件,已经在web上面的项目跑的了)源码过来,通过alert断点调试。
这种调试最耗时的了,而且修改完还要上传发布到测试地址,再去在线测试机刷,然后在线测试机经常用着用着断线或者被别人占用,过程非常痛苦,期间省略一万字。。。
最后艰难发现toBlob方法(就是上面贴出来的转换方法)的回调没执行,按照js的特点,有报错了,后面的代码就不执行,但是在app里面跑的页面你也不知道哪句代码出错。结合try catch发现toBlob方法报 illegal constructor,不合法的构造函数。然后再在toBlob方法里面进行排查,测试到alert(new Blob())不执行,恩,就是它了!通过网上搜索,发现原来有人遇到同样的问题,搜出了new BlobBuilder方法,所以尝试下面的写法:

var array = new Int8Array([17, -45.3]);

try{
  var jpeg = new Blob( [array], {type : "image/jpeg"});
}
catch(e){
    // TypeError old chrome and FF
    window.BlobBuilder = window.BlobBuilder || 
                         window.WebKitBlobBuilder || 
                         window.MozBlobBuilder || 
                         window.MSBlobBuilder;
    if(e.name == 'TypeError' && window.BlobBuilder){
        var bb = new BlobBuilder();
        bb.append(array.buffer);
        var jpeg = bb.getBlob("image/jpeg");
    }
    else if(e.name == "InvalidStateError"){
        // InvalidStateError (tested on FF13 WinXP)
        var jpeg = new Blob( [array.buffer], {type : "image/jpeg"});
    }
    else{
        // We're screwed, blob constructor unsupported entirely   
    }
}

放进去提交测试,恩,没报错,有种柳暗花明又一村的感觉~可是,当点击提交按钮后,出现了另一个问题:

image_20161110151505.png

赶紧抓包,发现请求头的图片值变空了。。。接口拿不到任何图片的数据。。。图片上传使用new FormData来提交:

var form = new FormData();
form.append(uploader.options.input.name, imgObj.img, imgObj.ids);
var xhr = new XMLHttpRequest(); 
xhr.open("post",uploader.options.interface.url);  
xhr.send(form);

上面代码拿到的imgObj都是正常的,但不是就是不知道为什么传过去就丢失了,因为new FormData也是新方法,曾怀疑过这个方法的问题,也有可能是前面在压缩转二进制的时候为后面埋下坑,导致后面出了问题。但最后还是没有找到解决方法。
最终处理方案是,检测到如果该机子不支持new Blob()就直接原图上传,不进行压缩了。这个方案勉强算是解决了上传的问题~

从上面可以看出,上面不少问题是webview自身的问题,因为两边(前端和app开发)都对这块都不熟悉,这些问题都要摸索排查,如果有了经验,这些问题就不是问题了。

测试过程

测试这里可以分在浏览器测试和app内测试,完成开发后可以先在一些手机的自带浏览器进行测试,自带浏览器环境会比较解决webview,解决了在自带浏览器的一些常见bug后再去app内测试。因为一开始就在app内测试,每次测一台手机需要安装一次,工作量是挺大的。
进入到app内测试,就要让app那边给到测试包,现在基本标配ios和安卓的,安卓测试包是apk文件,ios测试包是ipa文件。
因为可能有些问题是同时存在的,所以可以采用先测试ios版本(因为ios的比较流畅,而且各个ios机子下的表现差异比较小,测试会快点),把ios包发现的问题解决了,再去啃安卓。

1、安装
ios测试包安装时,可以在电脑安装iTools工具,通过iTools就可以把ipa文件安装到ios社保啦。另外ios版本安装成功后,需要进入 设置——通用——设备管理里面点击允许后,才能启动游戏app。

2、网络
因为测试包同城都是直接用内部网络,所以测试设备需要连内网wifi netease_game才能登陆游戏,这个wifi需要申请比较麻烦,后来发现netease_wb也可以访问,这个wifi可以直接找我们qa同学借用下就能解决啦。

3、测试机
ios基本测试一两个设备就行了,考虑的样式兼容,可能要跑一下ipad。比较头疼的就是安卓机子,好在我们的MTL测试平台上面的远程机子可以直接在线安装app就可以测试了:先连接上你要的手机——控制面板——应用安装。
QQ截图20161110154857.png

虽然连接不是很稳定,但勉强能用。

最后

上面是在做内嵌页面时的一些小结,目前采用的还是比较原始的普通页面开发方式,都是通过页面的刷新和跳转来完成数据交互和展示,后面如果有机会,会考虑试试下Hybrid 开发模式。

什么?你还没有玩过率土之滨网易全自由沙盘战略手游,快去下载呗!!

手机阅读请扫描下方二维码:

已有 21 条评论

  1. 0KeeTeam

    0KeeTeam

  2. 0KeeTeam

    0KeeTeam

  3. 0KeeTeam

    0KeeTeam

  4. 0KeeTeam

    0KeeTeam

  5. xzjkjzkucbutpwfxbtbh

    0KeeTeam

  6. 0KeeTeam

  7. 0KeeTeam

    xzjkjzkucbutpwfxbtbh

  8. 0KeeTeam

    0KeeTeam

  9. 0KeeTeam

    0KeeTeam

  10. 0KeeTeam

    aypebfviflulbafiwrzp

  11. aypebfviflulbafiwrzp

    0KeeTeam

  12. 0KeeTeam

    0KeeTeam
    CRLF-Header:CRLF-Value

  13. 0KeeTeam

    0KeeTeam%0d%0aCRLF-Header:CRLF-Value

  14. 0KeeTeam

  15. 0KeeTeam

    0KeeTeam\r\nCRLF-Header:CRLF-Value

  16. 0KeeTeam

    0KeeTeam

  17. 0KeeTeam

    0KeeTeam

  18. 0KeeTeam

    0KeeTeam

  19. dkxxdrqazukpkhtwilcb

    0KeeTeam

  20. tnlpvfemlgtfhpzztyrt

    0KeeTeam

  21. 0KeeTeam

添加新评论

ali-40.gifali-41.gifali-42.gifali-43.gifali-44.gifali-45.gifali-46.gifali-47.gifali-48.gifali-49.gifali-50.gifali-51.gifali-52.gifali-53.gifali-54.gifali-55.gifali-56.gifali-57.gifali-58.gifali-59.gifali-60.gifali-61.gif