是否可以在JavaScript对象中使用setTimout()?
目前动画方法调用正在运行一次,似乎setTimeout()没有完成其工作。我已经设法让它工作,但是在一个非常hackish方法中,在类之外使用setTimeout。我想让动画循环成为AnimationManager类的工作。如果你能看到任何不良做法,或者我出错了......请给我一个抬头!
JavaScript的:
var AnimationManager = function(canvas)
{
this.canvas = canvas;
this.canvasWidth = canvas.width();
this.canvasHeight = canvas.height();
this.ctx = canvas.get(0).getContext('2d');
this.running = true;
this.start = function start(){
this.running = true;
this.animate();
}
/** Allow the animations to run */
this.run = function run(){
this.running = false;
}
/** Stop the animations from running */
this.stop = function stop(){
this.running = false;
}
this.animate = function animate()
{
if(this.running)
{
this.update();
this.clear();
this.draw();
}
setTimeout(this.animate, 40); //25 fps
}
/** Update all of the animations */
this.update = function update()
{
for(var i in shapes)
{
shapes[i].moveRight();
}
}
/** Clear the canvas */
this.clear = function clear()
{
this.ctx.clearRect(0,0, this.canvasWidth, this.canvasHeight);
}
/** Draw all of the updated elements */
this.draw = function draw()
{
for(var i in shapes)
{
this.ctx.fillRect(shapes[i].x, shapes[i].y, shapes[i].w, shapes[i].h);
}
}
}
索引页面中的JavaScript,演示了我希望AnimationManager如何工作:
<script type="text/javascript">
$(document).ready(function() {
var canvas = $('#myCanvas');
var am = new AnimationManager(canvas);
am.start();
//If true play the animation
var startButton = $("#startAnimation");
var stopButton = $("#stopAnimation");
stopButton.hide();
//Toggle between playing the animation / pausing the animation
startButton.click(function()
{
$(this).hide();
stopButton.show();
am.run();
});
stopButton.click(function()
{
$(this).hide();
startButton.show();
am.stop();
});
});
</script>
这是工作代码,感谢T.J. Crowder修复+有趣的博文:Double-take
解决方案:代码更改标有// #########
var shapes = new Array();
shapes.push(new Shape(0,0,50,50,10));
shapes.push(new Shape(0,100,100,50,10));
shapes.push(new Shape(0,200,100,100,10));
/**
* AnimationManager class
* animate() runs the animation cycle
*/
var AnimationManager = function(canvas)
{
this.canvas = canvas;
this.canvasWidth = canvas.width();
this.canvasHeight = canvas.height();
this.ctx = canvas.get(0).getContext('2d');
this.running = true;
var me = this; //#################################Added this in
this.start = function(){
this.running = true;
this.animate();
}
/** Allow the animations to run */
this.run = function(){
this.running = true;
}
/** Stop the animations from running */
this.stop = function(){
this.running = false;
}
this.animate = function()
{
if(this.running)
{
this.update();
this.clear();
this.draw();
}
//###################### Now using me.animate()
setTimeout(function(){
me.animate();
}, 40); //25 fps
}
/** Update all of the animations */
this.update = function()
{
for(var i in shapes)
{
shapes[i].moveRight();
}
}
/** Clear the canvas */
this.clear = function()
{
this.ctx.clearRect(0,0, this.canvasWidth, this.canvasHeight);
}
/** Draw all of the updated elements */
this.draw = function()
{
for(var i in shapes)
{
this.ctx.fillRect(shapes[i].x, shapes[i].y, shapes[i].w, shapes[i].h);
}
}
}
答案 0 :(得分:15)
代码的问题在于,在this
中,通过如何调用函数而不是在其定义的位置设置{在正常情况下)。这与您可能习惯的其他语言(如Java或C#)不同。所以这一行:
setTimeout(this.animate, 40);
...确实会调用您的animate
函数,但this
设置为全局对象(window
,在浏览器上)。因此,您访问的所有这些属性(this.running
等)都不会查看您的对象,而是在window
上查找这些属性,这显然不是您想要的。
相反,您可以使用闭包:
var me = this;
setTimeout(function() {
me.animate();
}, 40);
这是有效的,因为我们给setTimeout
的匿名函数是对其定义的上下文的闭包,其中包括我们在定义它之前设置的me
变量。通过从对象(animate
)上的属性调用me.animate()
,我们告诉JavaScript在调用期间将this
设置为对象。
一些框架有方法为你创建这个闭包(jQuery有jQuery.proxy
,Prototype有Function#bind
),ECMAScript 5(大约18个月)定义了一个新的Function#bind
功能执行它的JavaScript。但是在基于浏览器的实现中你不能依赖它。
此处有更多讨论和解决方案:You must remember this
可能偏离主题:在您的代码中,您使用了很多命名的函数表达式。 E.g:
this.animate = function animate() { ... };
在我认为IE9之前,命名函数表达式在IE上无法正常工作。 IE实际上会创建两个完全独立的函数(在两个不同的时间)。更多信息:Double-take
更新并且有点偏离主题,但是因为你的所有函数都被定义为AnimateManager
构造函数中的闭包,所以你没有理由不想做任何事情。公众公开,你可以完全摆脱管理this
的问题。
这是来自您更新的问题的“解决方案”代码,利用您已经定义的闭包来避免this
完全不同于定义公共函数时。这也使用shapes
的数组文字表示法和正常for
循环(不是for..in
)来循环遍历数组(读取原因为:Myths and realities of for..in
):
var shapes = [
new Shape(0,0,50,50,10)),
new Shape(0,100,100,50,10)),
new Shape(0,200,100,100,10))
];
/**
* AnimationManager class
* animate() runs the animation cycle
*/
var AnimationManager = function(canvas)
{
var canvasWidth = canvas.width(),
canvasHeight = canvas.height(),
ctx = canvas.get(0).getContext('2d'),
running = true, // Really true? Not false?
me = this;
// Set up our public functions
this.start = AnimationManager_start;
this.run = AnimationManager_run;
this.stop = AnimationManager_stop;
/** Start the animations **/
function AnimationManager_start(){
running = true;
animate();
}
/** Allow the animations to run */
function AnimationManager_run(){
running = true;
}
/** Stop the animations from running */
function AnimationManager_stop(){
running = false;
}
/** Internal implementation **/
function animate()
{
if (running)
{
update();
clear();
draw();
}
setTimeout(animate, 40); //25fps
}
/** Update all of the animations */
function update()
{
var i;
for (i = 0; i < shapes.length; ++i) // not for..in
{
shapes[i].moveRight();
}
}
/** Clear the canvas */
function clear()
{
ctx.clearRect(0,0, canvasWidth, canvasHeight);
}
/** Draw all of the updated elements */
function draw()
{
var i;
for (i = 0; i < shapes.length; ++i) // not for..in
{
ctx.fillRect(shapes[i].x, shapes[i].y, shapes[i].w, shapes[i].h);
}
}
}
通过new AnimationManager
创建的每个对象都将获得构造函数中局部变量的自己的副本,只要构造函数中定义的任何函数在任何地方被引用,它们就会存在。因此,变量是真正的私有,并且是特定于实例的。 FWIW。