Анимация очереди элементов приводит к переходу к конечному элементу

#javascript #html #animation

#javascript #HTML #Анимация

Вопрос:

CodePen

Правила:

  • Каждый щелчок добавит <progress> элемент.
  • Каждый индикатор выполнения будет анимирован полностью.
  • Анимации не начнутся, пока не завершится предыдущий.

Проблема:

Если вы быстро нажмете, это оживит только последнюю progress строку. И анимации должны происходить одна за другой до завершения.

Вопрос:

Почему при быстром нажатии анимация выходит из строя?

HTML:

 <div class="container">
  <div class="add-progress-container">
    <input class="input-seconds" type="number" min="1" max="10" value="1">
    <button class="add-progress">Add progress</button>
  </div>  
  <div class="progress-container"></div>
</div>
  

JS:

 const container = document.querySelector('.progress-container');
const inputSeconds = document.querySelector('.input-seconds');
const addBtn = document.querySelector('.add-progress');
let animating = false;


function animateProgress(duration, el) {
  const intervalId = setInterval(() => {
    if(el.value >= el.max) {
      animating = false;
      window.clearInterval(intervalId);
      console.log('finished');
      checkQueue();
      return;
    }

    el.value  = el.max/duration;
  }, 1000);
} 


function getSeconds() {
  return parseInt(inputSeconds.value, 10);
}

let progressCount = 0;

function createProgress() {
  const template = `<progress data-progress="${progressCount}" value="0" max="100"></progress>`;
  container.innerHTML  = template;
  const el = document.querySelector(`[data-progress="${progressCount}"]`);
  progressCount  ;
  return el;
}

let queue = [];
function addProgress() {
  const el = createProgress()
  queue.push(el);
  checkQueue();
}

function checkQueue() {
  if(queue.length amp;amp; !animating) {
    animating = true;
    animateProgress(getSeconds(), queue.shift());
  }  
}

addBtn.addEventListener('click', addProgress);
  

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

1. Как, по-вашему, это должно быть? Анимированный один за другим независимо от того, был ли он быстро нажат?

2. Да, один за другим, даже при быстром нажатии

3. ПРИМЕЧАНИЕ: в вашем селекторе отсутствует закрывающая квадратная скобка.

4. @trincot хороший вызов, но не то, что его нарушало:(

Ответ №1:

innerHTML = Присвоение воссоздаст все предыдущие элементы внутри элемента container, что означает, что ваши ссылки на другие progress элементы больше не относятся к фактическим элементам, которые создает это присвоение.

Вы должны добавить новый элемент прогресса, подобный этому:

 function createProgress() {
  const el = document.createElement("progress");
  el.setAttribute("data-progress", progressCount);
  el.setAttribute("value", 0);
  el.setAttribute("max", 100);
  container.appendChild(el);
  return el;
}
  

Как прокомментировал Makyuu ниже, количество шагов для отдельных элементов прогресса выполняется только при запуске их анимации. Это означает, что если вы измените входное значение, оно будет применяться также к ранее созданным элементам (когда они еще не начали анимироваться).

Если предполагается использовать количество шагов, указанное в момент создания элемента, то измените две строки в своем коде:

 queue.push([getSeconds(), el]);
  

и:

 animateProgress(...queue.shift());
  

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

1. @MatthewHarwood Не уверен, в курсе вы или нет. Но после того, как вы поставите их в очередь, вы заметите, что увеличение строки прогресса было основано на текущем значении, а не на момент его ввода.

2. В случае, если такое поведение не было предусмотрено, см. Дополнение к моему ответу.