コピペで簡単!JavaScriptとCSSで作る3Dサイコロ抽選アニメーション

Webサイトに「動き」を!CSSとJSで作る3Dサイコロ抽選アニメーション

静的なWebサイトも良いですが、ユーザーの目を引くインタラクティブな要素(動き)を加えることで、サイトの印象はガラッと変わります。

特にキャンペーンページやイベント告知などで、「抽選」の仕組みがあると盛り上がりますよね。

今回は、Web制作者や学習中の方がコピペしてすぐ使える、CSSの3D表現とJavaScriptの配列処理を組み合わせた「3Dサイコロ抽選アニメーション」の実装方法を、コード付きで分かりやすく解説します。

完成デモ

まずは完成イメージをご覧ください。「サイコロを振る」ボタンを押すとアニメーションが始まり、「とめる」ボタンで配列(今回は人の名前)からランダムに1つを選んで表示します。一度選ばれた要素は、配列から削除される仕組みです。

実装コード全体

実装に必要なコードは「HTML」「SCSS (CSS)」「JavaScript」の3つです。今回のJavaScriptはjQueryを使用しているため、別途jQuery本体を読み込んでおく必要があります。

HTMLコード

構造は非常にシンプルです。サイコロの各面となる .item と、操作ボタン .js-dice を配置します。

<div id="wrap">
  <div class="stage">
    <div class="dice">
      <div class="item">1</div>
      <div class="item -main">2</div>
      <div class="item">3</div>
      <div class="item">4</div>
      <div class="item">5</div>
      <div class="item">6</div>
    </div>
  </div>
  <div class="button_wrap">
    <button class="js-dice">サイコロを振る</button>
  </div>
  <p class="leftovers-caption">【残りの値】</p>
  <div id="leftovers"></div>
</div>

SCSS (CSS) コード

サイコロの3D表現とアニメーションを定義します。SCSSを使用していますが、ネスト(入れ子)を解除すれば通常のCSSとしても機能します。

/* サイコロ本体 */
.dice {
  display:block;
  position:relative;
  margin:0 auto;
  width:100px;
  height:100px;
  transform-style:preserve-3d;
}
/* 目 */
.dice .item {
  border-radius: 3px;
  position:absolute;
  left:0;
  right:0;
  display:flex;
  flex-direction:row;
  align-items:center;
  justify-content:center;
  box-sizing:border-box;
  border:1px solid #333;
  width:100px;
  height:100px;
  background-color:rgba(#005bac, 0.7);
  font-size:2rem;
  text-align:center;
  color:#fff;
  &:nth-child(1) {
    transform:translate3d(0, -50px, 0) rotateX(-90deg);
  }
  //rotateがないのが正面
  &:nth-child(2) {
    transform:translate3d(0, 0, 50px);
  }
  &:nth-child(3) {
    transform:translate3d(50px, 0, 0) rotateY(90deg);
  }
  &:nth-child(4) {
    transform:translate3d(-50px, 0, 0) rotateY(-90deg);
  }
  &:nth-child(5) {
    transform:translate3d(0, 0, -50px) rotateY(180deg);
  }
  &:nth-child(6) {
    transform:translate3d(0, 50px, 0) rotateX(-90deg);
  }
}

//回転アニメーション:遠近
@keyframes rotate-animation {
  0% { transform:rotate3d(0); }
  100% { transform:rotate3d(0.5,0.75,0.5, 360deg); }
}
@keyframes rotate-animation1 {
  0% { transform:rotate3d(0); }
  100% { transform:rotate3d(1,0,1, -360deg); }
}
@keyframes rotate-animation2 {
  0% { transform:rotate3d(0); }
  100% { transform:rotate3d(0,1,1, -360deg); }
}
//ストップモーションアニメーション
@keyframes stopmotion {
  0% { transform:scale(0.5); }
  50% { transform:scale(1.2); }
  100% { transform:scale(1); }
}

//その他
#wrap {
  padding: 0 0 80px;
}
.stage {
  margin: 60px auto;
  perspective: 300px;
  transition: transform 0s;
  .dice {
    animation:rotate-animation 10s linear infinite;
    //サイコロを斜めに
    //transform:rotateX(-25deg) rotateY(-45deg);
  }
  //アニメ中style
  &.animation {
    transition: transform .3s;
    transform: scale(0.5);
    .dice {
      transform: none;
      animation:rotate-animation1 .3s linear infinite;
    }
    &.plus .dice{
      animation:rotate-animation2 .3s linear infinite;
    }
  }
  //結果画面
  &.stop {
    animation: stopmotion .2s both;
    .dice {
      transform: none;
      animation: none;
      .item.-main {
        //結果画面の背景画像変えるなら
        //background-color:rgba(0, 30, 30, 0.5);
      }
    }
  }
}
//ボタン
.button_wrap {
  margin-top: 60px;
  text-align: center;
  button {
    cursor: pointer;
  }
}

//配列の中身
.leftovers-caption {
  margin-top: 2em;
  text-align: center;
}
#leftovers {
  margin: 0 auto 2em;
  text-align: center;
}

JavaScript (jQuery) コード

ボタンのクリックイベントを監視し、CSSのクラスを付け替えることでアニメーションを制御します。抽選ロジックがメインです。

//settimeout用変数
var timerId;

//結果用配列
var resultArray = [
  "佐藤",
  "鈴木",
  "田中",
  "渡辺",
  "山本",
  "★"
]

