Javascript HTML5 Canvas 性能和优化技巧、技巧和编码最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8205828/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
HTML5 Canvas Performance and Optimization Tips, Tricks and Coding Best Practices
提问by jaredwilli
DO YOU KNOW SOME MORE BEST PRACTICES FOR CANVAS??
你知道画布的更多最佳实践吗??
Please add to this thread what you know, have learned, or have read online any and all Canvas best practices, tips/tricks for performance
请将您所知道的、所学的或在线阅读的任何和所有 Canvas 最佳实践、性能提示/技巧添加到此线程
With Canvas being still very new to internet, and no signs of it ever getting old that I can see in the future, there are not too many documented "best practices" or other really important tips that are a 'must know' for developing with it in any one particular place. Things like this are scattered around and many times on lesser known sites.
由于 Canvas 对 Internet 来说仍然很新,并且没有任何迹象表明我将来会看到它变老,因此没有太多记录在案的“最佳实践”或其他真正重要的技巧,这些技巧对于开发来说是“必须知道的”它在任何一个特定的地方。像这样的事情在鲜为人知的网站上多次散布。
There's so many things that people need to know about, and still so much to learn about too.
人们需要了解的东西太多了,还有很多东西需要学习。
I wanted to share some things to help people who are learning Canvas and maybe some who already know it quite well and am hoping to get some feedback from others about what they feel are some best practices or other tips and tricks for working with Canvas in HTML5.
我想分享一些东西来帮助那些正在学习 Canvas 的人,也许一些已经很了解它的人,我希望从其他人那里得到一些反馈,让他们知道在 HTML5 中使用 Canvas 的一些最佳实践或其他技巧和窍门.
I want to start off with one I personally found to be quite a useful yet surprisingly uncommon thing for developers to do.
我想从我个人认为对开发人员来说非常有用但非常罕见的事情开始。
1. Indent your code
1. 缩进你的代码
Just as you would any other time, in any other language whatever the case may be. It has been a best practice for everything else, and I have come to find that in a complex canvas app, things can get a little confusing when dealing with several different contexts and saved/restore states. Not to mention the code is just more readable and overall cleaner looking too.
就像您在其他任何时候一样,无论情况如何,都可以使用任何其他语言。这是其他所有方面的最佳实践,我发现在复杂的画布应用程序中,在处理多个不同的上下文和保存/恢复状态时,事情可能会变得有点混乱。更不用说代码更具可读性,整体看起来也更干净。
For example:
例如:
...
// 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();
...
Is the IF statement not easier and cleaner to read and know what is what immediately going on than the ELSE statement in this? Can you see what I'm saying here? I think this should be a method that developers should continue to practice just as they would when writing plain 'ol javascript or any other language even.
IF 语句不是比 ELSE 语句更容易和更清晰地阅读并了解立即发生的事情吗?你能看到我在这里说什么吗?我认为这应该是开发人员应该继续练习的一种方法,就像他们在编写普通的 'ol javascript 或任何其他语言时一样。
Use requestAnimationFrame instead of setInterval / setTimeout
使用 requestAnimationFrame 而不是 setInterval / setTimeout
setInterval and setTimeout were never intended to be used as animation timers, they're just generic methods for calling functions after a time delay. If you set an interval for 20ms in the future, but your queue of functions takes longer than that to execute, your timer won't fire until after these functions have completed. That could be a while, which isn't ideal where animation is concerned. RequestAnimationFrameis a method which tells the browser that an animation is taking place, so it can optimize repaints accordingly. It also throttles the animation for inactive tabs, so it won't kill your mobile device's battery if you leave it open in the background.
setInterval 和 setTimeout 从未打算用作动画计时器,它们只是用于在时间延迟后调用函数的通用方法。如果您将来将间隔设置为 20 毫秒,但您的函数队列需要比执行时间更长的时间,则在这些函数完成之前,您的计时器不会触发。这可能需要一段时间,这在动画方面并不理想。RequestAnimationFrame是一种告诉浏览器正在发生动画的方法,因此它可以相应地优化重绘。它还会限制非活动选项卡的动画,因此如果您将其在后台打开,它不会耗尽移动设备的电池。
Nicholas Zakas wrote a hugely detailed and informative article about requestAnimationFrameon his blog which is well worth reading. If you want some hard and fast implementation instructions, then Paul Irish has written a requestAnimationFrame shim– this is what I've used in every one of the Canvas apps I have made until recently.
Nicholas Zakas在他的博客上写了一篇关于 requestAnimationFrame的非常详细且内容丰富的文章,非常值得一读。如果你想要一些硬性和快速的实现说明,那么Paul Irish 写了一个 requestAnimationFrame shim——这是我最近在我制作的每个 Canvas 应用程序中使用的。
ACTUALLY
实际上
Even better than using requestAnimationFrame in place of setTimeout and setInterval, Joe Lambert has written a NEW and improved shimcalled requestInterval and requestTimeout, which he explains what issues exist when using requestAnimFrame. You can view the gist of the script.
与使用 requestAnimationFrame 代替 setTimeout 和 setInterval 相比,Joe Lambert 编写了一个名为 requestInterval 和 requestTimeout的新改进垫片,他解释了使用 requestAnimFrame 时存在的问题。您可以查看脚本的要点。
ACTUALLY x2
实际上 x2
Now that all the browsers have caught up on the spec for this, there has been an update to the requestAnimFrame() polyfill, one which will probably remain the one to use to cover all vendors.
现在所有的浏览器都已经赶上了这个规范,requestAnimFrame() polyfill已经更新了,它可能会继续用于覆盖所有供应商。
Use more than one canvas
使用多个画布
A technique for animation-heavy games which @nicolahibbertwrote about in a post of hers on optimizing Canvas gamesmentions that it may be better to use multiple canvasses layered on top of one another rather than do everything in a single canvas. Nicola explains that "drawing too many pixels to the same canvas at the same time will cause your frame rate to fall through the floor. Take Breakout for example. Trying to draw the bricks, the ball, the paddle, any power-ups or weapons, and then each star in the background – this simply won't work, it takes too long to execute each of these instructions in turn. By splitting the starfield and the rest of the game onto separate canvases, you are able to ensure a decent framerate."
@nicolahibbert在她的一篇关于优化 Canvas 游戏的帖子中提到了一种用于大量动画游戏的技术,其中提到最好使用多个叠加在一起的画布,而不是在单个画布中完成所有工作。Nicola 解释说,“同时在同一画布上绘制过多像素会导致帧率下降。以 Breakout 为例。尝试绘制砖块、球、桨、任何电源或武器, 然后是背景中的每个星星——这根本行不通,依次执行这些指令需要很长时间。通过将星空和游戏的其余部分拆分到单独的画布上,您可以确保体面的帧率。”
Render Elements Off-screen
在屏幕外渲染元素
I have had to do this for a few apps I've made including Samsung's Olympic Genome Project facebook app. It's an extremely useful thing to know and to make use of whether it's needed or not. It decreases load time immensely, plus it can be a really useful technique to load images off screen since they can sometimes take a while.
我不得不为我制作的一些应用程序执行此操作,包括三星的奥林匹克基因组项目 facebook 应用程序。了解并利用它是否需要,这是一件非常有用的事情。它极大地减少了加载时间,而且它可以是一种非常有用的技术,可以将图像加载到屏幕外,因为它们有时需要一段时间。
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';
Notice that the src of the image is set after it is loaded. This is a key thing to remember to do too. Once the images are done loading and drawn into these temp canvases, you can then draw them to your main canvas by using the same ctx.drawImage(), but instead of putting the image as the first argument, you use 'tmpCtx.canvas' to reference the temp canvas.
请注意,图像的 src 是在加载后设置的。这也是要记住的关键事情。一旦图像完成加载并绘制到这些临时画布中,您就可以使用相同的 ctx.drawImage() 将它们绘制到主画布上,但不是将图像作为第一个参数,而是使用 'tmpCtx.canvas'引用临时画布。
Other tips, tricks and resources
其他提示、技巧和资源
- Canvas test cases
- Some more canvas and JS tests
- HTML5Rocks performance Improving
- **requestAnimFrame to Optimize Dragging Events
Canvas has a back-reference
Canvas 有一个反向引用
The 2d context has a back reference to it's associated DOM element:
2d 上下文具有对其关联 DOM 元素的反向引用:
var ctx = doc.getElementById('canvas').getContext('2d');
console.log(ctx.canvas); // HTMLCanvasElement
I'd love to hear more from other people on this. I am working on making a list of things that we should standardize to add a new section to my company's Front-end Code Standards and Best Practices. I'd love to get as much feedback on this as I can.
我很想从其他人那里听到更多关于这方面的信息。我正在制定一份清单,列出我们应该标准化的内容,以便在我公司的前端代码标准和最佳实践中添加一个新部分。我很想得到尽可能多的反馈。
采纳答案by jaredwilli
Redraw Regions
重绘区域
The best canvas optimization technique for animations is to limit the amount of pixels that get cleared/painted on each frame. The easiest solution to implement is resetting the entire canvas element and drawing everything over again but that is an expensive operation for your browser to process.
动画的最佳画布优化技术是限制在每帧上清除/绘制的像素数量。最容易实现的解决方案是重置整个画布元素并重新绘制所有内容,但这对于您的浏览器来说是一项代价高昂的操作。
Reuse as many pixels as possible between frames. What that means is the fewer pixels that need to be processed each frame, the faster your program will run. For example, when erasing pixels with the clearRect(x, y, w, h) method, it is very beneficial to clear and redraw only the pixels that have changed and not the full canvas.
在帧之间重复使用尽可能多的像素。这意味着每帧需要处理的像素越少,程序运行得越快。例如,在使用 clearRect(x, y, w, h) 方法擦除像素时,仅清除和重绘发生变化的像素而不是整个画布是非常有益的。
Procedural Sprites
程序精灵
Generating graphics procedurally is often the way to go, but sometimes that's not the most efficient one. If you're drawing simple shapes with solid fills then drawing them procedurally is the best way do so. But if you're drawing more detailed entities with strokes, gradient fills and other performance sensitive make-up you'd be better off using image sprites.
按程序生成图形通常是可行的方法,但有时这不是最有效的方法。如果您要绘制带有实心填充的简单形状,那么按程序绘制它们是最好的方法。但是,如果您使用笔触、渐变填充和其他对性能敏感的构成来绘制更详细的实体,则最好使用图像精灵。
It is possible to get away with a mix of both. Draw graphical entities procedurally on the canvas once as your application starts up. After that you can reuse the same sprites by painting copies of them instead of generating the same drop-shadow, gradient and strokes repeatedly.
两者混合使用是可能的。当您的应用程序启动时,在画布上按程序绘制图形实体一次。之后,您可以通过绘制它们的副本来重用相同的精灵,而不是重复生成相同的阴影、渐变和笔触。
State Stack & Transformation
状态堆栈和转换
The canvas can be manipulated via transformations such as rotation and scaling, resulting in a change to the canvas coordinate system. This is where it's important to know about the state stack for which two methods are available: context.save() (pushes the current state to the stack) and context.restore() (reverts to the previous state). This enables you to apply transformation to a drawing and then restore back to the previous state to make sure the next shape is not affected by any earlier transformation. The states also include properties such as the fill and stroke colors.
画布可以通过旋转和缩放等转换进行操作,从而改变画布坐标系。这是了解状态堆栈的重要之处,有两种方法可用:context.save()(将当前状态推送到堆栈)和 context.restore()(恢复到以前的状态)。这使您可以对绘图应用变换,然后恢复到之前的状态,以确保下一个形状不受任何先前变换的影响。状态还包括诸如填充和笔触颜色之类的属性。
Compositing
合成
A very powerful tool at hand when working with canvas is compositing modes which, amongst other things, allow for masking and layering. There's a wide array of available composite modes and they are all set through the canvas context's globalCompositeOperation property. The composite modes are also part of the state stack properties, so you can apply a composite operation, stack the state and apply a different one, and restore back to the state before where you made the first one. This can be especially useful.
使用画布时,手头的一个非常强大的工具是合成模式,除其他外,它允许蒙版和分层。有多种可用的复合模式,它们都是通过画布上下文的 globalCompositeOperation 属性设置的。复合模式也是状态堆栈属性的一部分,因此您可以应用复合操作,堆叠状态并应用不同的状态,然后恢复到您创建第一个状态之前的状态。这可能特别有用。
Anti-Aliasing
抗锯齿
To allow for sub-pixel drawings, all browser implementations of canvas employ anti-aliasing (although this does not seem to be a requirement in the HTML5 spec). Anti-aliasing can be important to keep in mind if you want to draw crisp lines and notice the result looks blurred. This occurs because the browser will interpolate the image as though it was actually between those pixels. It results in a much smoother animation (you can genuinely move at half a pixel per update) but it'll make your images appear fuzzy.
为了允许子像素绘图,画布的所有浏览器实现都采用抗锯齿(尽管这似乎不是 HTML5 规范中的要求)。如果您想绘制清晰的线条并注意到结果看起来很模糊,请务必记住抗锯齿。发生这种情况是因为浏览器将插入图像,就好像它实际上是在这些像素之间一样。它会产生更流畅的动画(您可以真正以每次更新半像素的速度移动),但它会使您的图像显得模糊。
To work around this you will need to either round to whole integer values or offset by half a pixel depending on if you're drawing fills or strokes.
要解决此问题,您需要四舍五入为整数值或偏移半个像素,具体取决于您是绘制填充还是描边。
Using Whole Numbers for drawImage() x and y positions
对 drawImage() x 和 y 位置使用整数
If you call drawImage on the Canvas element, it's much faster if you round the x and y position to a whole number.
如果在 Canvas 元素上调用 drawImage,将 x 和 y 位置四舍五入为整数会快得多。
Here's a test case on jsperfshowing how much faster using whole numbers is compared to using decimals.
这是一个关于 jsperf 的测试用例,显示了使用整数比使用小数快多少。
So round your x and y position to whole numbers before rendering.
因此,在渲染之前将 x 和 y 位置四舍五入为整数。
Faster than Math.round()
比 Math.round() 快
Another jsperf test showsthat Math.round() is not necessarily the fastest method for rounding numbers. Using a bitwise hack actually turns out to be faster than the built in method.
另一个 jsperf 测试表明Math.round() 不一定是舍入数字的最快方法。使用按位 hack 实际上比内置方法更快。
Clearing the Canvas
清除画布
To clear the entire canvas of any existing pixels context.clearRect(x, y, w, h) is typically used – but there is another option available. Whenever the width/height of the canvas are set, even if they are set to the same value repeatedly, the canvas is reset. This is good to know when working with a dynamically sized canvas as you will notice drawings disappearing.
要清除整个画布上的任何现有像素,通常使用 context.clearRect(x, y, w, h) - 但还有另一个选项可用。每当设置画布的宽度/高度时,即使将它们重复设置为相同的值,也会重置画布。在使用动态大小的画布时,了解这一点很有好处,因为您会注意到绘图消失了。
Computation Distribution
计算分布
The Chrome Developer Tools profiler is very useful for finding out what your performance bottlenecks are. Depending on your application you may need to refactor some parts of your program to improve the performance and how browsers handle specific parts of your code.
Chrome 开发者工具分析器对于找出您的性能瓶颈非常有用。根据您的应用程序,您可能需要重构程序的某些部分以提高性能以及浏览器如何处理代码的特定部分。
回答by jaredwilli
After having worked on a recently launched Facebook app that uses Canvasand users Facebook profile information (the amount of data it must accommodate is massive for some) to match you and friends of yours also using the app, to Olympic athletes like a 6 degrees of separation type of thing, there's quite a lot I have learned in my extensive efforts to do everything I could possibly try for increasing performance within the app.
在最近推出了使用 Canvas 的 Facebook 应用程序和用户 Facebook 个人资料信息(它必须容纳的数据量对某些人来说是巨大的)进行工作后,将您和您的朋友也使用该应用程序与奥林匹克运动员相匹配,例如 6 度分离类型的事情,我在我的广泛努力中学到了很多东西,我可以尝试提高应用程序的性能。
I literally spent months, and days at a time just working to re-factor the code which I knew already so well, and believed it to be the most optimal way to do things.
我真的花了几个月,几天的时间来重构我已经非常熟悉的代码,并相信它是做事情的最佳方式。
Use DOM Elements Whenever Possible
尽可能使用 DOM 元素
The fact is, browsers are still just not ready to handle more intensive running applications in Canvas, especially if you're required to develop the app with support for IE 8. There are sometimes cases where the DOM is faster than the current implementation of the Canvas API at the time of writing this. At least I've found it to be while working on a massively complex single page animating html5 and canvas application for Samsung.
事实是,浏览器还没有准备好在 Canvas 中处理更密集的运行应用程序,特别是如果您需要开发支持 IE 8 的应用程序。有时在某些情况下 DOM 比当前的实现更快撰写本文时的 Canvas API。至少我发现它是在为三星处理一个非常复杂的单页动画 html5 和画布应用程序时。
We were able to do quite well at improving the performance of things while still using Canvas to do some complex work to crop images into circles, which would've probably been ok to stick with how we were doing it.
我们能够在提高性能方面做得很好,同时仍然使用 Canvas 来做一些复杂的工作,将图像裁剪成圆形,这可能会一直坚持我们的做法。
Days before the launch, we decided to try a different technique, and rather than create temporary canvases off-screen which were placed on the visible canvas once cropped into circles etc.., we just appended Image DOM elements on the Canvas, using the x and y coordinates that we had been using for placing the temp canvases before.
在发布前几天,我们决定尝试一种不同的技术,而不是在屏幕外创建临时画布,这些画布一旦被裁剪成圆形等就被放置在可见画布上,我们只是在画布上附加了 Image DOM 元素,使用 x以及我们之前用于放置临时画布的 y 坐标。
For cropping the images into circles, well that was simple, we just used the CSS3 border-radius property to do it which was far less work than the complex series of state changes and while ingenious and creative yet over-use of the .clip() method.
为了将图像裁剪成圆形,这很简单,我们只使用了 CSS3 border-radius 属性来完成它,这比复杂的状态变化系列要少得多,虽然巧妙而有创意但过度使用了 .clip( ) 方法。
Once they are placed in the DOM, the animation of images the occurs, and the DOM nodes for each image are animated as separate entities of the Canvas. Ones that we can have full control over the styling off easily through CSS.
一旦它们被放置在 DOM 中,就会发生图像的动画,并且每个图像的 DOM 节点都作为 Canvas 的单独实体进行动画处理。我们可以通过 CSS 轻松完全控制样式。
This technique is similar to another method for doing this type of work that is quite good to know as well, which involves layering Canvases on top of each other, rather than draw them to one context.
这种技术类似于执行此类工作的另一种方法,该方法也很值得了解,它涉及将 Canvas 分层放置在彼此之上,而不是将它们绘制到一个上下文中。
回答by jaredwilli
Here's some more tips and suggestions I put into a list last night worth sharing.
以下是我昨晚放入值得分享的列表中的更多提示和建议。
Don't include jQuery unless you need to do more than just selecting the
<canvas>.I've managed to get by without it for almost everything I've made in canvas
Create abstracted functionsand decouple your code. Separate functionality from appearance or initial draw state.
Make common functions reusable as much as possible. Ideally, you should use a module pattern, which you can create a utils object that contains common functions.
Use single and double letter variable names when it makes sense(x, y, z).
The coordinate system in Canvas adds more single letters that are commonly declared as variables. Which can lead to creating multiple single/double variables (dX, dY, aX, aY, vX, vY) as part of an element.
I suggest you type out or abbr. the word (dirX, accelX, velX) or be descriptive, otherwise things could get pretty confusing for you later on, trust me.
Create constructor functions which can be invoked as needed for making game elements. You can add custom methods and properties within the constructor, and create any number of you may need and they all will have their own properties and methods.
Example of a Ball constructor function I made:
// Ball constructor var Ball = function(x, y) { this.x = x; this.y = y; this.radius = 10; this.color = '#fff'; // Direction and min, max x,y this.dX = 15; this.dY = -15; this.minX = this.minY = 20 + this.radius; this.maxX = this.radius - (canvasWidth - 20); this.maxY = this.radius + canvasHeight; this.draw = function(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, twoPI, true); ctx.closePath(); ctx.save(); ctx.fillStyle = this.color; ctx.fill(); ctx.restore(); }; };
不要包含 jQuery,除非您需要做的不仅仅是选择
<canvas>.我在画布上制作的几乎所有东西都没有它
创建抽象函数并解耦您的代码。将功能与外观或初始绘制状态分开。
尽可能使常用功能可重用。理想情况下,您应该使用模块模式,您可以创建一个包含常用功能的 utils 对象。
在有意义时使用单字母和双字母变量名( x, y, z)。
Canvas 中的坐标系添加了更多通常声明为变量的单个字母。这可能导致创建多个单/双变量(dX, dY, aX, aY, vX, vY)作为元素的一部分。
我建议你输入或缩写。这个词 ( dirX, accelX, velX) 或者是描述性的,否则以后你可能会很困惑,相信我。
创建可以根据需要调用以制作游戏元素的构造函数。您可以在构造函数中添加自定义方法和属性,并创建您可能需要的任意数量的方法和属性,它们都将拥有自己的属性和方法。
我制作的 Ball 构造函数示例:
// Ball constructor var Ball = function(x, y) { this.x = x; this.y = y; this.radius = 10; this.color = '#fff'; // Direction and min, max x,y this.dX = 15; this.dY = -15; this.minX = this.minY = 20 + this.radius; this.maxX = this.radius - (canvasWidth - 20); this.maxY = this.radius + canvasHeight; this.draw = function(ctx) { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, twoPI, true); ctx.closePath(); ctx.save(); ctx.fillStyle = this.color; ctx.fill(); ctx.restore(); }; };
Creating the Ball
创造球
ball = new Ball(centerX, canvasHeight - paddle.height - 30);
ball.draw(ctx);
A good base to work with is to create 3 functions: init() - do all the initial work, and setup the base vars and event handlers etc... draw() - called once to begin the game and draws the first frame of the game, including the creation of elements that may be changing or need constructing. update() - called at the end of draw() and within itself via requestAnimFrame. Updates properties of changing elements, only do what you need to do here.
Do the least amount of work within the loop, making updates to the changing parts or elements. Create the game elements do any other UI work outside the animation loop.
The animation loop is often a recursive function, meaning it calls itself rapidly and repeatedly during the animation to draw each frame.
If there are many elements being animated at once, you might want to first create the elements using a constructor function if your not already, and then within the constructor make a 'timer' method that has requestAnimFrame/setTimeout using it just how you would normally within any animation loop, but effects this element specifically only.
You could make each game element have it's own timer, draw, and animate methodsin the constructor.
Doing this gives you full separation of control for each element and one big animation loop will not be necessary at all since the loop is broken up into each element and you start/stop at will.
一个好的基础是创建 3 个函数: init() - 完成所有初始工作,并设置基本变量和事件处理程序等... draw() - 调用一次以开始游戏并绘制第一帧游戏,包括创建可能会改变或需要构建的元素。update() - 在 draw() 结束时调用,并通过 requestAnimFrame 在其内部调用。更新变化元素的属性,这里只做你需要做的。
在循环中做最少的工作,对变化的部分或元素进行更新。创建游戏元素执行动画循环之外的任何其他 UI 工作。
动画循环通常是一个递归函数,这意味着它在动画期间快速重复地调用自己以绘制每一帧。
如果有许多元素同时进行动画处理,您可能希望首先使用构造函数创建元素(如果还没有的话),然后在构造函数中创建一个具有 requestAnimFrame/setTimeout 的“timer”方法,就像您通常使用的那样在任何动画循环中,但仅特别影响此元素。
您可以让每个游戏元素在构造函数中都有自己的计时器、绘制和动画方法。
这样做可以让您完全分离对每个元素的控制,并且根本不需要一个大的动画循环,因为循环被分解为每个元素并且您可以随意开始/停止。
Or another option:
或另一种选择:
- Create a Timer() constructor functionwhich you can use and give each animating element individually, thereby minimizing work load within animation loops
- 创建一个 Timer() 构造函数,您可以使用它并单独赋予每个动画元素,从而最大限度地减少动画循环中的工作量
回答by Shamaila Tahir
Here are my tips
这是我的提示
1) Use clearRect to clear the canvas instead of canvas.width=canvas.width, because later resets the canvas states
1)使用clearRect来清除画布而不是canvas.width=canvas.width,因为后面会重置画布状态
2) If you are using mouse events on the canvas use following function, its is reliable and works in most of the cases.
2)如果您在画布上使用鼠标事件,请使用以下功能,它是可靠的并且在大多数情况下都可以使用。
/** 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) Use ExplorerCanvas if you want to support IE7
3) 如果您想支持 IE7,请使用 ExplorerCanvas
4) Instead of clearing the whole canvas clear only the part which is needed to be cleaned. Its good for performance.
4)只清除需要清除的部分,而不是清除整个画布。它对性能有好处。

