【Sassで解説】ヤマト運輸HPの404エラーページのミニゲームはほぼCssゲームだった


2021/05/22
蛸壺の技術ブログ|ヤマト運輸HPの404エラーページのミニゲームはほぼCssゲームだった

ちょっと前にエラーページなのにカワイイと話題になっていた
ヤマト運輸社の404ページで遊べるミニゲームでは、シンプルながらHtml&Cssのみでほぼゲームとしての機能が完成されており、個人的なWebデザイン創作魂をくすぐられるものがあります。

今回はそのミニゲームの機能の一部をSassで実装できるか、ベースとなるテクニックをちょっとだけ掘り下げてみたいと思います。


エラーページをカスタマイズするという心意気

エラーページは本来SEO効果もない蛇足なものですので、わざわざリソースを割いてまでこだわりのページに作り上げることはしないのですが、ごくごく稀に、訪れるビジターを喜ばせたいがために画期的なアイデアでおもてなししてくれるウェブサイトを見かけます。

一見集客性のない404ページですが、何かの拍子で話題に上がれば、ウェブサイトを訪れた人の印象にも残ったり、企業であれば顧客好感度アップの効果もあるかも知れません。

このブログでも以前、AWSで静的ホスティングしてCDN型のウェブサイトの404ページのカスタマイズ方法に関して以下のような内容でポストしておりました。

AWS CloudFrontを駆使した柔軟なHTTPリクエストのリダイレクト機能を利用して、特設の404ページに誘導することは割と簡単ですので、Webページ作成に予算と時間の余裕があれば是非ともチャレンジしてはいかがかと思います。


画像の位置を変えてアニメーション

ますはヤマト運輸の404ページへリンクさせていただいた状態で、ゲームの挙動だけを抽出して実行させてみましょう。

最低限動作するテスト用のindex.htmlは、

            
            <html>
    <head>
        <link rel="stylesheet" type="text/css" href="style.css">
    </head>
    <body>
        <div id="game-wrapper">
            <div class="box-game" id="box-1">
                <input type='checkbox' id='checkbox1'>
                <label for='checkbox1'></label>
                <div class="box-game-inner"></div>
            </div>
            <div class="box-game" id="box-2">
                <input type='checkbox' id='checkbox2'>
                <label for='checkbox2'></label>
                <div class="box-game-inner"></div>
            </div>
        </div>
    </body>
</html>
        
としておきましょう。

このhtmlに割り当てるcssスタイルファイル(style.css)ですが、以下のScssファイル(style.scss)をSassコンパイルしたものを利用します。

            
            #game-wrapper {
    width: 100%;
    height: 300px;
    background-color: darkgray;
    box-sizing: border-box;
    position:relative;
    .box-game {
        position: absolute;
        width: 84px;
        height: 184px;
        overflow: hidden;
        label {
            display: block;
            position: absolute;
            bottom: 5px;
            left: 18px;
            width: 55px;
            height: 55px;
            z-index: 1;
        }
        input {
            display: none;
        }
    }
    #box-1{
        display: block;
        left: 100px;
        bottom: 158px;
        .box-game-inner {
            position: absolute;
            display: block;
            width: 84px;
            height: 184px;
            overflow: hidden;
            background: transparent url('https://www.kuronekoyamato.co.jp/ytc/img/404error_miss01.png') no-repeat 0 0;
            background-size: auto;
            background-size: 84px 12880px;
        }
        #checkbox1:checked ~ .box-game-inner {
            animation: sprite-miss 3s steps(69) 0s forwards;
            @keyframes sprite-miss {
                0% {
                    background-position: 0 0;
                }
                100% {
                    background-position: 0 -12696px;
                }
            }
        }
    }
    #box-2{
        display: block;
        left: 300px;
        bottom: 27px;
        .box-game-inner {
            position: absolute;
            display: block;
            width: 84px;
            height: 184px;
            overflow: hidden;
            background: transparent url('https://www.kuronekoyamato.co.jp/ytc/img/404error_hit01.png') no-repeat 0 0;
            background-size: auto;
            background-size: 84px 13248px;
        }
        #checkbox2:checked ~ .box-game-inner {
            animation: sprite-hit 3s steps(71) 0s forwards;
            @keyframes sprite-hit {
                0% {
                    background-position: 0 0;
                }
                100% {
                    background-position: 0 -13064px;
                }
            }
        }
    }
}
        
※なお、Sassコンパイラをインストールしていない環境であれば、オンラインのコンパイルツールが利用できます。

とりあえずこれらのコードを適当なフォルダに配置し、ブラウザでindex.htmlを叩くと、以下のようなプログラムが走ります。

2つの箱をクリックしていただくと、2通りのアニメーションが始まると思います。

本家のミニゲームには、ゲームのスタート時に箱がシャッフルするステージと、カウントダウンタイマーなどの機能も実装されていますが、今回の記事ではこのミニゲームのもっとも基礎的なhtmlスプライトクリックでのイベントのトリガー方法と、png画像をスライドさせたアニメーションの基礎方法を解説します。


Cssでの(擬似)クリックイベント