//HTMLに配列の値を追加
for (var key in resultArray) {
  var num = parseInt(key) + 1;
  $('.dice .item:nth-child('+num+')').text(resultArray[key]);
}

//配列の値を指定のIDにテキストとして追加
$('#leftovers').text(JSON.stringify(resultArray));


//配列の値の数を取得
//オブジェクト(連想配列)の時の撮り方 → var res = Object.keys(resultArray).length;
var res = resultArray.length;

$('.js-dice').on('click',function(){

  //配列の残りの数が0以上なら実行
  if(res > 0) {

    //アニメーション中にボタンが押されたら
    if($(this).hasClass('start')) {
      //class="animation"を除去してclass="stop"を追加
      $('.stage').removeClass('animation').addClass('stop');
      //ボタンからもclass="start"を除去して、テキスト変更
      $(this).removeClass('start').text('サイコロを振る');
      //setIntervalを解除
      window.clearInterval(timerId);
      //サイコロの目のテキストを変更
      let saikoro = Math.floor( Math.random() * res) +1;
      $('.-main').text(resultArray[saikoro - 1]);

      //サイコロででた値を配列から削除
      res = res - 1;
      resultArray.splice( saikoro - 1, 1 );

      //値の減った配列の値をテキストにして出力
      $('#leftovers').text(JSON.stringify(resultArray));

      //配列に何も残ってなかったらボタンのテキストを変更
      if(res == 0) {
        $(this).removeClass('start').text('本日の抽選は以上です');
      }

    //アニメが停止中にボタンが押されたら
    } else {

        //class="stop"を除去してclass="animation"を追加
        $('.stage').addClass('animation').removeClass('stop');
        //ボタンにclass="start"を追加して、テキスト変更
        $(this).addClass('start').text('とめる');
        //サイコロの動きに変化をつけるようsetIntervalでclassを付け替え
        timerId = setInterval(function() {
          $('.stage').toggleClass('plus');
        }, 500);

    }
  }

});

コードのポイント解説

この実装には、Web制作初心者がつまずきやすいポイントがいくつか含まれています。特に重要な2点を解説します。

1. CSSによる3D空間の構築

サイコロが立体的に見えるのは、CSSの transform 関連のプロパティのおかげです。

perspective: 300px;(.stage): これが「視点」の役割を果たします。この値(奥行き)がないと、3Dの表現はただの平面的な変形(2D)に見えてしまいます。親要素に指定するのがポイントです。

transform-style: preserve-3d;(.dice): これが「3D空間」を宣言するプロパティです。子要素(.item)の3D変形を有効にし、立体的な配置を維持します。

transform: translate3d(...) rotateX(...)(.item): 各面を translate3d で移動させ、rotateXrotateY で回転させることで、立方体の6面を組み立てています。

2. JavaScriptによる抽選ロジック

JavaScriptは、アニメーションの制御と抽選処理を担当しています。

クラスの付け替え: .js-dice ボタンをクリックするたびに、.stage 要素の .animation クラスと .stop クラスを付け替えています。CSS側では、このクラスの有無で @keyframes アニメーションを再生したり停止したりしています。

Math.floor( Math.random() * res) + 1: JavaScriptでランダムな整数を生成する定番の記述です。Math.random()(0以上1未満の小数)に配列の現在の長さ(res)を掛け、切り捨てることで「0から(res-1)まで」のインデックス番号を取得しています。

resultArray.splice( saikoro - 1, 1 ): splice メソッドは、配列から指定した要素を「削除」するために使います。これにより、一度当選したものが再度抽選されない「重複なし」の抽選を実現しています。

まとめ

今回は、CSSの3D表現とJavaScriptの配列処理を組み合わせた、インタラクティブな抽選アニメーションをご紹介しました。

resultArray の中身を商品名に変えればキャンペーンの抽選機になりますし、CSSの背景色や @keyframes の内容を変更すれば、オリジナルのアニメーションにカスタマイズできます。ぜひあなたのWebサイトにも、遊び心のある「動き」を取り入れてみてください。

よくある質問(FAQ)

サイコロの面を数字やテキストではなく、画像に差し替えることはできますか?
はい、可能です。HTMLの <div class="item">テキスト</div> の中身を、<img src="..." alt="..."> タグに置き換えるのが最も簡単です。ただし、.item に指定しているCSS(display: flex など)が画像の表示に影響する場合があります。その際は、.itembackground-image プロパティを使って背景画像として設定する方法もおすすめです。
抽選対象(配列の中身)を動的に変更することは可能ですか?
はい、可能です。今回のサンプルコードでは resultArray がJavaScriptファイル内に直接書き込まれていますが、例えばPHPやデータベースと連携し、サーバーから抽選対象のリスト(JSON形式など)を取得して resultArray に代入するロジックに変更すれば、動的な抽選システムを構築できます。
CSSの preserve-3d がうまく効かず、サイコロが立体的に見えません。
3D表現でつまずく最も一般的な原因は、親要素に perspective が指定されていないことです。サイコロ本体(.dice)の親要素である .stage に、perspective: 300px; のような奥行きを指定しているか確認してください。また、.dice 自身に transform-style: preserve-3d; が指定されているかも併せてご確認ください。

CONTACT

webサイト制作、デザインに関するご相談、御見積のご依頼など、弊社へのお問い合わせはこちら