[英]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.