アニメーション期間0msの場合のイベント発火問題を修正

shoelace-style/webawesome

アニメーション期間が0msに設定されている場合、animationendイベントが発火せず、ダイアログのイベントハンドリングが正常に動作しない問題が修正されました。この変更により、prefers-reduced-motionなどでアニメーションを無効化する場合でも、コンポーネントが正しく動作するようになります。

背景

--show-duration をアクセシビリティ対応のために0msに設定した場合、特にWindows環境で問題が発生していました。アニメーション期間が0msの場合、ブラウザはanimationendイベントを発火させないため、animateWithClass関数内のPromiseが永久に解決されない状態になっていました。この問題により、ダイアログの表示・非表示に関連するイベントリスナーが適切に動作せず、後続の処理がブロックされる状態でした。

従来のコードには「failsafeの追加が必要」というTODOコメントがありましたが、具体的な実装がなされていませんでした。アクセシビリティ要件としてモーション削減を設定する環境が増える中、この問題への対応が求められていました。

技術的な変更

packages/webawesome/src/internal/animate.tsanimateWithClass 関数に、アニメーションが実際に存在するかをチェックする機構が追加されました。

変更前:

let onEnd = () => {
  el.classList.remove(className);
  resolve();
  controller.abort();
};

el.addEventListener('animationend', onEnd, { once: true, signal });
el.addEventListener('animationcancel', onEnd, { once: true, signal });
// TODO add failsafe if neither of these fires

変更後:

let resolved = false;
let onEnd = () => {
  if (resolved) {
    return;
  }
  resolved = true;
  el.classList.remove(className);
  resolve();
  controller.abort();
};

el.addEventListener('animationend', onEnd, { once: true, signal });
el.addEventListener('animationcancel', onEnd, { once: true, signal });

// if there are no animations or animation is set to 0ms, end immediately
requestAnimationFrame(() => {
  if (!resolved && el.getAnimations().length === 0) {
    onEnd();
  }
});

変更の中核は requestAnimationFrame コールバック内での el.getAnimations() チェックです。requestAnimationFrameを使用することで、ブラウザが次のフレームを描画する前にアニメーション状態を確認できます。この時点でアニメーションが存在しない場合、即座にonEndを呼び出してPromiseを解決します。

resolvedフラグの導入により、onEndが複数回呼び出されても副作用が発生しないようガードされています。これはanimationendイベントとrequestAnimationFrameのコールバックが競合する可能性に対する防御策です。

設計判断

requestAnimationFrameによるポーリング方式 が採用されました。

アニメーション期間0msの検出方法として、CSS変数の値を直接読み取る方法やタイムアウトベースの実装も考えられましたが、getAnimations()を使用する方式が選ばれています。この API は Web Animations API の標準メソッドであり、ブラウザが実際に適用しているアニメーション状態を正確に反映します。

requestAnimationFrameの使用により、CSSクラスが適用された直後ではなく、ブラウザがスタイル計算を完了した後のタイミングでチェックが実行されます。これは、CSSクラス追加直後はまだアニメーションが評価されていない可能性があるためです。

TODOコメントの削除は、この実装がアニメーションイベントが発火しないすべてのケース(期間0ms、アニメーション未定義、display: none要素など)をカバーすることを示しています。

まとめ

本PRは、アニメーション期間0msのエッジケースに対する堅牢性を向上させた変更です。getAnimations()による実行時チェックとresolvedフラグによる冪等性の保証により、アクセシビリティ設定に関わらずコンポーネントが一貫して動作するようになりました。Windows環境での実地テストも完了しており、prefers-reduced-motion設定下でも安定した動作が期待できます。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

「リード文(総論)→セクション群(各論)→まとめ(結論)」の構成が明確です。背景、技術的な変更、設計判断といった各論の要素もすべて含まれており、模範的な記事構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```typescript:path/to/file.ts)とGitHubのPRリンク記法([#2068](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

内容は`animationend`イベントや`requestAnimationFrame`などフロントエンドの専門知識を前提としており、対象読者であるエンジニアに適した技術レベルと表現です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、かつ各段落の先頭にトピックセンテンスが置かれているため、非常に読みやすいです。1段落1トピックの原則も守られています。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事で引用されている変更後のコードは、提供されたDiffの内容と完全に一致しています。変更前のコードも、変更点を理解するための文脈として適切に引用されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「animationend」「requestAnimationFrame」「getAnimations」などの技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

アニメーション期間が0msの場合にイベントが発火しない問題や、`getAnimations()`を用いた解決策の説明は、技術的に正確かつ論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(Windowsでの問題発生、TODOコメントの存在、テスト完了など)は、PRのDescriptionやDiffの内容によって裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#2068)やファイルパス(packages/webawesome/src/internal/animate.ts)などの固有名詞はすべて正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「アニメーション期間0msの場合のイベント発火問題を修正」は、PRのタイトル「Fix animateWithClass for 0ms duration」の内容を的確に表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事には、PRに記載のないバージョンサポート情報やリリース日程などの外部知識は含まれていません。技術的な背景説明もPRの範囲内に留まっています。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「問題が発生していました」「修正されました」など、過去の問題と今回の変更点を区別する時間表現が正確に使用されています。