requestAnimationFrame выдает неверную метку времени при отображении диалогового окна подтверждения (Chromium?)

1

У меня простая анимация, которая выполняется с помощью requestAnimationFrame (для демонстрационных целей, адаптированных из примера на MDN). Если перед анимацией я показываю диалог confirm, метка времени, полученная функцией анимации, неверна. Разница между первой и второй отметками времени равна времени с момента отображения сообщения confirm до тех пор, пока не будет нажата кнопка "ОК". Это поведение (ошибка?) Отображается в Chrome и Opera (оба работают с Chromium). Firefox и Internet Explorer 11 работают как ожидалось. Проверьте скрипту или пример ниже.

const cache = {
  start: null,
  target: null
};

function animate(timestamp) {
  console.log(timestamp);
  if (cache.start === null) {
    cache.start = timestamp;
  }
  var progress = timestamp - cache.start;
  cache.target.style.left = Math.min(progress / 10, 100) + 'px';
  if (progress < 1000) {
    requestAnimationFrame(animate);
  } else {
    cache.target.style.left = 0;
    cache.start = null;
  }
}

(function() {
  const target = document.getElementsByTagName("div")[0];
  cache.target = target;
  const cb = document.getElementsByTagName("input")[0];

  const btn = document.getElementsByTagName("button")[0];
  btn.addEventListener("click", function() {
    if (cb.checked) {
      if (confirm("Just click 'OK' to start the animation, ok?")) {
        requestAnimationFrame(animate);
      }
    } else {
      requestAnimationFrame(animate);
    }
  })
})();
html,
body {
  padding: 0;
  margin: 0;
}

div {
  width: 50px;
  height: 50px;
  border: 1px solid black;
  background-color: yellowgreen;
  position: absolute;
  top: 50px;
}

button {
  margin-top: 20px;
}
<button type="button">Start</button>
<label>
  <input type="checkbox" />use "confirm"</label>
<div>

</div>

Откройте консоль, чтобы увидеть полученные метки времени. Анимация настроена на 2 секунды. При отображении диалогового окна confirm, если нажатие кнопки "ОК" происходит быстрее, чем на 2 секунды, анимация запускается для "оставшегося" времени. Если время, необходимое для нажатия кнопки "ОК", больше, чем время анимации времени, элемент не будет анимироваться, и на консоль будут отправлены 2 значения (метки времени); разница этих двух значений - это время, необходимое для нажатия кнопки "ОК".

Я предполагаю, что это ошибка в Chromium. Есть ли обходной путь для этого (все еще оживляющий с requestAnimationFrame, а не через CSS)? Я не мог найти что-либо по этому поводу в своем трекере. У кого-нибудь есть дополнительная информация об этом?

  • 0
    В целом, я бы порекомендовал вам избегать блоков confirm / alert поскольку они блокируют поток и, таким образом, приостанавливают таймеры.
  • 0
    @NicholasKyriakides я знаю. Я также помню, что они пытаются удалить их полностью. Но в этом примере это не актуально. Поток блокируется и возобновляется до запуска анимации. Странная часть - это разница между двумя последовательными временными метками кадров анимации.
Теги:
animation
chromium

1 ответ

1
Лучший ответ

Должен сказать, я нашел это очень интересным.

Проведя много времени на это, я, возможно, нашел обходное решение для вас. Вы можете видеть это здесь. https://jsfiddle.net/qtj467n0/13/

Основной его суть заключается в том, что я заменил DOMHighResTimeStamp который requestAnimationFrame предоставляет с помощью функции performance.now() которая также возвращает DOMHighResTimeStamp.

const cache = {
  start: null,
  target: null,
  time: 2000
};

function animate(timestamp) {
  console.log(timestamp);
  if (cache.start === null) {
    cache.start = timestamp;
  }
  var progress = timestamp - cache.start;
  cache.target.style.left = Math.min(progress / 10, cache.time / 10) + 'px';
  if (progress < cache.time) {
    requestAnimationFrame(animate);
  } else {
    cache.target.style.left = 0;
    cache.start = null;
  }
}

const render = () => {

  requestAnimationFrame((timestamp) => {

    const performanceNow = performance.now();

    animate(performanceNow)
  });
}

(function() {
  const target = document.getElementsByTagName("div")[0];
  cache.target = target;
  const cb = document.getElementsByTagName("input")[0];


  const btn = document.getElementsByTagName("button")[0];
  btn.addEventListener("click", function() {
    if (cb.checked) {
      const confirmed = confirm("Just click 'OK' to start the animation, ok?");
      if (confirmed) {
        render();
      }
    } else {
      requestAnimationFrame(animate);
    }
  })
})();
html,
body {
  padding: 0;
  margin: 0;
}

div {
  width: 50px;
  height: 50px;
  border: 1px solid black;
  background-color: yellowgreen;
  position: absolute;
  top: 50px;
}

button {
  margin-top: 20px;
}
<button type="button">Start</button>
<label>
  <input type="checkbox" />use "confirm"</label>
<div>

</div>
  • 0
    Красиво сделано! Интересно, что значения от performance.now() и requestAnimationFrame разные. На мой взгляд, это еще раз подтверждает, что это ошибка.
  • 0
    @AndreiV Да, я должен согласиться. Собираетесь ли вы сообщить об этом?
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню