[英]Using setTimeout() within a JavaScript class function
是否可以在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>
这是工作代码,感谢TJ 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);
}
}
}
代码的问题在于,在JavaScript中, 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个月)为JavaScript定义了一个新的Function#bind
功能。 但是在基于浏览器的实现中你不能依赖它。
这里有更多的讨论和解决方案: 你必须记住this
可能偏离主题:在您的代码中,您使用了许多命名函数表达式 。 例如:
this.animate = function animate() { ... };
在我认为IE9之前,命名函数表达式在IE上无法正常工作。 IE实际上会创建两个完全独立的函数(在两个不同的时间)。 更多信息: Double-take
更新和有点偏离主题,但由于你的所有函数都被定义为AnimateManager
构造函数中的闭包,所以没有理由让你不想公开任何公开,并且你可以完全摆脱管理问题this
。
下面是从更新的问题的“解决方案”的代码,利用你已经确定,以避免倒闭的this
定义的公共职能时不是完全等。 这也使用数组文字符号表示shapes
并使用普通for
循环(不是for..in
)来循环遍历数组(阅读本文的原因: 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。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.