簡體   English   中英

在JavaScript類函數中使用setTimeout()

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

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM