首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >HTML5画布性能和优化技巧、技巧和编码最佳实践

HTML5画布性能和优化技巧、技巧和编码最佳实践
EN

Stack Overflow用户
提问于 2011-11-20 23:56:59
回答 4查看 34.7K关注 0票数 57

你知道更多关于画布的最佳实践吗?

请在这个线程中添加您所知道的、所学到的或在线阅读过的任何和所有画布最佳实践、技巧/技巧,以提高性能。

由于画布在互联网上仍然是非常新的,而且我在未来也看不到它老化的迹象,因此在任何一个特定的地方开发画布都没有太多的文档化的“最佳实践”或其他非常重要的技巧,这些技巧是“必须知道的”。像这样的东西到处都是,而且很多时候都在不太知名的网站上。

有很多事情是人们需要知道的,而且还有很多东西需要学习。

我想分享一些东西来帮助那些正在学习画布的人,或者一些已经非常了解画布的人,我希望能从其他人那里得到一些反馈,让他们知道在HTML5中使用画布的一些最佳实践或其他技巧。

首先,我个人认为对于开发人员来说,这是一件非常有用但却令人惊讶的事情。

1.缩进代码

就像你在其他任何时候一样,在任何其他语言中,不管情况如何。这是其他所有东西的最佳实践,我发现在一个复杂的画布应用程序中,当处理几个不同的上下文和保存/恢复状态时,事情会变得有点混乱。更别提代码只是更可读性更强,整体看起来也更干净。

例如:

代码语言:javascript
运行
复制
...
// Try to tell me this doesn't make sense to do
ctx.fillStyle = 'red';
ctx.fill();
ctx.save();
    if (thing < 3) {
        // indenting
        ctx.beginPath();
            ctx.arc(2, 6, 11, 0, Math.PI*2, true);
        ctx.closePath();
        ctx.beginPath();
            ctx.moveTo(20, 40);
            ctx.lineTo(10, 200);
            ctx.moveTo(20, 40);
            ctx.lineTo(100, 40);
        ctx.closePath();
        ctx.save();
            ctx.fillStyle = 'blue'
            ctx.fill();
        ctx.restore();
    } else { 
        // no indenting
        ctx.drawImage(img, 0, 0, 200, 200);
        ctx.save();
        ctx.shadowBlur();
        ctx.beginPath();
        ctx.arc(2, 60, 10, 0, Math.PI*2, false);
        ctx.closePath();
        ctx.fillStyle 'green';
        ctx.fill();
        ctx.restore();
    }
ctx.restore();
ctx.drawRect();
ctx.fill();
...

IF语句是否不容易阅读,也不清楚其中的on语句是怎么回事?你能明白我在说什么吗?我认为这应该是一种开发人员应该继续练习的方法,就像编写普通的“”或者任何其他语言时一样。

使用requestAnimationFrame而不是setInterval / setTimeout

setInterval和setTimeout从来没有被用作动画计时器,它们只是在时间延迟之后调用函数的通用方法。如果您在将来设置了20‘t的间隔,但是您的函数队列比执行时间长,那么直到这些函数完成之后,您的计时器才会触发。这可能需要一段时间,对于动画来说,这并不理想。RequestAnimationFrame是一种告诉浏览器动画正在发生的方法,因此它可以相应地优化重绘。它也为不活动的标签节流动画,所以如果你把它放在后台,它不会杀死你的移动设备的电池。

尼古拉斯·扎卡斯( Nicholas )在他的博客上写了一个非常详细、内容丰富的关于requestAnimationFrame的文章,非常值得一读。如果您想要一些硬和快速的实现说明,那么保罗·爱尔兰人写了一本requestAnimationFrame诗 -这是我在最近制作的每个画布应用程序中使用的。

其实

与使用requestAnimationFrame代替setTimeout和setInterval相比,Joe编写了一个名为requestInterval和requestTimeout的新的改进的垫片,他解释了使用requestAnimFrame时存在的问题。您可以查看剧本要点

实际上是x2

既然所有的浏览器都已经赶上了这个规范,就有了一个更新requestAnimFrame()填充,它可能仍然是用来覆盖所有供应商的一个。

使用一个以上的画布

