ランサーズ(Lancers)エンジニアブログ > JavaScript > 某お笑い番組っぽい結果発表シーンを JavaScript CSS で再現してみた

某お笑い番組っぽい結果発表シーンを JavaScript CSS で再現してみた

tsuyoshi|2017年12月03日
JavaScript

tsuyoshi(@numanomanu)です。ランサーズアドベントカレンダ−3日目の記事です。前回 React Redux を用いた SPA 新規サービスを運用して得た知見と実装例 を書いたので、今回も引き続き javascript 周りについて書きました。話は変わりますが、忘年会シーズンですね。会社でクイズ大会などのお楽しみコンテンツがあるかもしれません。今日はそんな時にしか役に立たない(!?)某お笑い番組的な結果発表シーンを JavaScript と CSS を使って再現するチップスのご紹介です。

雑な完成品は以下の gif 画像または、 音出ます注意 からどうぞ

これはランサーズ、「熱狂」の全社合宿を実施しました! にて、地方プレゼンバトルというコンテンツで利用した、結果発表をインタラクティブに表示する仕組みです。地方プレゼンバトルとは各地方出身者がその土地の魅力を発表しあい、頂点を争い合う競技です。(最終盤はもう少しクオリティが高いのですが、著作権などを侵害しないように現バージョンで止めております)
最初に、審査員たちの得点をスタイリッシュに並べるシーンがあり、合計得点をドーンとだし、最後にグイッと結果を表示するというフローでできています。

今回は上記を実現するために使った Javascript と CSS のテクニックを、それぞれご紹介します。

数字をクルクルしてシュキーンとさせる



<li class="kurukuru">50</li>


.kurukuru {
  transition: transform 0.3s ease-in;
  transform: rotateX(0deg)
}
.start {
  transform: rotateX(1800deg)
}
  $('li.kurukuru').addClass('start')

クルクル要素は transform の rotate で実現できます。
1800 / 360 = 5 回クルクルさせてます。rotateX で x 方向(横軸)の回転を表現し、 start が付与されるタイミングで 0 -> 1800dg の角度回転します。
transition を使う事でプロパティの変更があった時に、一定時間(今なら 0.3s)の間でクルクル回転している様が再現されます。

さらに、手前から来ているシュキーン感を出すために、 scale を使い、最初は大きく、最後は小さくします。
こちらも transition の効果で、大きくなるところから徐々に小さくなっていく様が再現できます。
transition については、以下の記事が詳しくてわかりやすいです。
https://qiita.com/soarflat/items/4a302e0cafa21477707f

.kurukuru {
  transition: transform 0.3s ease-in;
  transform: rotateX(0deg) scale(5)
}

.start {
  transform: rotateX(1800deg) scale(1)
}

fps が荒いですがこんな感じ。

ブルブルっとさせる

.buruburu {
    animation: hurueru .05s;
    animation-iteration-count: 5;
}

@keyframes hurueru {
    0% {transform: translate(0px, 0px) rotateZ(0deg)}
    25% {transform: translate(2px, 2px) rotateZ(1deg)}
    50% {transform: translate(0px, 2px) rotateZ(0deg)}
    75% {transform: translate(2px, 0px) rotateZ(-1deg)}
    100% {transform: translate(0px, 0px) rotateZ(0deg)}
}

震えを再現するために、 @keyframes を使います。
またまた fps が荒いですがこんな感じです。

フォントをそれっぽくする

Google Fonts から、太めのフォントを探して、埋め込みます。
一番それっぽかった「Russo One」を利用しました。text-shadow を使えば、文字を立体的に見せることができます。

<link href="https://fonts.googleapis.com/css?family=Aldrich|Changa+One|Russo+One|Squada+One|Stalinist+One" rel="stylesheet">
.score {
    color: #ffee10;
    font-size: 4rem;
    font-style: italic;
    font-weight: bold;
    text-shadow: 1px 3px 0px #939393, 0px 6px 0px #949350;
    font-family: Russo One;
}

下からふわっとしてドーンとさせる

結果の順序を入れ替えるところを実装します。

.insertTarget {
  transition: opacity 0.6s, transform 1s cubic-bezier(0.95, 0.05, 0.795, 0.035);
  transform: translate(0px, 600px) rotateX(0deg) scale(5)
}
  $(function () { $('.insertTarget').css({
      transform: 'translate(0px, 200px) scale(5)'
  }).on('transitionend', function () {
      $(this).css({
          transform: 'scale(1)'
      }).off('transitionend');
  })});



<div class="insertTarget">hoge</div>



