在定义不同动画的同一元素上切换 class 时,我可以限制 CSS 动画只播放一次吗?

Can I limit a CSS animation to play only once when toggling a class on the same element defining a different animation?

基本上,我试图在 "most recent action" 中受影响的某些元素上显示动画。此动画完成后不应再次播放。我现在遇到的问题是我还定义了一个动画,它在元素为 "selected" 时运行。当元素被取消选择时,原始动画再次播放,即使已经达到 1 的 animation-iteration-count。我当然可以为 animationend 触发时设置一个事件侦听器,然后删除与动画关联的 class。我更想解释为什么这不能按预期工作。我想这与 question.

有点相反

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>

我相信 animation-iteration-count 在重新应用时会重置,因为您切换了 select class。这是由于 CSS.

的级联性质

一个选择是在您转到 select 时简单地删除 one-time-animation class 它:

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>

纯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 class 添加到元素时,它不再具有 one-time-animation。这是因为 animation 属性 的最新指定值覆盖了先前指定的值(正如 sennan 正确指出的那样)。

selected class 关闭时,one-time-animation 变得可用,到现在用户代理已经不记得这个动画早先出现在元素上了。这会导致动画重新执行,即使 animation-iteration-count 设置为 1.

解决此问题的一种方法是让用户代理知道原始动画从未真正从元素中移除。这可以通过将其保留为 selected class 中堆栈的一部分来执行。动画 属性 可以支持逗号分隔格式的多个动画。原始动画不会再 运行 因为用户代理现在将其视为曾经存在并因此维护迭代计数器。

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 class 堆栈的一部分(比如因为它用于多个元素,每个元素都有不同的原始动画),那么方法应该是获取当前的 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>