TriggerHappyButton トリガーハッピーボタン
この度、JavaScript,HTML5,CSSを書いて共有するサイトjsdo.itのJAM Session3 「お題:思わず押したくなるボタンをつくってください」に応募してみました。
(以下のiframe内のPlayを押すと本作品が見られます。)
TriggerHappyButton トリガーハッピーボタン - jsdo.it - share JavaScript, HTML5 and CSS
さて、皆さんは"Trigger Happy トリガーハッピー"という言葉をご存知でしょうか?
ここでの"Trigger"は銃の引き金を指し、"Trigger Happy"はすぐに(銃を)撃ちたがる人、というような意味になります。
今回の作品"TriggerHappyButton トリガーハッピーボタン"は、その名のとおり、ただひたすら画面いっぱいに銃弾を撃ちまくるボタン。
発射モード・口径も自由に選択できる優れもの(?)で銃の反動も再現しています。
また、デザイナーが趣味全開で作ったものなのでかなりグラフィックに凝っている点も特徴のひとつ(笑)。
それでは、そのメイキングを簡単にご紹介しましょう。
主な構成要素
"TriggerHappyButton トリガーハッピーボタン"の主な構成を以下に示します。
(順番はDOMツリー上のそれとは無関係)
- 1. 背景(<canvas>(1))
- 2. 操作パネル【塗装あり】(<canvas>(1))
- 3. 操作パネル【塗装なし】(CSS)
- 4. 発射モードセレクター(<canvas>(2)・CSS)
- 5. 口径調整スライダー(CSS)
- 6. ボタン(CSS)
- 7. デジタル表示(CSS)
- 8. 弾痕(<canvas>(1))
- 9. 壁の後ろのjsdo.it(<iframe>)
以降、それぞれについて簡単に説明します。
背景の作成
まずは背景画像の作成です。
廃墟の壁のようなイメージにしたかったため、適当なフリー素材を複数用意し、描画モードの変更や色調補正などを加え、重ね合わせて作成しています。
イメージ通りに出来上がったら、リピートした際に切れ目が目立たないよう調整し、DATA-URIに変換します。
背景の描画
以下のhtmlを用意します。
簡単のため画面サイズは400px × 400pxとします。
<canvas>は複数使用するので"wall"というIDを付与しておきます。
(※以下のコードは説明用のため、作品のものとは一部異なります。)
<body> <canvas id="wall" width="400" height="400"></canvas> </body>
<canvas>で画像を扱う際には、画像の読み込み完了のタイミングを知る必要があります。
画像が一つなら簡単ですが、複数となるとちょっと面倒ですね。
そのため、本作品中では複数画像の読み込み機能を提供するクラスImageLoaderクラスを作成し使用しています。
なお、ImageLoaderクラスは別途作成したカスタムイベントを扱うクラスEventDispatcherの派生クラスとしています。
ImageLoaderクラスの使用例を以下に示します。
//ImageLoaderクラスのインスタンスを作成 var loader = new ImageLoader(); //読み込む画像のURIを設定(addメソッドはImageクラスのインスタンスを生成し返します) loader.add("hoge1.jpg"); loader.add("hoge2.png"); //読み込み完了時イベントリスナを追加 loader.addEventListener(ImageLoader.COMPLETE, function(evt){ // // 読み込み完了時の処理 // }); //タイムアウト時イベントリスナを追加 loader.addEventListener(ImageLoader.TIMEOUT, function(evt){ // // タイムアウト時の処理 // }); //画像読み込み開始 loader.start();
このImageLoaderを使用し、背景画像の描画を行います。
サンプルコードを以下に示します。
(※以下のコードは説明用のため、作品のものとは一部異なります。)
//ImageLoaderクラスのインスタンスを作成 var loader = new ImageLoader(); //読み込む画像のURIを設定 var BACKGROUND = loader.add("背景画像のDATA URIが入ります"); //読み込み完了時イベントリスナを追加 loader.addEventListener(ImageLoader.COMPLETE, function(evt){ //キャンバスタグの描画コンテキストを取得 var ctxWall = document.querySelector("#wall").getContext("2d"); //塗りつぶしパターンを作成&設定 ctxWall.fillStyle = ctxWall.createPattern(BACKGROUND, "repeat"); //塗りつぶし ctxWall.fillRect(0, 0, 400, 400); }); //画像読み込み開始 loader.start();
これで背景の描画は終わり!!簡単ですね。
実際の作品ではこれに加え、複数の円形グラデーションを描画しさらに質感を高めています。
操作パネルの作成
こちらもまずはPhotoshopでデザインを作成していきましょう。
(以下の図ではわかりやすくするため色をつけています。)
【図:左上】まずはスマートオブジェクトで角丸の短径を作成します。
【図:右上】作成したスマートオブジェクトを複製し、ねじ止め用の「ざぐり」を追加します。
【図:左下】さらに4隅のネジ、デジタル表示部、口径調整スライダのベースなどを追加します
【図:右下】大まかなパーツが出来上がったらレイヤー効果を駆使し、質感を高めましょう。
最後に発射モードや口径の刻印を追加して完成。こちらもDAT-URIに変換しておきましょう。
操作パネルの作成
操作パネルも先ほどと同様ImageLoaderクラスを使用し、画像読み込み完了後に描画を行います。
操作パネルのサイズは320px × 200pxとしたのでちょうど真ん中に来るよう(40, 100)に描画します。
ctxWall.drawImage(PANEL, 40, 100);//PANELはImageクラスのインスタンス
実際の作品ではドロップシャドウを加え、さらに質感を高めています。
さらにcanvasタグの背景画像としてこんな画像(↓↓)を作成し仕込んでおきます。
操作パネルに弾があたると塗装がはげるギミックです。
デジタル表示
デジタル表示はcssとJavaScriptによるDOM操作で行っています。
これはcanvas上に描画するよりも効率がいいと判断したためです。
以下のような画像をPhotoshopで作成しDATA-URIで準備しておきます。
なお、高速化のため数字部分は最大2ケタずつの表示としています。
表示内容が512の場合に、JavaScriptにより動的に作成されるHTMLを以下に示します。
<div id="count"> <p class="d d51"></p> <p class="d d2"></p> </div>
発射モードセレクター・口径調節用スライダー・発射ボタンの表示
これらもまずはスマートオブジェクトで形を作成し、レイヤー効果を駆使して質感を高めてから、DATA-URIに変換します。
表示に関しては、「発射モードセレクター」のみが<canvas>、一方「口径調節用スライダー」・「発射ボタン」は<a>タグ/CSSとなっています。
「発射モードセレクター」の表示のみ<canvas>で行っている理由は、モード変更に伴い回転させる必要がある&ドロップシャドウをかけるためです。
なお、「発射モードセレクター」のつまみ部分にはマウスイベント取得用に透明な<a>タグをこっそり設置してあります。
発射モードセレクター・口径調節用スライダーの動作
縦方向のスライダを扱うクラス、VSliderを作成しその位置と値を制御・管理しています。
VSliderクラスはImageLoaderクラスと同様、カスタムイベントを扱うEventDispatcherクラスを継承しています。
VSliderクラスの使用例を以下に示します。
//VSliderクラスのインスタンスを作成 var vslider = new VSlider( elm, //DOM要素 0, //y座標(top)最小値 50, //y座標(top)最大値 5 //メモリの数 ); //値変化時のイベントリスナを追加 vslider.addEventListener(VSlider.CHANGE, function(evt){ // // 値が変化した際の処理 // }); //初期化 vslider.init(3);
なお、initメソッドは(開始時のアニメーショーン用に)急きょ作成したものなのでprototypeではなくコンストラクタ内でthis.init = function(){};で追加してしまいました。
後日prototypeに移したいと思います…。
弾痕
さて次は、弾痕の作成です。これは本作品の大きなポイントですね。
弾痕の描画は背景画像と操作パネルを描画した<canvas>に対し行います。
用意する画像は弾痕の穴の部分とその周りの亀裂画像の2点。
Photoshopで素材を作成しDATA-URIに変換しておきます。
弾痕の描画は大きく2つのプロセスで行っています。
1. 穴をあける(穴の部分を透明にする)
2. 亀裂を描画する
弾痕描画用のサンプルコードを以下に示します。
なお、HOLEは穴の画像、CRACKは亀裂画像とします(どちらもImageクラスのインスタンス)
(※以下のコードは説明用のため、作品のものとは一部異なります。)
//現在のセッティングを保存(これから座標を移動・回転させるため) ctxWall.save(); //座標を移動(x, yには弾痕の中心座標が入る) ctxWall.translate(x, y); //描画モードを指定(重なった部分を削除) ctxWall.globalCompositeOperation = "destination-out"; //弾痕がランダムに見えるよう座標を回転 ctxWall.rotate( Math.random() * Math.PI * 2 ); //穴をあける ctxWall.drawImage(HOLE, -HOLE.width / 2, -HOLE.height / 2); //亀裂用に描画モードを変更(重なった部分のみ描画) ctxWall.globalCompositeOperation = "source-atop"; //こちらもランダムに見えるよう座標を回転 ctxWall.rotate( Math.random() * Math.PI * 2 ); //亀裂を描画 ctxWall.drawImage(CRACK, -CRACK.width / 2, -CRACK.height / 2); //保存した状態を復帰(座標の移動・回転をリセット) ctxWall.restore();
この座標の移動と回転がキャンバスの特徴の一つですね。CSS3やActionScriptとは概念が異なるため、最初は理解しずらいかもしれません。
なお、作品中では口径に合わせて画像を縮小し、描画しています。
壁の後ろのjsdo.it
壁の後ろにはjsdo.itのトップページを<iframe>で画面いっぱい表示しています。
この<iframe>は、体感速度向上のため、背景・操作パネルの描画といった初期化作業完了後に動的に生成・挿入しています。
このように射撃を続けるとjsdo.itのトップページが現れる仕組み。
<canvas>だから可能なギミックですね。
最後に
さて、今回の作品"TriggerHappyButton トリガーハッピーボタン"気に入っていただけたでしょうか?
時間の関係で今回は音なしとなってしまいましたが、機会があれば追加してみたいです。
また、今回の作品で初めて<canvas>を扱ったのですが、非常に面白いと感じました。
工夫次第でいろんなことができそうですね。