某些移动浏览器中,HTML5画布性能问题。

15

嗨,我有一个网络应用程序,应该能够在智能手机和台式机浏览器上运行。虽然我预计在像iPhone这样的小设备上会遇到一些奇怪的行为,但我相当有信心它可以在Android Galaxy Tab上良好运行,这是我目前可用于测试的Android设备。

现在我已经在Galaxy Tab上安装了一堆浏览器来进行测试:

  • Android原生浏览器
  • Chrome for Android
  • Firefox for Android

在台式机上,我使用的是:

  • Firefox
  • Google Chrome

最后,我有一台iPhone可以进行测试。

该网站使用HTML5画布进行基于像素和精灵的绘图,没有花哨的转换、滤镜或效果,主要是简单的路径和多边形。 我确实监听触摸事件并使用requestAnimationFrame进行正确的重绘。

总体而言,该应用程序在台式机浏览器上运行良好,在iOS Safari(iPhone)和Firefox-on-Android上也运行良好。但是,Android原生浏览器给我带来了麻烦。 我已经设置好了屏幕在javascript不响应时刷新为红色,几乎每次触摸屏幕时它都会闪烁。

所以我想知道Android原生应用程序和HTML5是否存在任何已知的问题。 由于原生浏览器的名称不存在,因此很难在Google上搜索相关信息。您有任何建议可以获取更多信息吗?您有任何想法是什么导致本机Android浏览器延迟的原因?

关于这个问题有一些想法:

  • iOS不支持requestAnimationFrame,因此我使用基于timeout的替代方案进行了替换。如果我在Android的原生浏览器上使用该替代方案,则问题仍然存在。

  • 我经常使用AJAX(Google Closure Xhrio)从服务器检索数据。是否可能数据检索回调正在阻塞我的事件管道?

  • 是否已知日志控制台消息(console.log)会减慢应用程序? 它们是否会触发浏览器重新运行DOM树或其他任何相关操作?


3
我无法回答你确切的问题,但是使用控制台可能会消耗大量内存,具体取决于你如何使用它。特别是如果你记录大型对象或非常频繁地记录日志。 - idbehold
2个回答

44

我在许多浏览器中进行了许多与画布相关的实验。注意到一些性能问题:

首先,关于你的猜测:

  • 当浏览器支持requestAnimationFrame时,绘制和应用程序本身更具响应性。使用setTimeoutsetInterval作为后备始终是可能的,但需要注意时间。这个强大的 polyfill可能有所帮助,但与本地的 requestAnimationFrame 相比无法相提并论。

  • 如果每帧(或几乎每帧)调用 console.log,则性能会下降。 由于原生 Android 浏览器没有控制台对象,每次调用都会生成错误,这也会导致应用程序变慢。你可以这样做:

    if(typeof console === "undefined"){ console = {}; }

  • 对于强烈的实时应用程序,WebSockets 比 http 请求更快。不幸的是,旧版原生 Android 浏览器不支持此功能。如果无法使用 WebSockets,则应尽量减少 http 请求。

注意:Chrome for android支持这里引用的大多数HTML5功能,包括requestAnimationFramewebsockets
更多信息:
  • 使用上下文2d fillText绘制文本太昂贵了,在某些浏览器中甚至更糟。预先在另一个画布中渲染您的文本或使用位图字体。(在原生Android浏览器中,将filltext绘制替换为预渲染后,我制作的一些游戏的性能从10-15 FPS提高到30-45 FPS)。

  • 避免缩放和旋转上下文,因为它们也会导致性能下降。如果您只需要一次缩放或旋转精灵,请使用预渲染。

  • 尽可能减少实时绘图。在可以的情况下预渲染您的内容。仅重新绘制已更改并需要更新的内容。

  • 尝试编写内存效率和垃圾收集器友好的代码。

还有很多事情要做。我只列举了一些。

提示:对于你不确定是否会影响性能的功能,进行一些压力测试并捕获基准结果。

在移动应用程序中,特别是实时应用程序,所有优化都受欢迎,无论是过度优化还是一点内存收益。

有关更多信息,请参阅以下链接:

还可以在Posts & Tutorials中搜索性能相关内容。

编辑
jsfiddle code snippet显示了本答案涵盖的一些内容,并提供了一个粗略的fps计数器来进行基准测试。自己编辑此代码片段并查看它。


5
非常感谢您的帮助和深入的回答。非常感激 ;) - wirrbel
在使用额外的画布进行预渲染时,请确保它们的内容不会被裁剪,例如旋转文本。在这种情况下,您可以在预渲染画布上进行内容转换,然后在使用drawImage的画布上将其移回:preRenderCtx.translate(100, 100); preRenderCtx.rotate(rotation); preRenderCtx.fillText(char, 0, 0); context.drawImage(preRenderCanvas, x-100, y-100); - Micha Schwab

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);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接