繁体   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