您好我有一个Web应用程序应该可以在智能手机和桌面浏览器上运行。虽然我期望在像Iphone这样的小型设备上得到一些奇怪的行为,但我非常有信心它会在Android Galaxy Tab上运行良好,这是我现在可以运行测试的Android设备。
现在我在Galaxy Tab上安装了一堆浏览器来测试:
在我使用的桌面上
最后我有一台Iphone要测试。
该网站使用HTML5画布进行像素和基于精灵的绘制,没有花哨的转换,滤镜或效果,主要是简单的路径和多边形。我会听取触摸事件并使用requestAnimationFrame
进行正确的重绘。
整体而言,应用程序在桌面浏览器上运行良好,它在iOS Safari(iPhone)和Firefox-on-Android上运行良好。 Androids Native Browser给我带来了麻烦。我已经设置好了,当javascript没有响应时屏幕会刷新红色,并且在触摸屏幕时它几乎总是闪烁。
所以我想知道Android Native App和HTML5是否存在任何已知问题。由于本机浏览器的名称不存在,因此很难获得有关此内容的Google信息。 我可以获取更多信息的任何想法?什么可能导致本机Android浏览器滞后的想法?
关于这个问题有一些想法:
iOS不支持requestAnimationFrame,因此我将其替换为基于超时的替换。如果我在Android的本机浏览器上使用该替代品,则问题仍然存在。
我经常使用AJAX(google clojure xhrio)从服务器检索数据。可能是数据检索回调是cloggin我的事件管道吗?
是否已知日志控制台消息(console.log)会降低应用程序的速度?他们可以触发浏览器重新运行DOM树或任何相关的东西吗?
答案 0 :(得分:42)
我在很多浏览器中都用canvas进行了很多实验。我注意到的一些性能问题:
首先,关于你的猜测:
当浏览器支持requestAnimationFrame
时,绘图内容和应用本身的响应速度更快。使用setTimeout
或setInterval
作为后备始终是可行的,但您需要注意时间安排。这个robust polyfill可能会有所帮助,但与原生requestAnimationFrame相比没什么。
如果每帧(或几乎)调用console.log,则表示性能下降。 由于原生Android浏览器没有控制台对象,因此每次调用它都会产生错误,这也会导致应用程序运行速度变慢。你可以这样做:
if(typeof console === "undefined"){
console = {};
}
对于强烈的实时应用程序web sockets比http请求更快。遗憾的是,旧的原生Android浏览器不支持此功能。如果无法使用Web套接字,则应最小化http请求。
注意:Chrome for android支持此处引用的大部分HTML5功能,包括requestAnimationFrame
和websockets
。
更多信息:
使用上下文2d fillText
绘制文本它太贵了,但在某些浏览器中这更糟糕。在另一个画布中预渲染文本或使用位图字体。 (在原生Android浏览器中,在替换filltext
绘图用于预渲染之后,在我制作的一些游戏中,性能从10-15 FPS增长到30-45 FPS。
避免缩放和旋转上下文,因为它们还会导致性能下降。如果您只需要缩放或旋转精灵一次,请使用预渲染。
您需要最小化实时绘图。尽可能预先渲染你的东西。仅重绘已更改且需要更新的内容。
尝试编写memory efficient和垃圾收集器友好代码。
还有很多事情要做。我刚才引用了一些。
提示:对您不确定是否是性能杀手的功能进行一些压力测试并获取基准测试结果。
在移动应用程序中,特别是实时应用程序,如果只是过度优化或一点内存增益,所有优化都是受欢迎的。
有关更多信息,请访问以下链接:
还在Posts & Tutorials中搜索效果。
修改强>
这个jsfiddle code snippet显示了这个答案中涵盖的一些内容,并提供了与基准测试相对应的粗略fps。自己编辑这个小提琴并查看它。
答案 1 :(得分:0)
根据您的绘图,使用Html5画布的最常见的性能改进策略是利用图层(即多个画布)并仅更新需要重绘的图层,而不是重绘每个动画帧上的整个图形。你可以自己滚动这样的东西,或者你可以使用http://www.concretejs.com/之类的东西,它是一个轻量级的Html5画布框架,支持点击检测,分层,缓存,像素比例支持,下载等外围设备。你会做点什么像这样:
var wrapper = new Concrete.Wrapper({
width: 500,
height: 300,
container: el
});
var layer1 = new Concrete.Layer();
var layer2 = new Concrete.Layer();
wrapper.add(layer1).add(layer2);
// something happens which requires you to redraw layer2, but not layer1...
layer2.sceneCanvas.context.fillStyle = 'red';
layer2.sceneCanvas.context.fillRect(0, 0, 200, 100);