Я хочу рисовать на холсте с определенным интервалом, а setTimeout не работает

#javascript #html #settimeout

#javascript #HTML #settimeout

Вопрос:

Вот часть кода, которая имеет значение:

 function drawCircle(i, color1, color2) {
            var ctx = canvas.getContext("2d");
            if (i % 2 == 1) {
                ctx.fillStyle = color1;
            }
            else {
                ctx.fillStyle = color2;
            }
            ctx.beginPath();
            ctx.arc(110, 270, 10, 0, Math.PI * 2, true);
            ctx.closePath();
            ctx.fill();
        }
for (var i = 0; i < 10; i  ) {
    setTimeout(drawCircle(i, color2, color5), 4000);
}
  

Обратите внимание, что это всего лишь фрагмент, который я хотел опробовать, прежде чем использовать подобный код в более крупном проекте. Это работает неправильно, изображение рисуется только один раз, то есть я вижу только последний нарисованный круг. Я загуглил это до смерти, но пока мне ничего не помогло.

Ответ №1:

То, что вы хотите использовать, это setInterval . setTimeout просто запускает событие один раз. У них одинаковый список аргументов.

Также я не думаю, что способ, который вы используете setTimeout , правильный. То, как вы написали это сейчас, функция запускается перед фактической передачей чего-либо в setTimeout / setInterval . Итак, вы должны написать:

 setInterval(function() {              // GOOD. An actual function is passed 
    drawCircle(...);                  // as the first argument.
}, 4000);
  

или:

 setInterval('drawCircle(...)', 4000); // GOOD. the JS statement is supplied as 
                                      // a string and later on will be evaluated 
                                      // and executed with eval().
  

и НЕ:

 setInterval(drawCircle(...), 4000);   // BAD! drawCircle() is fired, its result 
                                      // evaluated and passed on to setInterval 
                                      // that doesn't know what to do with it.
  

Редактировать

В JavaScript нет никаких процедур блокировки. Это однопоточный язык, управляемый событиями. Если вы вызовете что-то вроде setInterval , это немедленно завершится успешно. Только через 4 секунды или около того будет вызвана ваша функция обратного вызова. Однако тем временем JS будет занят выполнением разного рода вещей — например, реагированием на другие события, сгенерированные пользователем. Что вы хотите сделать, так это вызвать setTimeout один раз, а затем внутри функции обратного вызова, непосредственно перед возвратом вызвать ее снова с той же функцией и другим набором аргументов i . Что-то в этом роде:

 function drawCircle(i, ...) {

    // ... do stuff ...

    if (i < 10) {                    // Check if the callback has been invoked 10 
                                     // times already.
        setTimeout(function() {      // Schedule the callback for execution
            drawCircle(i   1, ...);  // again after anoter 4 seconds.
        }, 4000);
    }
}

setTimeout(function() {              // Invoke the callback the first time 
    drawCircle(1, ...);              // after first 4 seconds.
}, 4000);
  

Комментарии:

1. Спасибо за подробный ответ. Это работает лучше, сайт ждет четыре секунды, чем рисует круг. Но то, что происходит после этого, странно. Функция drawcircle вызывается десять раз подряд с тем же значением для i, затем ожидает четыре секунды и повторяет процесс. Есть идеи относительно того, почему это происходит? Я впервые использую циклы в javascript наряду с canvas, поэтому точно определить ошибку довольно сложно.

2. Чтобы прояснить, что я хочу сделать, это: для каждого запуска цикла for вызывайте функцию drawcircle(…) и подождите четыре секунды. Таким образом, функция должна быть вызвана 10 раз, с i в диапазоне от 0 до 9, и процесс должен занять 40 секунд.

3. Я опубликую свой ответ в РЕДАКТИРОВАНИИ.

4. Это сработало как по волшебству 🙂 Это мой первый вопрос здесь, и я действительно удивлен оперативностью и качеством ответов. Спасибо, что, без сомнения, сделали меня постоянным посетителем здесь 🙂 И, конечно, спасибо другим, кто ответил.

5. Да, я заметил, но я не создавал учетную запись, поэтому я не мог проголосовать. Я сделал это сейчас, и я дам ответ с повышением, когда получу необходимую «репутацию». Я тоже поставил галочку. Спасибо за ссылку для получения дополнительной информации.

Ответ №2:

На самом деле есть пара вещей, которые приводят к неправильному поведению вашего кода, и все они связаны с тем, как написан ваш цикл. Первая проблема заключается в вызове setTimeout, главным образом в том, что вы не передаете ему объект function в качестве первого аргумента, и большинство людей исправили бы это, выполнив что-то вроде этого:

 for (var i = 0; i < 10; i  ) {
    setTimeout(function() {
        drawCircle(i, color2, color5);
    }, 4000);
}
  

Это приведет к задержке setTimeout, как вы и ожидали, но в конечном итоге вызов drawCircle с 10 в качестве первого аргумента 10 раз, и это потому, что каждый вызов использует одну и ту же ссылку на переменную «i». Значение для i не извлекается до тех пор, пока не будет выполнен вызов … примерно через 4 секунды после завершения цикла, когда i уже 10.

Чтобы обойти это, вам нужна еще одна функция:

 function getDrawer(i, color2, color5) {
    return function() {
        drawCircle(i, color2, color5);
    }
}

for (var i = 0; i < 10; i  ) {
    setTimeout(getDrawer(i, color2, color5), 4000);
}
  

Функция getDrawer (ужасное название, пожалуйста, не используйте его) вызывается немедленно, поэтому значение i немедленно доступно и запоминается анонимной функцией, которую возвращает getDrawer. Эта анонимная функция будет вызвана setTimeout.

Ответ №3:

Проблема здесь в том, что вы вызываете setTimeout несколько раз подряд, вместо того, чтобы вызывать его снова из обратного вызова. Попробуйте переместить вызов setTimeout в конец drawCircle функции, чтобы увидеть анимацию.