Могу ли я ограничить воспроизведение анимации CSS только один раз при переключении класса на один и тот же элемент, определяющий другую анимацию?

По сути, я пытаюсь показать анимацию на некоторых элементах, которые были затронуты в "самом последнем действии". После завершения этой анимации она не должна воспроизводиться снова. Проблема, с которой я столкнулся на данный момент, заключается в том, что я также определяю анимацию, которая запускается, когда элемент (ы) "выделен". Когда элемент отменен, оригинальная анимация воспроизводится снова, даже если animation-iteration-count из 1 был достигнут. Я мог бы, конечно, настроить прослушиватель событий, когда animationend запускает, а затем удаляет класс, связанный с анимацией. Я больше пытаюсь рассуждать о том, почему это не работает, как задумано. Я думаю, что это своего рода противоположность этого вопроса.

document.querySelector('.one-time-animation').addEventListener("click", function(e) {
 e.target.classList.toggle('selected');
})
.one-time-animation {
    animation: one-time-animation 2s forwards 1;
}

.selected {
    animation: selected 2s forwards 1;
}

@keyframes one-time-animation {
    from {
        background:red;
    }
    to {
        background:transparent;
    }
}

@keyframes selected {
    from {
        box-shadow: 0 0 0 green inset;
    }
    to {
        box-shadow: 0 0 10px green inset;
    }
}
<div class="one-time-animation">Click to toggle selected class</div>

2 ответа

Решение

Чистое решение CSS: (без загрязнения selected стиль)

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

document.querySelector('.one-time-animation').addEventListener("click", function(e) {
  e.target.classList.toggle('selected');
})
.one-time-animation {
  animation: one-time-animation 2s forwards 1;
}
@keyframes one-time-animation {
  from {
    background: red;
  }
  to {
    background: transparent;
  }
}

/* do the following changes */

.selected {
  box-shadow: 0 0 10px green inset;
  transition: box-shadow 2s ease;
}

/* the below is generally not required but if the box-shadow just appears instantly instead of gradually then uncomment it
div {
  box-shadow: 0 0 0 green inset;
}
*/
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="one-time-animation">Click to toggle selected class</div>


Причина:

Анимации работают так, как они должны быть основаны на спецификациях. При нажатии, когда selected класс добавлен к элементу, он больше не имеет one-time-animation, Это потому, что последнее указанное значение для animation свойство переопределяет предыдущий указанный (как правильно указал Шеннан).

Когда selected класс отключен, one-time-animation становится применимым, и к настоящему времени пользовательский агент не помнит, что эта анимация ранее присутствовала в элементе. Это приводит к повторному выполнению анимации, даже если animation-iteration-count установлено на 1.

Один из способов решения этой проблемы - сообщить агенту пользователя, что оригинальная анимация никогда не удаляется из элемента. Это можно сделать, сохранив его как часть стека в selected учебный класс. Свойство Animation может поддерживать несколько анимаций в формате через запятую. Исходная анимация больше не запускается, потому что пользовательский агент теперь обрабатывает ее как всегда и, таким образом, поддерживает счетчик итераций.

document.querySelector('.one-time-animation').addEventListener("click", function(e) {
  e.target.classList.toggle('selected');
})
.one-time-animation {
  animation: one-time-animation 2s forwards 1;
}
.selected {
  animation: one-time-animation 2s forwards 1, selected 2s forwards 1;
}
@keyframes one-time-animation {
  from {
    background: red;
  }
  to {
    background: transparent;
  }
}
@keyframes selected {
  from {
    box-shadow: 0 0 0 green inset;
  }
  to {
    box-shadow: 0 0 10px green inset;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="one-time-animation">Click to toggle selected class</div>

Если невозможно добавить оригинальную анимацию как часть стека для selected класс (скажем, потому что он используется для нескольких элементов, каждый из которых имеет разные оригинальные анимации), то подход должен быть, чтобы получить текущий animation, добавить selected анимация к нему, а затем установить его через JS.

Я не эксперт по JS, но приведенный ниже фрагмент должен дать вам представление о том, что я имею в виду.

var i = false;

document.querySelector('.one-time-animation').addEventListener("click", function(e) {
  var style = window.getComputedStyle(e.target);
  var currAnim = style.getPropertyValue(PrefixFree.prefix + 'animation-name') + ' ' + style.getPropertyValue(PrefixFree.prefix + 'animation-duration') + ' ' + style.getPropertyValue(PrefixFree.prefix + 'animation-fill-mode') + ' ' + style.getPropertyValue(PrefixFree.prefix + 'animation-iteration-count');
  var newAnim;
  if (!i)
    newAnim = currAnim + ', selected 2s forwards 1;';
  else
    newAnim = currAnim.substring(0, currAnim.indexOf(','));
  e.target.setAttribute('style', PrefixFree.prefix + 'animation: ' + newAnim);
  i = !i;
});
.one-time-animation {
  animation: one-time-animation 2s forwards 1;
}
@keyframes one-time-animation {
  from {
    background: red;
  }
  to {
    background: transparent;
  }
}
@keyframes selected {
  from {
    box-shadow: 0 0 0 green inset;
  }
  to {
    box-shadow: 0 0 10px green inset;
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class="one-time-animation">Click to toggle selected class</div>

Я верю animation-iteration-count сбрасывается при повторном применении из-за переключения select учебный класс. Это связано с каскадным характером CSS.

Одним из вариантов будет просто удалить one-time-animation класс, когда вы идете, чтобы выбрать его:

document.querySelector('.selectable').addEventListener("click", function(e) {
    e.target.classList.remove('one-time-animation');
    e.target.classList.toggle('selected');
})
.one-time-animation {
    animation: one-time-animation 2s forwards 1;
}

.selected {
    animation: selected 2s forwards 1;
}

@keyframes one-time-animation {
    from {
        background:red;
    }
    to {
        background:transparent;
    }
}

@keyframes selected {
    from {
        box-shadow: 0 0 0 green inset;
    }
    to {
        box-shadow: 0 0 10px green inset;
    }
}
<div class="selectable one-time-animation">Click to toggle selected class</div>

Другие вопросы по тегам