[英]Using setTimeout() within a JavaScript class function
Is it possible to use setTimout() within a JavaScript object? 是否可以在JavaScript对象中使用setTimout()?
Currently the animation method call is running once, it seems that the setTimeout() isn't doing its job. 目前动画方法调用运行一次,似乎setTimeout()没有完成它的工作。 I have managed to get it working, but in a really hackish method of having a function outside of the class which uses the setTimeout.
我已经设法让它工作,但是在一个非常hackish方法中,在类之外使用setTimeout。 I'd like to make the animation loop a job for the AnimationManager class.
我想让动画循环成为AnimationManager类的工作。 If you can see any bad practice, or where i'm going wrong.. please give me a heads up!
如果你能看到任何不良做法,或者我出错了......请给我一个抬头!
JavaScript: 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 within the index page, which demonstrates how i'd like the AnimationManager to work: 索引页面中的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>
Here's the working code, thanks to TJ Crowder for fix + interesting blog post: Double-take 这是工作代码,感谢TJ Crowder修复+有趣的博客文章: Double-take
Solution: Changes in code are marked with //######### 解决方案:代码更改标有// #########
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);
}
}
}
The problem with the code is that in JavaScript, this
is set (in the normal case) by how a function is called, not where it's defined. 代码的问题在于,在JavaScript中,
this
是通过如何调用函数而不是在其定义的位置设置(在正常情况下)。 This is different than some other languages you might be used to such as Java or C#. 这与您可能习惯的其他语言(如Java或C#)不同。 So this line:
所以这一行:
setTimeout(this.animate, 40);
...will indeed call your animate
function, but with this
set to the global object ( window
, on browsers). ...确实会调用你的
animate
函数,但是this
设置为全局对象( window
,浏览器)。 So all of those properties you're accessing ( this.running
, etc.) will not be looking at your object, but rather looking for those properties on window
, which is clearly not what you want. 因此,您正在访问的所有这些属性(
this.running
等)都不会查看您的对象,而是在window
上查找这些属性,这显然不是您想要的。
Instead, you can use a closure: 相反,你可以使用一个闭包:
var me = this;
setTimeout(function() {
me.animate();
}, 40);
That works because the anonymous function we're giving to setTimeout
is a closure over the context in which it's defined, which includes the me
variable we're setting up before defining it. 这是有效的,因为我们给
setTimeout
的匿名函数是对它定义的上下文的闭包,其中包括我们在定义它之前设置的me
变量。 By calling animate
from a property on the object ( me.animate()
), we're telling JavaScript to set up this
to be the object during the call. 通过调用
animate
从对象上的属性( me.animate()
我们告诉JavaScript来建立this
是在通话过程中的对象。
Some frameworks have methods to create this closure for you (jQuery has jQuery.proxy
, Prototype has Function#bind
), and ECMAScript 5 (about 18 months old) defines a new Function#bind
feature for JavaScript that does it. 有些框架有为你创建这个闭包的方法(jQuery有
jQuery.proxy
,Prototype有Function#bind
),而ECMAScript 5(大约18个月)为JavaScript定义了一个新的Function#bind
功能。 But you can't rely on it yet in browser-based implementations. 但是在基于浏览器的实现中你不能依赖它。
More discussion and solutions here: You must remember this
这里有更多的讨论和解决方案: 你必须记住
this
Possibly off-topic: In your code, you're using a lot of named function expressions . 可能偏离主题:在您的代码中,您使用了许多命名函数表达式 。 Eg:
例如:
this.animate = function animate() { ... };
Named function expressions don't work correctly on IE prior to, I think, IE9. 在我认为IE9之前,命名函数表达式在IE上无法正常工作。 IE will actually create two completely separate functions (at two separate times).
IE实际上会创建两个完全独立的函数(在两个不同的时间)。 More here: Double-take
更多信息: Double-take
Update and a bit off-topic, but since all of your functions are defined as closures within your AnimateManager
constructor anyway, there's no reason for anything you don't want to be public to be public, and you can completely get rid of issues managing this
. 更新和有点偏离主题,但由于你的所有函数都被定义为
AnimateManager
构造函数中的闭包,所以没有理由让你不想公开任何公开,并且你可以完全摆脱管理问题this
。
Here's the "solution" code from your updated question, making use of the closures you're already defining to avoid this
entirely other than when defining the public functions. 下面是从更新的问题的“解决方案”的代码,利用你已经确定,以避免倒闭的
this
定义的公共职能时不是完全等。 This also uses array literal notation for shapes
and a normal for
loop (not for..in
) for looping through the array (read this for why: Myths and realities of for..in
): 这也使用数组文字符号表示
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);
}
}
}
Each object created via new AnimationManager
will get its own copy of the local variables within the constructor, which live on as long as any of the functions defined within the constructor is referenced anywhere. 通过
new AnimationManager
创建的每个对象将在构造函数中获得自己的局部变量副本,只要构造函数中定义的任何函数在任何地方被引用,它们就会存在。 Thus the variables are truly private, and instance-specific. 因此,变量是真正的私有,并且是特定于实例的。 FWIW.
FWIW。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.