本来、HTMLのスタイリングのために使われるCssではクリックやキー入力などのイベントを受け付ける機能は備わっていません。そのため、イベントに対する処理を細かくやろうとするとJavascriptで補助なしでは無理なのですが、input要素とlabel要素を上手く組み合わせることで、擬似的なクリックイベントを発生させることが可能です。

この擬似クリックイベントの実装は難しくはなく、まず、

            
            <style>
.hoge {
    /* inputにチェックが入っていない時のスタイル */
}
要素のID名:checked ~ .hoge {
    /* inputにチェックが入っている時のスタイル */
}
</style>
<input type=“checkbox” id="要素のID名">
<label for="要素のID名"></label>
<div class="hoge">...</div>
        
というlabelとinput要素と何かの後続要素(ここではdiv)を連続して仕込んでおき、input要素のチェック状態がOn・Offで切り替わるごとに、label要素直後にある後続要素が影響を受けて、スタイルも切り替わるという仕組みになっています。

実際の簡単な例をやってみると、

            
            <style>
#pseudo-event:checked ~ .content-area {
    color:red;
}
.content-area {
    color:black;
}
</style>
<input type="checkbox" id="pseudo-event">
<label for="pseudo-event"></label>
<div class="content-area">
    <p>チェックすると色が変化</p>
</div>
        
👇が動作するサンプル

とはいえ、サンプルを見ての通りで、この方法は単純なスタイルのON・OFFの制御しかできませんので、ちょっとでも複雑なゲームになるとCssだけでは制御が厳しくなります。

より複雑なゲームを作成したいのであれば、その場合には大人しくJavascriptでのプログラミングに切り替えた方が賢明といえます。


背景画像をスライドさせてアニメーション効果を付ける

擬似クリックイベントのテクニックと合わせて、チェック後にアニメーションを与えるスタイルを仕込んでおくと、Cssだけで動作する簡単なゲームも作れるわけです。

ミニゲーム内のソースコードを覗いてみますと、
段ボール箱のスプライトに仕込んである背景画像はえらく縦に長い画像であることが分かります。

この一見滑らかに動いているスプライトの動きは、cssの
animation属性のstepsと、@keyframes属性のbackground-positionを上手く組み合わせて、24fpsのパラパラ動画で再現されているものです。

このパラメータの組み合わせを正確に計算しておかないとパラパラ動画のタイミングがズレて見えたりしますので、フレームサイズとステップ数とアニメーション間隔の計算方法を考えてみましょう。

先程のリンク先のpng画像は、サイズが
168x25760の大きさで、ユニットフレームが168x368で全70フレームに分解できます。

合同会社タコスキングダム|蛸壺の技術ブログ

このpng画像の構造を踏まえた上で、画像をクリック時に適用されるcssアニメーションスタイルのコード部分だけを抜き出すと以下になります。

            
            #box-1{
    display: block;
    left: 100px;
    bottom: 158px;
    .box-game-inner {
        position: absolute;
        display: block;
        width: 84px;
        height: 184px;
        overflow: hidden;
        background: transparent url('https://www.kuronekoyamato.co.jp/ytc/img/404error_miss01.png') no-repeat 0 0;
        background-size: auto;
        background-size: 84px 12880px;
    }
    #checkbox1:checked ~ .box-game-inner {
        animation: sprite-miss 3s steps(69) 0s forwards;
        @keyframes sprite-miss {
            0% {
                background-position: 0 0;
            }
            100% {
                background-position: 0 -12696px;
            }
        }
    }
}
        
まずクリック前の状態では、background-size: 84px 12880pxで元画像を半分のサイズで読み込み、ユニットフレームもその半分の84 x 184pxの領域が見えるようにしておきます。

そしてチェックボックスがcheckedの状態になると、最初のフレームからアニメーション間隔3秒をかけて最後のフレームが見えるようになるまで、つまり
70 - 1 = 69フレーム分をanimation属性に指定して動かします。(このときのアニメーションフレームレートは69 / 3 = 23 fps)

アニメーション設定と同時に
@keyframes sprite-missで、どの位置まで動かすかの設定も重要で、最後のフレーム(70番目)が見える位置で終了するように、その手前のフレーム(69番目)が見えなくなる位置(y = 184 - 12880 = -12696 px)が100%のキー位置になるように仕込む必要があります。

ということで、このアニメーションが正しく描画されるためには、

            
            + 画像全体のサイズ
+ フレームサイズ
+ 総フレーム数
+ フレームレート
+ (アニメーション終了時の)キー位置
        
が全てあらかじめ計算の上で設定することで、あたかもリアルで動いているように見せることが出来ていたわけです。


まとめ

今回は主にHtml&Cssゲーム作成で使える基礎的なテクニックを解説してみました。

見ての通りでCssのスタイリングだけでゲームを開発するのは機能がかなり簡単なプログラムに限定はされますが、HtmlとCssだけあればあとはアイデア次第で簡易ゲームが動かせる!ということに色々と面白い応用の可能性を感じます。

参考サイト

ヤマト運輸 - お探しのページは見つかりませんでした。

黒猫はどの箱に? ヤマト運輸の「404エラー」ページにキュンする人が続出…しかも結構むずかしい!

【CSS】スプライト画像をstepsでアニメーションさせる方法・作り方

記事を書いた人

記事の担当:taconocat

ナンデモ系エンジニア

主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。