k8o

Blog

公開: 2026年10月10日(土)約6分で読めます

イベントのanimationプロパティで発火元のCSSAnimationとCSSTransitionを取り出す

AnimationEventとTransitionEventにanimationプロパティが追加され、animationstartなどのイベントから発火元のCSSAnimationやCSSTransitionを受け取れるようになりました。Animation accessorsとしてBaseline 2026に加わったこの機能について、getAnimations()との違いや疑似要素の扱い、使うときの注意を整理します。

Newly availableAnimation accessors2026年〜
  • Chrome151
  • Edge151
  • Firefox152
  • Safari27

はじめに

CSSのアニメーションやトランジションの節目は、animationstartやtransitionendなどのイベントで受け取れます。ただし、イベントから分かるのはanimationNameやpropertyNameといった名前とelapsedTimeだけでした。

アニメーションを途中で止めたり速度を変えたりするには、Animationオブジェクトが必要です。これまではgetAnimations()で要素のアニメーションを一覧にして、名前の一致するものを探していました。

js
element.addEventListener('animationstart', (event) => {
  const animation = element
    .getAnimations()
    .find((a) => a.animationName === event.animationName);
});

Baseline 2026では、この探し方をしなくても済むようになりました。AnimationEventとTransitionEventにanimationプロパティが追加され、イベントを発火させたAnimationをそのまま受け取れます。この2つのプロパティはBaselineでAnimation accessorsとまとめて呼ばれており、Chrome 151、Firefox 152、Safari 27が対応しています。

書き方

使い方はevent.animationを読むだけです。animationstartなどで受け取るAnimationEventのanimationにはCSSAnimationが、transitionrunなどで受け取るTransitionEventのanimationにはCSSTransitionが入っています。どちらもAnimationのサブクラスなので、playbackRateやpause()、finishedをそのまま使えます。

js
panel.addEventListener('transitionrun', (event) => {
  const transition = event.animation;
  if (transition.transitionProperty !== 'opacity') return;
  // 閉じるときのフェードだけ3倍速で終わらせる
  if (panel.classList.contains('is-closing')) {
    transition.playbackRate = 3;
  }
});

transitionrunで受け取ったCSSTransitionは、同じトランジションのtransitionstartやtransitionendで受け取るものと同じオブジェクトです。1本のアニメーションのanimationstartとanimationiteration、animationendでも同様です。animationiterationで周回を数え、決めた回数でpause()を呼ぶといった使い方ができます。event.animation.finishedをawaitすれば、終わるまで待つことも可能です。

js
let iterations = 0;
badge.addEventListener('animationiteration', (event) => {
  iterations += 1;
  // 2周したところで止める
  if (iterations === 2) event.animation.pause();
});

getAnimations()で探す方法との違い

getAnimations()から名前で探す方法には、うまくいかない場面が2つありました。

1つ目は、同じ名前のアニメーションを2本指定したときです。animation: fade 0.1s, fade 0.2sのように書くとanimationstartは2回発火しますが、event.animationNameは2回ともfadeなので、どちらのアニメーションのイベントかを名前では区別できません。この問題は、CSSWGでこのプロパティを決めたときの動機の1つでもあります。event.animationには別々のCSSAnimationが入り、event.animation.effect.getTiming().durationを読むと100と200が返ります。

2つ目は、animationendで受け取るときです。animation-fill-modeがnoneだと、animationendの中でgetAnimations()を呼んでも、いま終わったアニメーションは見つかりません。getAnimations()が返すのは仕様でrelevantと定義されたアニメーション、つまり効果がこれから始まるか進行中か、まだ残っているものだけです。noneの場合、終わった時点でこの条件から外れます。event.animationなら、終わった後でもplayStateがfinishedのCSSAnimationとして読めます。

Playground

同じ名前の2本のアニメーションをevent.animationで区別する

animation-accessors

同じ@keyframesを長さ違いで2本指定した要素のイベントを記録し、animationNameでは同じに見える2本をevent.animationのdurationで見分けられる様子を確認できます。animationendでは、終わったアニメーションがgetAnimations()から外れていることも分かります。

イベントのログ

再生を押すと、イベントごとにevent.animationから読んだ値が並びます

要素に当てているCSSです。animationNameはどちらもbeatですが、event.animationのdurationで2本を区別できます。

@keyframes beat {
  50% {
    opacity: 0.2;
  }
}

.animation-accessors-target {
  animation:
    beat 0.5s 3,
    beat 0.8s 3;
}

ログのanimationNameはすべてbeatですが、event.animationから読んだdurationで2本を区別できます。animationendの行ではgetAnimations().includes(animation)がfalseになり、終わったアニメーションが一覧から外れたと分かります。

疑似要素のアニメーション

::beforeなどの疑似要素に指定したアニメーションのイベントは、元の要素で発火します。event.targetは元の要素を指し、event.pseudoElementが::beforeになります。

element.getAnimations()で疑似要素のアニメーションまで探すには、getAnimations({ subtree: true })のようにオプションが要ります。既定では要素自身のアニメーションしか返さないためです。event.animationなら探す必要はなく、event.animation.effectのpseudoElementが::before、targetが元の要素になっています。

使うときの注意

使うときに気をつけたい点が2つあります。

1つ目は、event.animationがnullのことがある点です。new AnimationEvent('animationend')のように自前で作ったイベントは、初期化辞書でanimationを渡さない限りnullです。dispatchEvent()で流したイベントも同じリスナーで受けるなら、nullの場合に備えておきます。

js
// 初期化辞書で渡さなければ event.animation は null
const event = new AnimationEvent('animationend', { animationName: 'fade' });

2つ目は、event.animationからアニメーションを操作すると、CSS側の指定が効かなくなる点です。たとえばanimation-play-stateは、CSSアニメーションをrunningで再生し、pausedで止めるプロパティです。:hoverのときだけpausedにして、ホバー中は止めるといった使い方をします。ところがevent.animationでpause()やplay()を一度呼ぶと、そのアニメーションは以後animation-play-stateの変更に従わなくなります。CSS Animations Level 2が、APIでの操作をCSSより優先すると定めているためです。effect.updateTiming()で指定した項目も同じで、対応するanimation-*プロパティの変更は反映されません。CSS側で制御し続けたいアニメーションでは、event.animationは読むだけにとどめます。

おわりに

Animation accessorsによって、アニメーションのイベントから発火元のAnimationをそのまま受け取れるようになりました。getAnimations()から名前で探す必要も、同じ名前の2本を区別する工夫も要りません。Chrome 151、Firefox 152、Safari 27が対応し、主要ブラウザで使えます。

読了率 0%
もくじ