@nicolahibbert在一个关于优化画布小游戏中提到了一种动画游戏的技术,它提到最好使用多个画布层叠在一起,而不是在一个画布上做所有的事情。尼古拉解释说,“同时在同一张画布上画太多像素会导致你的帧率从地板上掉下来。以”突破“为例。试着画砖块、球、桨、任何力量或武器,然后背景中的每一颗星星--这根本行不通,轮流执行这些指令需要太长时间。通过将星场和游戏的其余部分分割到不同的画布上,你就能确保一个像样的拼图。”

在屏幕外呈现元素

我已经做了一些应用程序,包括三星奥运基因组计划facebook应用程序,我不得不这样做。这是一件非常有用的事情,要知道和利用它是否需要。它极大地减少了加载时间,另外,它也是一种非常有用的技术,可以从屏幕上加载图像,因为它们有时需要一段时间。

代码语言:javascript
运行
复制
var tmpCanvas = document.createElement('canvas'),
    tmpCtx = tmpCanvas.getContext('2d'),
    img = document.createElement('img');

img.onload = function() {
    tmpCtx.drawImage(thumbImg, 0, 0, 200, 200);
};
img.src = '/some/image/source.png';

注意,图像的src是在加载后设置的。这也是一件需要记住的关键事情。完成图像加载并绘制到这些临时画布后,您可以使用相同的ctx.drawImage()将它们绘制到主画布上,但是不用将图像作为第一个参数,而是使用'tmpCtx.canvas‘来引用临时画布。

其他技巧、技巧和资源

  • 画布测试用例
  • 更多的画布和JS测试
  • HTML5Rocks性能改进
  • **优化拖动事件的requestAnimFrame

画布有一个反向引用。

2d上下文对其关联的DOM元素有一个反向引用:

代码语言:javascript
运行
复制
var ctx = doc.getElementById('canvas').getContext('2d');
console.log(ctx.canvas);    // HTMLCanvasElement

我很想听听其他人对此的看法。我正在做一份清单,列出我们应该标准化的东西,以便在我的公司的前端代码标准和最佳实践中增加一个新的部分。我很想得到尽可能多的反馈。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-12-13 07:56:56

重绘区域

动画的最佳画布优化技术是限制每个帧上被清除/绘制的像素的数量。实现最简单的解决方案是重置整个画布元素并重新绘制所有内容,但对于浏览器来说,这是一个昂贵的操作。

在帧之间重用尽可能多的像素。这意味着每帧需要处理的像素越少,程序运行的速度就越快。例如,当使用clearRect(x,y,w,h)方法擦除像素时,只清除和重新绘制已更改的像素而不是整个画布是非常有益的。

程序雪碧

程序生成图形通常是要走的路,但有时这并不是最有效的方法。如果你用实心填充绘制简单的形状,那么按程序绘制它们是最好的方法。但是如果你用笔画、渐变填充和其他性能敏感的化妆来绘制更详细的实体,你最好使用图像精灵。

两者兼而有之是可能的。在应用程序启动时,在画布上按过程绘制图形实体一次。在此之后,您可以通过绘制它们的副本来重用相同的精灵,而不是重复生成相同的阴影、渐变和笔画。

状态堆栈与变换

可以通过诸如旋转和缩放等转换操作画布,从而对画布坐标系进行更改。了解有两种方法可用的状态堆栈是很重要的: context.save() (将当前状态推送到堆栈)和context.restore() (恢复到以前的状态)。这使您能够将转换应用于绘图,然后还原回以前的状态,以确保下一个形状不受任何早期转换的影响。州还包括属性,如填充和笔画颜色。

合成

在使用画布时,一个非常强大的工具是合成模式,它允许掩蔽和分层。有大量可用的复合模式,它们都是通过画布上下文的globalCompositeOperation属性设置的。复合模式也是状态堆栈属性的一部分,因此可以应用复合操作、堆栈状态和应用不同的状态,并在创建第一个状态之前恢复到状态。这可能特别有用。

抗锯齿

为了允许亚像素的绘图,画布的所有浏览器实现都采用了反混叠(尽管这似乎不是HTML5规范中的要求)。如果你想画出清晰的线条,并且注意到结果看起来很模糊的话,反混叠可能是很重要的。这是因为浏览器会插入图像,就好像它实际上是在这些像素之间一样。它的结果是一个更流畅的动画(你真的可以移动半像素每次更新),但它会使你的图像看起来模糊。

要解决这个问题,您将需要整数值,或抵消半像素取决于您是绘制填充或笔画。

对drawImage() x和y位置使用整数

如果在画布元素上调用drawImage,则如果将x和y的位置舍入整数,速度要快得多。

