Three.jsのアニメーションに停止ボタンを実装してみた

に公開

はじめに

こんにちは、もりみちです。
本記事では、Three.jsを使った3Dアニメーションにアクセシブルな停止機能を実装し、WCAG達成基準2.2.2(一時停止、停止、非表示)を満たす方法を紹介します。

デモ

こちらがデモサイトになります。
https://accessible-threejs-demo-01.pages.dev/

なぜ停止ボタンが必要なのか

Three.jsを含む動きがあるアニメーションはユーザーの注意を引きつけ、視覚的なフィードバックを提供するなど、多くのメリットがあります。しかし持続する動きによって一部のユーザーが、集中力の阻害、読字困難などの問題を引き起こす可能性があります。
また、WCAG 2.2.2「一時停止、停止、非表示」は、動きのあるコンテンツに対して、ユーザーが一時停止、停止、非表示にする機能を提供することを要求しています。
そのため、自動的に開始され継続するアニメーションはユーザー自身が制御できる選択肢を提供することが、アクセシビリティの観点から非常に重要です。

解説

それではデモで実装したコードを一部抜粋し解説します。

HTML

index.html
 <button type="button" class="button" aria-label="アニメーションを停止" data-button>
 <svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" class="icon stop">
  <path fill="currentColor" d="M14 19V5h4v14zm-8 0V5h4v14z"></path>
 </svg>
 <svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true" class="icon play">
  <path fill="currentColor" d="M8 5.14v14l11-7z"></path>
 </svg>
</button>

ボタンがアイコンのみで構成されている場合、スクリーンリーダーのユーザーにその役割を伝えるためにaria-labelを使用します。今回はサイト訪問時にアニメーションが既に再生されていることを想定し、初期ラベルを「アニメーションを停止」に設定しました。

CSS

style.css
.icon.play {
  display: none;
}
.button.stop .icon.play {
  display: block;
}
.button.stop .icon.stop {
  display: none;
}

JavaScript

script.js
// DOM要素の取得
const canvas = document.querySelector('[data-canvas]');
const button = document.querySelector('[data-button]');

// ...Three.jsの基本設定(シーン、カメラ、レンダラーなど)

// サンプルのボックスメッシュの作成
const box = new THREE.BoxGeometry(window.innerHeight * 0.5, window.innerHeight * 0.5, window.innerHeight * 0.5);
const material = new THREE.MeshBasicMaterial({ color: 'blue' });
const boxMesh = new THREE.Mesh(box, material);
scene.add(boxMesh);

let isPlaying = true;
let animationId = null;
const tick = () => {
  if (isPlaying) {
    boxMesh.rotation.y += 0.01;
    boxMesh.rotation.x += 0.01;
    boxMesh.rotation.z += 0.01;
  }
  renderer.render(scene, camera);
  animationId = requestAnimationFrame(tick);
};

const handleButtonClick = () => {
  isPlaying = !isPlaying;

  if (!isPlaying) {
    if (animationId) {
      cancelAnimationFrame(animationId);
      animationId = null;
    }
  } else {
    tick();
  }

  button.setAttribute("aria-label", isPlaying ? "アニメーションを停止" : "アニメーションを再開");
  button.classList.toggle("stop", !isPlaying);
};

button.addEventListener('click', handleButtonClick);
tick();

isPlayingはアニメーションの再生状態を管理するフラグです。
ボタンをクリックすると、アニメーションの再生/停止を切り替え、状態に応じてアイコンとaria-labelを更新します。これにより、スクリーンリーダーのユーザーにも、ボタンの現在の役割(「停止」か「再開」か)が正確に伝わります。

まとめ

今回はThree.jsを使用した3Dアニメーションに対して再生・停止ボタンを実装し、WCAG 2.2.2を達成する方法について解説しました。アニメーションの停止機能は、特定のユーザーのためだけでなく、すべてのユーザーにとっての使いやすさを向上させる重要な要素です。
ほんの少しの実装で、より多くのユーザーが快適にコンテンツを利用できるようになります。
この記事が、皆さんの実装の参考になれば幸いです。

参考

https://waic.jp/translations/WCAG22/Understanding/
https://waic.jp/files/cheatsheet/waic_jis-x-8341-3_cheatsheet_201812.pdf
https://www.w3.org/TR/WCAG22/#pause-stop-hide

Discussion