Я занимался веб-программированием, когда столкнулся с этой проблемой. Проблема в том, что когда я пытаюсь многократно увеличивать радиус круга в цикле for, он не показывает результат изменения после каждой итерации, хотя отображает результат на последней итерации, т.е. когда я достиг 40, круг с отображается радиус 40. Так в чем тут проблема?
<!DOCTYPE html>
<html>
<head>
<script type = "text/javascript">
function strokeincr(){
var circl = document.getElementById("circ");
var i;
for(i=10;i<=40;i++)
{
alert("for i = "+i);//this was just to see the value of i changing.
circl.setAttribute("r",i);
}
}
</script>
<style>
#div1 svg{
width:200px;
height: 200px;
background-color:pink;
}
</style>
</head>
<body>
<div id = "div1">
<svg>
<circle id = "circ" cx = "100" cy = "100" r = "10" fill = "green"></circle>
</svg>
</div>
<button onclick = "strokeincr()"> click me</button>
</body>
</html>
.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Во-первых, предупреждение останавливает процессы, но, что более важно, for loop сообщает программе, что нужно запустить код, заключенный в фигурные скобки, и она сделает это как можно быстрее и эффективнее. Как отметил @YonaAppletree, хотя и не написано в официальной спецификации, большинство всех браузеров (если не все) реализуют одни и те же или тривиально разные правила обработки и не позволяют перерисовывать представление во время цикла for. Это оптимизация для экономии ресурсов, и на самом деле она не имеет большого значения с точки зрения конечного пользователя, поскольку, даже если бы он этого не делал, код чаще всего обрабатывался бы быстрее, чем мог бы различить человеческий глаз. Это означает, что это хорошая оптимизация, но это также означает, что ваш текущий код не будет работать так, как вы ожидаете.
Что мы можем сделать, так это установить интервальный таймер, который сбрасывается после того, как i станет равным 40 или больше. Интервал принимает два параметра
setInterval( function , time in ms )
ms - миллисекунды. 1000 мс равно 1 секунде. Предоставленная функция будет повторяться снова и снова с указанным интервалом, пока интервал не будет очищен. Например:
setInterval( function() { console.info("hi!") }, 1000)
напечатает "привет!" на консоль каждые 1000 мс, что эквивалентно 1 секунде. Это никогда не прекратится, потому что никогда не исчезнет.
Назначая эту функцию переменной, мы гарантируем, что сможем очистить ее (остановить функцию), когда i достигнет 40 или больше.
Вот как вы назначаете интервал переменной:
var myIntervalTimer = setInterval(function, time in ms)
чтобы очистить этот интервал, вы вызываете clearInterval( name of interval ). Например:
clearInterval(myIntervalTimer)
Затем мы просто вставляем его в ваш уже установленный код, и вуаля!
var i = 10;
var myIntervalTimer = setInterval(function() {
console.info("i is" + i);
circl.setAttribute("r",i);
if (i >= 40) clearInterval(myIntervalTimer);
i++;
}, 250);
Теперь у нас есть таймер, который будет увеличивать радиус круга каждую четверть секунды (250 мс) и очищать интервальный таймер, если i когда-либо будет равно 40 или больше.
<!DOCTYPE html>
<html>
<head>
<script type = "text/javascript">
function strokeincr(){
var circl = document.getElementById("circ");
var i = 10;
var myIntervalTimer = setInterval(function() {
circl.setAttribute("r",i);
if (i >= 40) clearInterval(myIntervalTimer);
i++;
}, 250);
}
</script>
<style>
#div1 svg{
width:200px;
height: 200px;
background-color:pink;
}
</style>
</head>
<body>
<div id = "div1">
<svg>
<circle id = "circ" cx = "100" cy = "100" r = "10" fill = "green"></circle>
</svg>
</div>
<button onclick = "strokeincr()"> click me</button>
</body>
</html>@YonaAppletree Вы правы насчет перерисовки, и я соответствующим образом обновлю свое объяснение скорости. Я никогда не пытался подразумевать, что код в интервале работал «медленнее, чем мог», просто вышеупомянутая функция выполнялась с заданной задержкой.
При нажатии кнопки выполняется весь цикл. Если вы хотите увеличивать радиус круга каждый раз, когда вы нажимаете кнопку, вы должны использовать другой подход.
function strokeincr() {
var circl = document.getElementById("circ");
var r = parseInt(circl.getAttribute("r"));
circl.setAttribute("r", r + 10);
}
Это очень интересный вопрос. Я постараюсь ответить на него как можно лучше.
Вы столкнулись с неожиданным поведением, которое существует из-за оптимизации браузера. Это связано с перекрашиванием. Вы меняете радиус круга на каждом шаге цикла. Если бы браузеру приходилось перерисовывать экран на каждой итерации, это было бы очень сложной операцией.
Так что же сделали производители браузеров? Они (по-видимому) отложили перерисовку до завершения цикла. Имеет смысл, правда?
Это внезапно стало видимым, когда вы начали использовать предупреждение для приостановки цикла (обратите внимание, как значение в DOM изменяется на каждой итерации, но экран не перерисовывается).
Если вы хотите оживить это изменение, есть несколько подходов. Вы можете использовать переходы css или использовать JavaScripts window.requestAnimationFrame(). Но это выходит за рамки вашего вопроса;).
PS: Этот ответ основан на моих собственных наблюдениях и некоторых статьях, которые я прочитал некоторое время назад, поэтому прокомментируйте, если вы знаете официальную документацию об этом поведении перерисовки (мне самому это любопытно).
Проблема здесь связана с тем, как браузеры обновляют экран и как работают события. В браузерах есть так называемый «цикл событий». Браузер ждет, когда произойдет какое-то событие, например, когда пользователь нажмет вашу кнопку, а затем выполняет связанный код. Вызовы, которые изменяют документ, например ваш вызов setAttribute, не вызывают обновления экрана напрямую. Скорее, они обновляют DOM (объектную модель документа), которая определяет, что браузер должен отображать на экране.
Экран обновляется только после завершения всего кода обработки событий. Итак, вы выполняете цикл от 10 до 40, обновляя радиус круга. Но браузер проверяет значение только после завершения цикла. Таким образом, вы видите только окончательное значение.
Как упоминалось в ответе @zfrisch, один из способов решить эту проблему - использовать функции планирования событий браузера. Эти функции позволяют вам попросить браузер запустить событие (и обработать его с помощью кода по вашему выбору) в более позднее время.
Вам доступны три основные функции планирования: setTimeout(callback, delayInMs), setInterval(callback, intervalInMs) и requestAnimationFrame(callback). Вы можете поискать их в документации для дальнейшего объяснения, но в целом для анимации вы должны использовать requestAnimationFrame.
requestAnimationFrame в основном просит браузер выполнить некоторый код позже, после того, как он все отрендерил и будет готов отрендерить что-то новое.
При использовании requestAnimationFrame ваш пример может выглядеть примерно так:
<!DOCTYPE html>
<html>
<head>
<script type = "text/javascript">
function strokeincr() {
// Note the use of const instead of var. It indicates a variable that we won't change, and ensures that we don't change it.
const circl = document.getElementById("circ");
// We need to know how long our animation should last, so we can compute where we are.
// Note that since the varaible contains milliseconds, we label it as such for clarity.
const animationDurationMs = 1000;
// The starting time of the animation (in milliseconds since the epoch), so we can compute where we are in the animation later.
const startTimeMs = Date.now();
// Define the starting and ending values for the animation for later computation
const startRadius = 10;
const endRadius = 40;
function incrementAnimation() {
// Get the now-current time so we can compute the animation progress
const currentTimeMs = Date.now();
// Compute where we are in the animation as a fraction, ensuring we don't go over 1, using Math.min
const animationFraction = Math.min(
1,
(currentTimeMs - startTimeMs) / animationDurationMs
);
// Compute what the current radius should be, given where we are in the animation, using liner interpolation
const currentRadius = startRadius + (endRadius - startRadius) * animationFraction;
// Update the circle with the new radius
circl.setAttribute("r", currentRadius);
// Check if there is more animating to do, and if so, request that this function is run again after the screen is updated.
if (animationFraction < 1)
requestAnimationFrame(incrementAnimation);
}
// Start the animation by calling the animation function directly.
incrementAnimation();
}
</script>
<style>
#div1 svg {
width: 200px;
height: 200px;
background-color: pink;
}
</style>
</head>
<body>
<div id = "div1">
<svg>
<circle id = "circ" cx = "100" cy = "100" r = "10" fill = "green"></circle>
</svg>
</div>
<button onclick = "strokeincr()">click me</button>
</body>
</html>
Решение правильное, но я думаю, что оно в некоторой степени упускает из виду, что Зачем является правильным. Круг не перерисовывается «Даже быстрее, тогда ты сможешь увидеть». И программа в решении не работает «медленнее, чем может». Фактически, круг обновляется только один раз в исходном коде после завершения обработки цикла for. Браузеры основаны на событиях. Происходит какое-то событие, например, щелчок или заданное по времени событие. Затем он запускает связанный код обработчика. Затем он пересчитывает вид и обновляет экран. Затем он ждет новых событий.