5.11 增强画布并显示FPS值
看了上一节的例子后,你可能会想“微生物的数量是有限制的吗?”,对该问题直截了当的回答是“是”。由于HTML5的画布上下文没有硬件加速,并且我们的动画完全由JavaScript来驱动,如果长时间工作,肯定有这么一个点,当过了这个点,浏览器就会歇菜。为了说明这个,我们可以显示动画的FPS,并观察屏幕上微生物的数目和FPS之间的关系。
操作步骤
按照以下步骤,为画布加压,并显示FPS的值:
1. 链接到Animation类:
<head>
<script src="animation.js"> </script>
2. 定义drawFps()函数,该函数把FPS的值绘制到画布的右上角:
function drawFps(anim, fps){
var canvas = anim.getCanvas();
var context = anim.getContext();
context.fillStyle = "black";
context.fillRect(canvas.width - 100, 0, 100, 30);
context.font = "18pt Calibri";
context.fillStyle = "white";
context.fillText("fps: " + fps.toFixed(1), canvas.width - 93, 22);
}
3. 定义getRandColor()函数,该函数返回一个随机的颜色:
<script>
function getRandColor(){
var colors = ["red", "orange", "yellow", "green", "blue", "violet"];
return colors[Math.floor(Math.random() * colors.length)];
}
4. 定义getRandTheta()函数,该函数返回一个随机的角度:
function getRandTheta(){
return Math.random() * 2 * Math.PI;
}
5. 定义updateMicrobes()函数,该函数使用随机产生的角度为每个微生物添加一个新的头段,再把去掉其尾段来更新microbe对象:
function updateMicrobes(anim, microbes){
var canvas = anim.getCanvas();
var angleVariance = 0.2;
for (var i = 0; i < microbes.length; i++) {
var microbe = microbes[i];
var angles = microbe.angles;
/*
* good numNewSegmentsPerFrame values: * 60fps -> 1
* 10fps -> 10
*
* for a linear relationship, we can use the
equation:
* n = mf + b, where n = numNewSegmentsPerFrame and f
= FPS
* solving for m and b, we have: * n = (-0.18)f + 11.8
*/
var numNewSegmentsPerFrame = Math.round(-0.18 * anim.getFps() + 11.8);
for (var n = 0; n < numNewSegmentsPerFrame; n++) {
// create first angle if no angles
if (angles.length == 0) {
microbe.headX = canvas.width / 2;
microbe.headY = canvas.height / 2;
angles.push(getRandTheta());
}
var headX = microbe.headX;
var headY = microbe.headY;
var headAngle = angles[angles.length - 1];
// create new head angle
var dist = anim.getTimeInterval() / (10 * numNewSegmentsPerFrame);
// increase new head angle by an amount equal to
// -0.1 to 0.1
var newHeadAngle = headAngle + ((angleVariance / 2) - Math.random() * angleVariance);
var newHeadX = headX + dist * Math.cos(newHeadAngle);
var newHeadY = headY + dist * Math.sin(newHeadAngle);
// change direction if collision occurs
if (newHeadX >= canvas.width || newHeadX <= 0
|| newHeadY >= canvas.height || newHeadY <= 0) {
newHeadAngle += Math.PI / 2;
newHeadX = headX + dist * Math.cos(newHeadAngle);
newHeadY = headY + dist * Math.sin(newHeadAngle);
}
microbe.headX = newHeadX;
microbe.headY = newHeadY;
angles.push(newHeadAngle);
// remove tail angle
if (angles.length > 20) {
angles.shift();
}
}
}
}
6. 定义drawMicrobes()函数,该函数绘制所有微生物:
function drawMicrobes(anim, microbes){
var segmentLength = 2; // px
var context = anim.getContext();
for (var i = 0; i < microbes.length; i++) {
var microbe = microbes[i];
var angles = microbe.angles; context.beginPath();
context.moveTo(microbe.headX, microbe.headY);
var x = microbe.headX;
var y = microbe.headY;
// start with the head and end with the tail
for (var n = angles.length - 1; n >= 0; n--) {
var angle = angles[n];
x -= segmentLength * Math.cos(angle);
y -= segmentLength * Math.sin(angle);
context.lineTo(x, y);
}
context.lineWidth = 10;
context.lineCap = "round";
context.lineJoin = "round";
context.strokeStyle = microbe.color;
context.stroke();
}
}
7. 实例化一个Animation对象,并得到画布上下文对象:
window.onload = function(){
var anim = new Animation("myCanvas");
var canvas = anim.getCanvas();
var context = anim.getContext();
8. 初始化1,500个微生物:
// init microbes
var microbes = [];
for (var n = 0; n < 1500; n++) {
// each microbe will be an array of angles
microbes[n] = {
headX: 0,
headY: 0,
angles: [],
color: getRandColor()
};
}
9. 设置stage()函数,该函数更新微生物,每10帧更新一次FPS的值,清除画布,然后绘制微生物和FPS的值:
var fps = 0;
anim.setStage(function(){
// update
updateMicrobes(this, microbes);
if (anim.getFrame() % 10 == 0) {
fps = anim.getFps();
}
// clear
this.clear();
// draw
drawMicrobes(this, microbes); drawFps(this, fps);
});
10. 启动动画:
anim.start();
};
</script>
</head>
11. 在HTML文档的body部分嵌入canvas标签:
<body>
<canvas id="myCanvas" width="600" height="250" style="border:1px solid black;">
</canvas>
</body>
工作原理
要绘制动画的FPS,我们可以创建drawFps()函数,该函数把FPS的值作为输入,在画布的右上角绘制一个黑色盒子,并在盒子里输出FPS的值。为了避免FPS更新太过频繁,我们可以把FPS的值保存到变量FPS中,并每10帧更新一次。用这种方法,FPS的值最多6秒钟更新一次。
要给画布增加压力,我们可以简单的初始化更多的微生物。本节,我们初始化了1,500个微生物。如果你自己试验本示例代码,你可以使用不同的值,看它如何影响FPS。
了解更多
前面已经提到,一般动画的运行速率应该在40到60 FPS之间。如果FPS降低到30,你将会注意到动画稍微有点滞后。当用Google Chrome在32位、2.2 GHzAMD处理器、2 GB内存(是的,我知道,我需要更新电脑了)的Windows 7机器上测试,观察1,500个微生物在5 FPS的情况。看上去还不是那么差,但不是非常好。当微生物数目增加到2,000或更多时,动画走走停停,让人无法接受。
我们在2D上下文中创建的几乎所有动画,在台式机和笔记本电脑上都表现良好。然而,如果你发现在计算非常复杂的情况下,2D上下文表现就不够好,你可能考虑使用WebGL来代替(我们将在第9章WebGL介绍中介绍WebGL)。与2D上下文不同,WebGL会使用硬件加速。然而,使用WebGL确实需要付出代价,因为开发和维护WebGL动画,要比创建2D动画难得多。
相关参考
- 第1章 处理文本
- 第1章 绘制弹簧
- 第6章 创建绘图应用