область видимости / закрытие переменной javascript в цикле после тайм-аута - PullRequest
0 голосов
/ 27 января 2012

Уже поздно, и часть моего мозга, где живет Дуглас Крокфорд, закрыта.Я попробовал несколько вещей, но ничего не происходит, как ожидалось.

У меня есть холст, на котором я рисую 2 линии, затем затухаю их по таймеру, но затухает только последняя строка в цикле.Вот моя скрипка, посмотрите на строку 50ish в JS, чтобы увидеть, как она в действии, наведите курсор мыши на нижнюю правую панель:

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 ]

2 голосов
/ 27 января 2012

Проблема в том, что переменные dx, dy и т. Д., На которые вы ссылаетесь в функции, которую вы передаете setTimeout(), определены в окружающей области видимости, и к тому времени, когда любой из таймаутов фактически запускает эти переменные, все хранить значения из последней итерации цикла (ов).

Вам необходимо создать дополнительную содержащую функцию, чтобы закрывать значения из каждой итерации. Попробуйте что-то вроде следующего:

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 и т. Д. Такими, какими они были в момент выполнения каждой итерации цикла, и перемещения x, y и т. Д. Переменные из существующей функции и присвоение им параметров новой, поэтому к моменту истечения времени ожидания будут использоваться правильные значения.

1 голос
/ 27 января 2012

Вы можете попробовать что-то вроде этого:

`<script>
for(j=10;j>0;j--)
                {
                var fn = function(ind){return function()
                        {
                            console.log(ind);
                        };
                        }(j);
                    setTimeout(fn,
                    1000);
                }
</script>`
0 голосов
/ 27 января 2012

Я бы предложил вам использовать массив и хранить точки, избегая вызова 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;

    }

Я знаю, что это не совсем то, что вам нужно, но я думаю, что это можно изменить, чтобы получить его.

0 голосов
/ 27 января 2012
  1. Прежде всего j является глобальным.
  2. Во-вторых, вы никогда не закроете пути, которые начинаете , что может вызвать утечку памяти.Это кажется очень медленным, и это может быть, почему.Вам нужно вызывать closePath() всякий раз, когда вы закончите с путями, которые вы начинаете с beginPath()
  3. Далее, я думаю, что с этим все в порядке.Вы исчезаете, рисуя последнюю вещь белым.Я делал нечто похожее на это раньше, но вместо этого я очистил весь экран и продолжал рисовать вещи снова и снова.Это работало хорошо для меня.

Объяснение

Другие ответы о том, что dx и dy передаются из более высокой области, являются правильными ответами.Асинхронные функции, определенные в синхронных циклах for, будут иметь последнюю версию состояния.

for (var i = 0; i < 10; i++) setTimeout(function() { console.log(i)}, 10 )
10
10
// ...
0 голосов
/ 27 января 2012

Или другим способом (как только вы не используете IE, но позвольте ему сначала изучить canvas:))

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: нет необходимости в наборе дополнительных функций (причиныясно)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...