这里有一个关于jsperf的测试用例显示使用整数的速度比使用小数快得多。

所以在渲染之前,把x和y的位置舍入整数。

比Math.round()更快

另一个jsperf测试显示表示,Math.round()不一定是舍入数字最快的方法。实际上,使用逐位攻击的速度比内置方法要快。

帆布雪碧优化

清理画布

要清除所有现有像素的整个画布,通常使用context.clearRect(x、y、w、h),但还有另一个可用的选项。每当设置画布的宽度/高度时,即使它们被反复设置为相同的值,画布也会被重置。当使用动态大小的画布时,这一点很好,因为您会注意到绘图正在消失。

计算分布

Chrome开发工具分析器对于找出性能瓶颈是非常有用的。根据应用程序的不同,您可能需要重构程序的某些部分,以提高性能和浏览器如何处理代码的特定部分。

优化技术

票数 22
EN

Stack Overflow用户

发布于 2011-12-08 12:47:17

这是我的建议

1)使用clearRect清除画布而不是canvas.width=canvas.width,因为稍后会重置画布状态

2)如果在画布上使用鼠标事件,则使用以下功能,它是可靠的,并且在大多数情况下都能工作。

代码语言:javascript
运行
复制
/**  returns the xy point where the mouse event was occured. 
 @param ev The event object.
*/
function getXY(ev){
   return getMousePosition(ev, ev.srcElement || ev.originalTarget);
}

 /**  returns the top-left point of the element
       @param elem The element
   */
function getElementPos(elem){
   var obj = elem;
   var top = 0;
   var left = 0;
    while (obj && obj.tagName != "BODY") {
      top += obj.offsetTop-obj.scrollTop;
      left += obj.offsetLeft -obj.scrollLeft ;
      obj = obj.offsetParent;
     }
  return {
    top: top,
    left: left
    };
};

/**  returns the xy point where the mouse event was occured inside an element. 
@param ev The event object.
 @param elem The element
*/
function getMousePosition(evt, elem){
var pageX, pageY;
if(typeof(window.pageYOffset)=='number') {
    pageX=window.pageXOffset;
    pageY=window.pageYOffset;
}else{
    pageX=document.documentElement.scrollLeft;
    pageY=document.documentElement.scrollTop;
}
var mouseX = evt.clientX - getElementPos(elem).left + pageX;
var mouseY = evt.clientY - getElementPos(elem).top + pageY;
return {
    x: mouseX,
    y: mouseY
};
};

3)如果您想支持ExplorerCanvas,可以使用IE7

4)不只是清理整个帆布,而是清除需要清理的部分。这对表演很有好处。

票数 2
EN

Stack Overflow用户

发布于 2012-04-30 11:16:46

在完成了最近发布了使用画布的Facebook应用程序和用户Facebook配置文件信息(它必须容纳的数据量对一些人来说是巨大的)来匹配你和你的朋友使用该应用程序之后,我已经学到了很多东西,在我的广泛努力中,我可以尽我所能提高应用程序的性能。

我确实花了几个月,几天的时间,只是为了重新分析我已经非常熟悉的代码,并认为这是最理想的方法。

尽可能使用DOM元素

事实上,浏览器还没有准备好在画布中处理更密集的运行应用程序,特别是当您需要开发支持IE8的应用程序时。在编写本文时,有时DOM比Canvas API的当前实现速度更快。至少我发现它是在为三星开发一个非常复杂的单页动画html5和画布应用程序时发现的。

我们能够很好地改善事物的性能,同时仍然使用画布做一些复杂的工作,将图像裁剪成圆圈,这可能是可以坚持我们是如何做的。

在发布前几天,我们决定尝试一种不同的技术,而不是在屏幕外创建临时画布,这些画布一旦裁剪成圆圈,就会被放置在可见的画布上。我们只是在画布上添加了Image元素,使用了之前用于放置临时画布的x和y坐标。

对于将图像裁剪成圆圈,这很简单,我们只是使用CSS3边界-半径属性来完成它,这比复杂的状态变化系列要少得多,而且虽然巧妙和有创意,但过度使用了.clip()方法。

一旦将它们放置在DOM中,图像的动画就会发生,每个图像的DOM节点就会被动画化为画布上的单独实体。我们可以通过CSS轻松地完全控制样式的那些。

这种技术类似于另一种方法,用于完成这类工作,这也是很好的了解,它涉及到层叠画布在彼此之上,而不是画他们到一个上下文。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8205828

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档