Что произойдет, если мы будем манипулировать DOM в requestAnimationFrame?

0

Вопрос

Я понимаю, что всякий раз, когда происходит какая-либо манипуляция DOM, например, вставка элемента DOM, вызовет перерисовку и, скорее всего, последует перекраска. Пожалуйста, поправьте меня, если я ошибаюсь. Цитирование веб-документов MDN,

Метод window.requestAnimationFrame() сообщает браузеру, что вы хотите выполнить анимацию, и запрашивает, чтобы браузер вызывал указанную функцию для обновления анимации перед следующей перерисовкой

обратный вызов requestAnimationFrame (он же aAF) вызывается непосредственно перед тем, как браузер собирается перекрасить. Так значит ли это, что если нам каким-то образом удастся выполнить манипуляцию DOM внутри этого rAF (редактировать: а также поставить в очередь другой rAF в конце), который каждый раз запускает перерисовку и, следовательно, перерисовку, мы застрянем в бесконечном цикле, фактически ничего не отображая на экране.

Или это тот случай, когда, как только браузер решил сделать перекраску, он будет придерживаться ее и применять любые обновления, которые произошли при обратном вызове RAF, при следующей перекраске?

dom javascript reflow repaint
2021-11-21 07:17:28
1

Лучший ответ

1

всякий раз, когда происходит какая-либо манипуляция DOM, такая как вставка элемента DOM, вызовет перерисовку и, скорее всего, за ней последует перекраска

Действие рисования происходит асинхронно, поэтому "триггер" следует понимать именно так. Сначала ваш код JavaScript завершится до того, как это произойдет на самом деле.

если нам каким-то образом удастся выполнить манипуляцию DOM внутри этого rAF (редактировать: а также поставить в очередь другой rAF в конце), который каждый раз запускает перерисовку и, следовательно, перерисовку, мы застрянем в бесконечном цикле, фактически ничего не отображая на экране.

Потребности в перекраске накапливаются и не удовлетворяются синхронно. Сначала ваш код должен быть завершен до тех пор, пока стек вызовов не опустеет. Таким образом, здесь нет бесконечного цикла.

Или это тот случай, когда, как только браузер решил сделать перекраску, он будет придерживаться ее и применять любые обновления, которые произошли при обратном вызове RAF, при следующей перекраске?

ДА. Когда вызывается обратный вызов RAF, этот код получает последний шанс внести обновления в DOM, что может привести к дальнейшему накоплению потребностей в покраске. Если в этом обратном вызове вы также зарегистрируете другой обратный вызов на RAF, он будет выполнен не в это время, а позже: в следующий раз, когда браузер подготовит свою задачу перерисовки-так что не текущую.

Упрощенный пример

Допустим, у вас есть этот код:

requestAnimationFrame(update);

myElement.style.backgroundColor = "silver"; // This queues a need for repaint

function update() {
    // This queues a need for repaint
    myElement.style.width = Math.floor(Math.random() * 100) + "px";
    requestAnimationFrame(update);
}

Когда это выполняется, мы получаем следующую последовательность:

  1. update зарегистрирован как обратный вызов
  2. Изменение фона указывает на необходимость перекраски
  3. Стек вызовов становится пустым
  4. Браузер начинает свою работу по перекраске, но учитывает, что есть зарегистрированный обратный вызов. Таким образом, он удаляет эту регистрацию (потому что она должна выполняться только один раз) и выполняет update прежде чем делать что-либо еще.
  5. Изменение ширины указывает на необходимость перекраски. Список изменений теперь включает изменение фона и это изменение ширины, а также любой рассчитанный каскадный эффект. (То, как это представлено, зависит от браузера)
  6. То update функция снова регистрируется как обратный вызов.
  7. Теперь браузер проверяет, что ему нужно сделать в рамках этой работы по перекраске, и выполняет все необходимое для визуализации эффектов изменения фона и ширины.
  8. Работа с краской заканчивается. Все, что осталось, - это зарегистрированный update обратный звонок.
  9. Когда браузер выполнит свой следующий цикл рисования, мы начнем снова с шага 4, но теперь в очереди больше нет изменения фона. В остальном это будет тот же самый процесс.
2021-11-21 12:57:10

"4. Браузер начинает свою работу по верстке/перерисовке", это довольно запутанная формулировка, я думаю, что сказать "браузер начинает обновлять рендеринг" было бы немного менее запутанным. Макет и перекраска разделены, вы можете очень хорошо заставить макет синхронно с кодом пользователя, вы не можете принудительно перекрасить, что всегда будет последним шагом на этапах рендеринга. Кроме того, я чувствую, что ответы на первые пункты были бы намного проще, если бы с самого начала напомнили, что raf(()=>raf(fn2)) будет планировать fn2 чтобы выстрелить в следующий кадр. В противном случае этот ответ верен.
Kaiido

@Kaiido, спасибо за ваш комментарий. "вы очень хорошо можете заставить макет синхронно с кодом пользователя": вы имеете в виду воспринимаемое пользователем изменение в макете? можете ли вы привести пример кода этого?
trincot

В любом случае, я удалил ссылку на макет.
trincot

gist.github.com/paulirish/5d52fb081b3570c81e3a Вот список того, что запускает макет, и да, он "воспринимается пользователем": stackoverflow.com/questions/55134528/...
Kaiido

Хорошо, Кайидо!
trincot

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................