無限ループするアニメーションの、今進んでいるフレームが終わったら停止させる

2022/09/12に公開約2,200字

挙動

ざっくり概要

infiniteの着脱だけでは、進行中のアニメーションの途中で急にアニメーション開始前の状態にワープするので、iterationの回数をカウント→停止ボタン押下時にiteration分の回数繰り返したらアニメーション終了の状態で停止させる
もっとスマートな書き方がある気しかしない(何ならCSSだけで実現できそうな気もする)ですが、思いつかなかったのでとりあえずJSでなんとかしています【求】CSSだけで実現する方法

補足

今回の挙動では必要のない記述ですが、animationendたやanimationiterarionなどのイベントハンドラはeventにanimationNameを持っているので、animationNameによって処理を出し分けることが可能という発見があったのでソースに書いています。なくても問題ありません。(hoge.animationendだと、hogeで実行される全てのアニメーションが終了する度にanimationend内の処理が走るため、一つの要素に複数のアニメーションを適用しており、かつその中で特定のアニメーションが終了した場合にのみ追加処理を実行したい場合などに使う)

ソース

HTML

<button class="button-start">ループ開始</button>
<button class="button-stop">今のループで止める</button>
<div class="animation-control"></div>

SCSS

:root {
  --animationCount: 1;
}
.animation-control {
  width: 200px;
  margin-inline: auto;
  margin-top: 50px;
  aspect-ratio: 1;
  background: green;
  &.on-animation {
    animation: expand 2s infinite;
  }
  &.is-stopped {
    animation-name: expand;
    animation-duration: 2s;
    animation-iteration-count: var(--animationCount);
    animation-fill-mode: forwards;
  }
}
 
@keyframes expand {
  0% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.2);
  }
  100% {
    transform: scale(1);
  }
}

JavaScript

const buttonStart = document.querySelector(".button-start");
const buttonStop = document.querySelector(".button-stop");
const animationElement = document.querySelector(".animation-control");
const HTML = document.querySelector("html");

animationElement.addEventListener("animationiteration", () => {
  const animationCount = Number(getComputedStyle(HTML).getPropertyValue("--animationCount"));
  HTML.style.setProperty("--animationCount", animationCount + 1);
});

buttonStart.addEventListener("click", () => {
  animationElement.classList.add("on-animation");
});

buttonStop.addEventListener("click", () => {
  animationElement.classList.remove("on-animation");
  animationElement.classList.add("is-stopped");
  animationElement.addEventListener("animationend", (event) => {
    /* イベント対象のアニメーション名で処理の出し分可能 */
    if (event.animationName === "expand") {
      animationElement.classList.remove("is-stopped");
      HTML.style.setProperty("--animationCount", 1);
    }
  });
});

Discussion

ログインするとコメントできます