超时后循环中的javascript变量范围/闭包

时间:2012-01-27 04:33:41

标签: javascript html5 canvas scope

现在已经很晚了,道格拉斯·克罗克福德生活的大脑部分已经关闭了。我尝试了一些事情,但没有按预期做的事情。

我有一个画布,我绘制了2行,然后在计时器上将它们淡出,但只有循环中的最后一行被淡出。这是我的小提琴,在JS中向下看50行,看看它的实际效果在右下方的窗格中拖动你的鼠标:

http://jsfiddle.net/mRsvc/4/

这是函数,基本上超时只得到循环中的最后一个值,我以前见过这个,我敢肯定,如果我不是那么神志不清,它可能会更简单。这是特别的功能:

function update()
        {
            var i;
            this.context.lineWidth = BRUSH_SIZE;            
            this.context.strokeStyle = "rgba(" + COLOR[0] + ", " + COLOR[1] + ", " + COLOR[2] + ", " +  BRUSH_PRESSURE + ")";
            for (i = 0; i < scope.painters.length; i++)
            {
                scope.context.beginPath();
                var dx = scope.painters[i].dx;
                var dy = scope.painters[i].dy;
                scope.context.moveTo(dx, dy);   
                var dx1 = scope.painters[i].ax = (scope.painters[i].ax + (scope.painters[i].dx - scope.mouseX) * scope.painters[i].div) * scope.painters[i].ease;
                scope.painters[i].dx -= dx1;
                var dx2 = scope.painters[i].dx;
                var dy1 = scope.painters[i].ay = (scope.painters[i].ay + (scope.painters[i].dy - scope.mouseY) * scope.painters[i].div) * scope.painters[i].ease;
                scope.painters[i].dy -= dy1;
                var dy2 = scope.painters[i].dy;
                scope.context.lineTo(dx2, dy2);
                scope.context.stroke();
                for(j=FADESTEPS;j>0;j--)
                {
                    setTimeout(function()
                        {
                            var x=dx,y=dy,x2=dx2,y2=dy2;
                            scope.context.beginPath();
                            scope.context.lineWidth=BRUSH_SIZE+1;
                            scope.context.moveTo(x, y);
                            scope.context.strokeStyle = "rgba(" + 255 + ", " + 255 + ", " + 255 + ", " + .3 + ")";
                            scope.context.lineTo(x2, y2);
                            scope.context.stroke();
                            scope.context.lineWidth=BRUSH_SIZE;
                        },
                    DURATION/j);
                }
            }
        }

5 个答案:

答案 0 :(得分:2)

问题是您在传递给dx的函数中引用的变量dysetTimeout()等是在周围范围内定义的,并且是在任何时候定义的超时实际上运行这些变量都保存了循环的最后一次迭代的值。

您需要创建一个额外的包含函数来关闭每次迭代的值。尝试以下内容:

for(j=FADESTEPS;j>0;j--) {
   (function(x,y,x2,y2) {
      setTimeout(function() {
         scope.context.beginPath();
         scope.context.lineWidth=BRUSH_SIZE+1;
         scope.context.moveTo(x, y);
         scope.context.strokeStyle = "rgba(" + 255 + ", " + 255 + ", " + 255 + ", " + .3 + ")";
         scope.context.lineTo(x2, y2);
         scope.context.stroke();
         scope.context.lineWidth=BRUSH_SIZE;
      },
      DURATION/j);
   })(dx, dy, dx2, dy2);
}

这将为j=FADESTEPS循环的每次迭代创建一个新的匿名函数,立即执行它并传递dx等值,就像循环的每次迭代运行时一样,并且将xy等变量移出现有函数并将其作为新变量的参数,然后在超时运行时将使用正确的值。

答案 1 :(得分:1)

您可以尝试这样的事情:

`<script>
for(j=10;j>0;j--)
                {
                var fn = function(ind){return function()
                        {
                            console.log(ind);
                        };
                        }(j);
                    setTimeout(fn,
                    1000);
                }
</script>`

答案 2 :(得分:0)

或者另一种方式(一旦你不使用IE,但首先让它学习画布:))

for(j=FADESTEPS;j>0;j--)
{
   setTimeout(function(x,y,x2,y2)
     {
        scope.context.beginPath();
        scope.context.lineWidth=BRUSH_SIZE+1;
        scope.context.moveTo(x, y);
        scope.context.strokeStyle = "rgba(" + 255 + ", " + 255 + ", " + 255 + ", " + .3 + ")";
        scope.context.lineTo(x2, y2);
        scope.context.stroke();
        scope.context.lineWidth=BRUSH_SIZE;
     },
     DURATION/j,dx,dy,dx2,dy2);
}

ps:没有必要设置额外的功能(原因很清楚)

答案 3 :(得分:0)

  1. 首先j是全球性的。
  2. 其次,你永远不会关闭你开始的路径,这会导致内存泄漏。这似乎很慢,这可能是原因。完成closePath()
  3. 开头的路径后,您需要致电beginPath()
  4. 接下来,我认为这种方法有一些普遍的乐趣。用白色画出最后一件东西,你渐渐淡出了。我之前做过类似的事情,但我清除了整个屏幕并不断地画出一些东西。它对我有用。
  5. <强>解释

    关于dxdy从更高范围传递的其他答案是正确的答案。同步for循环中定义的异步函数将采用该状态的最后一个版本。

    for (var i = 0; i < 10; i++) setTimeout(function() { console.log(i)}, 10 )
    10
    10
    // ...
    

答案 4 :(得分:0)

我建议你使用一个数组并在循环中存储避免setTimeOut调用的点。有点像这样。

    this.interval = setInterval(update, REFRESH_RATE);

    var _points = [];

    function update() {
        var i;
        this.context.lineWidth = BRUSH_SIZE;
        this.context.strokeStyle = "rgba(" + COLOR[0] + ", " + COLOR[1] + ", " + COLOR[2] + ", " + BRUSH_PRESSURE + ")";
        for (i = 0; i < scope.painters.length; i++) {
            scope.context.beginPath();
            var dx = scope.painters[i].dx;
            var dy = scope.painters[i].dy;
            scope.context.moveTo(dx, dy);
            var dx1 = scope.painters[i].ax = (scope.painters[i].ax + (scope.painters[i].dx - scope.mouseX) * scope.painters[i].div) * scope.painters[i].ease;
            scope.painters[i].dx -= dx1;
            var dx2 = scope.painters[i].dx;
            var dy1 = scope.painters[i].ay = (scope.painters[i].ay + (scope.painters[i].dy - scope.mouseY) * scope.painters[i].div) * scope.painters[i].ease;
            scope.painters[i].dy -= dy1;
            var dy2 = scope.painters[i].dy;
            scope.context.lineTo(dx2, dy2);
            scope.context.stroke();
            _points.push([dx, dy, dx2, dy2]);

            clear();
        }
    }

    function clear(){

        if(_points.length < FADESTEPS){
            return;
        }

        var p = _points.shift();
                    if(!p){
                        return;
                    }
                    var x = p[0],
                        y = p[1],
                        x2 = p[2],
                        y2 = p[3];
                    scope.context.beginPath();
                    scope.context.lineWidth = BRUSH_SIZE + 1;
                    scope.context.moveTo(x, y);
                    scope.context.strokeStyle = "rgba(" + 255 + ", " + 255 + ", " + 255 + ", " + .3 + ")";
                    scope.context.lineTo(x2, y2);
                    scope.context.stroke();
                    scope.context.lineWidth = BRUSH_SIZE;

    }

我知道这不是你所需要的,但我认为可以修改它以获得它。