ポイントは、 jquery で ‘transitionend’ のイベントをキャッチして、 トランジションが終わった後に新たな transition を発生させています。
具体的には transform: translate(0px, 600px) で下から translate(0px, 200px) とふわっと出した後に、scale を scale(5) -> scale(1) に変更することで、吸い込まれるようなインサート感のあるアニメーションを演出しています。

キラっとさせる

結果のデータを目立たせるために、キラっとさせてみます。
こちらを参考にしました。
http://www.trust5.jp/jquery-light/

.kira {
    width: 100%;
    height: 300px;
    background: #4a3f3f;
}

.reflection {
    height      :100%;
    width       :30px;
    position    :absolute;
    top         :-180px;
    left        :0;
    background-color: #fff;
    opacity     :0;
    transform: rotate(45deg);
    animation: reflection 2s ease-in-out infinite;
}

@keyframes reflection {
    0% { transform: scale(0) rotate(45deg); opacity: 0; }
    80% { transform: scale(0) rotate(45deg); opacity: 0.5; }
    81% { transform: scale(4) rotate(45deg); opacity: 1; }
    100% { transform: scale(50) rotate(45deg); opacity: 0; }
}


<div class="kira">
  
<div class="reflection"></div>

</div>


ドゥクシッ!させる

音を出すには audio タグを使います

<audio id="duqushi" preload="auto">
  <source src="duqushi.mp3" type="audio/mp3">
</audio>
function duqushi() {
    var se = $('#duqushi');
    se[0].currentTime = 0;
    se[0].play();
}

音源は以下のサイトの 拳銃1 という音を利用しました。個人的にハマったドゥクシッ!音ですが、他にもたくさんあってお好みのものをどうぞ。
https://soundeffect-lab.info/sound/battle/battle2.html

着弾時にバーンと景気よく爆発させる

こちらの記事を参考にしました。
http://blog.enyojs.com/post/93812705854/sprite-tastic

css のスプライト画像を用意(930 x 400 で 10列、4行のスプライト画像)

930 × 400 のサイズなので、それぞれ、1コマ(93px)ずつ keyframes で切り取ります

@keyframes boom {
	0%     { -webkit-transform: translateZ(0) translateX(93px)   translateY(0px); }
	25%    { -webkit-transform: translateZ(0) translateX(-837px) translateY(0px); }
	25.01% { -webkit-transform: translateZ(0) translateX(93px)   translateY(-100px); }
	50%    { -webkit-transform: translateZ(0) translateX(-837px) translateY(-100px); }
	50.01% { -webkit-transform: translateZ(0) translateX(93px)   translateY(-200px); }
	75%    { -webkit-transform: translateZ(0) translateX(-837px) translateY(-200px); }
	75.01% { -webkit-transform: translateZ(0) translateX(93px)   translateY(-300px); }
	100%   { -webkit-transform: translateZ(0) translateX(-837px) translateY(-300px); }
}
.explosion {
    left: 0px;
    width: 93px;
    height: 100px;
    overflow: hidden;
    position: absolute;
}
.explosion:after {
    width: 930px;
    height: 400px;
    content: "";
    display: block;
    position: absolute;
    background-image: url(sprite-explosion.png);
    -webkit-animation: boom 1s steps(10, start) infinite;
 }

すると、以下のように、左から順にスプライトを表示し、アニメーションが完成します。

つなげてみる

var animationDelay = 1000;
$('li.kurukuru').each(function(i, elem){
  $(elem).delay(animationDelay * i).queue(function() {
    $(elem).addClass('visible').dequeue();
    $(elem).addClass('start').dequeue();
   });
});
<ul>
  <li class="kurukuru">50</li>
  <li class="kurukuru">20</li>
  <li class="kurukuru">39</li>
  <li class="kurukuru">14</li>
  <li class="kurukuru">10</li>
</ul>
.kurukuru {
  width: 100px;
  height: 100px;
  display: inline-block;
  transform: rotateX(0deg) scale(2);
  position: relative;
  visibility: hidden;
}

.visible {
  visibility: visible;
}

.start {
  transition: transform 0.3s ease-in;
  transform: rotateX(1800deg) scale(1);
}

ポイントは、 addClass では delay が使えないので、 queue と dequeue を使って、 順番にクラスを付与しているところです。
同じように、爆発のエフェクトや、ぶるぶるのエフェクトも、タイミングを合わせて class を付与して行えます。

応用例

スタイリッシュ(?)な自己紹介。

時限爆弾タイマー

あふれるほどのLGTM

最後に

最終的に色々組み合わせたりスタイルを当てると、以下のような仕組みが出来上がります。
全体のコードは 音出ます注意 の resources からダウンロード可能です。
ぜひ、今年の忘年会の結果発表などに役立てていただければ幸いです。

明日の記事は EC2オリジンのCloudFrontでサムネイルをキャッシュした話 です。