このhabratopicでは、 ダイジェストZfort#30で言及されたデモMedia Query Marioの作成について説明します。
私のブラウザーに届くすべての新しい技術デモが大好きです。 人々が時々それらを組み合わせることは、彼らの複雑さと創造性で脳を爆破することができます。
10月中旬にWebDevConf 2012を訪れた後、良いカンファレンスがもたらす非常に崇高なインスピレーションを感じました。 ロンドンでの会議から戻って、Mozilla Dev Derbyについてのツイートに気づき、今でも刺激を受けて貢献することにしました。 その結果、メディアクエリ、CSS3アニメーション、およびHTML5オーディオを組み合わせたMedia Query Marioの技術的なデモができました。
どこから始めますか?
私は、私が最も実験したかった技術のリストによって導かれるアイデアについて考え始めました。 私はしばらくの間、CSSアニメーションに深く入り込んで、メディアクエリと組み合わせることは理にかなっているように思えました。 JavaScriptの代わりにアニメーションをCSSで制御するのは良いことです。
マリオ3が最初に思い浮かびました。 2次元の横スクロールゲームを手に入れたかったのですが、レトロゲームに夢中なので、すぐにマリオのことを考えました。 マリオに関するゲームだけに興味がない人は誰でも、マリオ3がこの役割の唯一の候補者であることを理解します(誰かが同意しない場合、これが最高の 2次元マリオであることを証明するための議論が常にあります)。
デモが公開された後、他の技術がうまく機能するのにCSSアニメーションを使用する理由を尋ねられました。 主な理由は、彼らが何ができるかを見たかったからです。 キャンバスとSVGのクールさを示す多くのデモがありますが、私のデモはCSSアニメーションを促進することを意図したものではありません。 私は彼らの現在の状態を評価し、彼らも彼ら自身のスコープを持っているという事実について会話を始めたかっただけです。
私は自分の実装ルールを1つだけに制限しました。可能な限りCSSを使用してアニメーション化します。 CSSを使用して何かを実行できる場合は、パフォーマンスと実装の複雑さに関係なく、CSSを使用して実行する必要があります。 パフォーマンスが期待されるものに、後で戻ります。
開始するには、任意のボタンをクリックしてください
私が最初に遭遇した問題の1つは、ユーザーがすべてを表示するための画面の幅を理解することでした。 これは、アニメーションのサイズを設計するときだけでなく、同時にレベルのどの部分をユーザーに表示するかを決定することも重要でした。 表示する作品が大きいほど、一度に必要なアニメーションが増えます。
マリオ3自体について少し考えた後、元のメニュー画面を使用して制御するのが理にかなっているように思えました。 彼はデモのダウンロードを担当するという事実に加えて、アニメーションを開始する前にユーザーがウィンドウのサイズを変更したことも確認します。 アニメーション開始ボタンを非表示にする条件で、このメディアリクエストを制御しました。
@media screen and (max-width: 320px), (min-width: 440px) { .startBtn { display:none; } }
アニメーション自体を計画するとき、可能な限りオリジナルのゲームプレイに合わせたいと思いました。 それを簡単にするために、レベルを繰り返すことができるペースで合格するビデオを見つけました。 このビデオは、必要なリソースとアニメーションの速度を計画し、さまざまな敵やパワーアップをどのようにアニメーション化するかを考えるのに役立ちました。
構造の計画が完了したら、リソースを取得するだけで済みました。 予想どおり、インターネット上のゲームから元の画像、スプライト、サウンドファイルを見つけるのは簡単でした。 NESMapsとMario Mayhemで、キャラクターとオブジェクトのレベルマップとスプライトをピックアップし、 The Mushroom Kingdomで適切なサウンドを見つけました。 写真を少し編集する必要がありましたが、それらはすべて同じように私が迅速かつ順調に始めることができました。
後でアニメーションに使用されたものは次のとおりです。
行こう!
そのため、この瞬間までに、私は思慮深いアイデアと適切なリソースを手元に用意し、それらをコード内で結合する準備がすでに整っていました。
まず、CSSアニメーションの仕様を検討しました。 いくつかのリソースが本当に役に立ちました。 MDN( Mozilla Developer Network )は優れた出発点であり、この場合も例外ではありませんでした。 また、 Peter 、 Chris 、 Davidによるこれらのすばらしい記事のいずれかを読むことをお勧めします。これらはすべて、このトピックの素晴らしい紹介です。
これらの記事の内容については詳しく説明しませんが、自分が使用した重要な特性に注目します。 簡潔にするために、すべてのプレフィックスを省略しますが、自分で何かを繰り返す場合、それらを忘れてはいけません。それらはさまざまなブラウザで正しく動作するために必要です。
ちょっとしたコメント:新しいCSS3機能を使用する場合、LESSやSASSなどのプリプロセッサを使用すると、人生が大幅に簡素化されます。その方向に目を向けることを強くお勧めします。 ミックスインを使用すると、ベンダープレフィックスが非表示になり、直接作業するコードから削除され、コードの混乱が回避され、CSSプロパティをグローバルに置換する際の時間を大幅に節約できます。
特定の手法を検討する前に、アニメーションは2つの主要な部分で構成されていることを理解する必要があります。アニメーションプロパティと関連するキーフレームです。
アニメーションのプロパティ
アニメーションは、いくつかの関連するプロパティを使用して作成できます。 私が使用した主なプロパティは次のとおりです。
// , animation-name: mario-jump; //, , animation-duration: 500ms; // animation-timing-function: ease-in-out; // , animation-delay: 0s; // animation-iteration-count: 1; // animation-fill-mode: forwards;
デモで
animation-fill-mode
プロパティを使用することは特に重要でした。これは、再生の最後に最終スタイルを適用するようにアニメーションに指示するためです。 これがないと、要素はアニメーションの前の状態に戻ります。
たとえば、0pxの初期位置から30pxだけオブジェクトの左境界を移動するときに
animation-fill-mode
設定されていない場合、アニメーションの後、位置0pxに戻ります。 また、fill-modeが
forwards
に設定されている場合、要素は最終位置に残ります。
キーフレーム
@keyframes
ルールを使用すると、アニメーションステップを指定できます。 最も基本的なレベルでは、このルールは次のように記述できます。
@keyframes mario-move { from { left:0px; } to { left:200px; } }
from
および
to
は、それぞれ
0%
および
100%
アニメーションのキーワードです。 より複雑な例を示すために、複数のプラットフォーム間でのマリオのジャンプをアニメーション化するコードを見てみましょう。
@keyframes mario-jump-sequence { 0% { bottom:30px; left: 445px; } 20% { bottom:171px; left: 520px; } 30% { bottom:138px; left: 544px; } 32% { bottom:138px; left: 544px; } 47% { bottom:228px; left: 550px; } 62% { bottom:138px; left: 550px; } 64% { bottom:138px; left: 550px; } 76% { bottom:233px; left: 580px; } 80% { bottom:253px; left: 590px; } 84% { bottom:273px; left: 585px; } 90% { bottom:293px; left: 570px; } 100% { bottom:293px; left: 570px; } }
上記のアニメーションが1秒続く場合、マリオは
bottom: 30px; left: 445px;
位置から移動し
bottom: 30px; left: 445px;
bottom: 30px; left: 445px;
0秒(0%アニメーション)から
bottom: 138px; left: 520px;
bottom: 138px; left: 520px;
最初の200ミリ秒(または20%)で。 など、最初のキーフレームから最後のキーフレームまで。
アクションアニメーション
デモのアニメーションは、タイプに応じて3つのカテゴリに分類できます。
- マリオをジャンプしたり、質問から箱から飛び出してくるコインなどの動き 。
- スプライト管理 、つまり 文字とオブジェクトの背景画像を制御します。
- 数ミリ秒または数秒間繰り返す必要があるアニメーションをループします。
引越し
移動は、すべてのデモアニメーションの約75%を占めます。 たとえば、キャラクターの動き(走ったりジャンプしたり)、パワーアップの外観、質問でボックスを押すことです。 移動アニメーションの違いは、
animation-timing-function
、
animation-duration
animation-delay
プロパティによって設定されます。
animation-timing-function
プロパティは、アニメーションの継続時間全体にわたってアニメーションの速度を制御するのに役立ちます。 可能な場合は、イーズインや
ease-in-out
などのイージング機能を使用しました。そうしないと、アニメーションの各ステップを非常に詳細に説明する必要があります。 しかし、これで目的の効果が得られない場合は、
animation-timing-function
線形にし、キーフレームを使用して必要な結果を達成しました。
モーションアニメーションの例はここにあります 。
スプライト
文字とオブジェクトの
background-position
を制御するために、
step-end
機能を使用しました。
.mario { animation-timing-function: step-end; ... }
最初は、要素のクラスを追加および削除してスプライトを制御するためにJavaScriptを使用する必要があると考えました。 しかし、
step-end
内部構造を試した後、完璧な組み合わせが見つかりました。
これを実際に見るには、 マリオの動きとマッシュルームを食べた後のマリオの変容 の簡単なアニメーションを見てください。
ただし、この方法で
step-end
を使用してもそれほど苦痛はありません。 驚いたことに、Webkitに不具合が見つかりました。この不具合は、複数のメディアクエリの交差点で現れ、アニメーションのレンダリングが予想とは異なっていました。 このようなCSSアニメーションの適用は、ブラウザのレンダリングエンジンの境界線のケースですが、実際にはChromiumのバグであり、今後修正されることを願っています。
ループ
アニメーションがしばらく継続するたびに、ループは
animation-iteration-count
設定によって設定されました。
// 5 animation-iteration-count: 5; // animation-iteration-count: infinite;
デモからのこの例は、火の玉の回転です 。
したがって、デモ全体は3種類のアニメーションから構築されました。 最後のステージは声優でした。
声優
以前に必要なすべてのサウンドファイルを
.wav
形式でダウンロードしましたが、それらをHTML5オーディオで利用可能な形式
.ogg
および
.mp3
に変換する必要がありました。 これにはSwitch Audio Convertor (Mac)を使用しましたが、他のソフトウェアは問題なく動作します。
ファイルを変換した後、どの形式でブラウザに渡すことができるかを判断する必要がありました。 これを行うには、JSのいくつかの行を使用する必要がありました。
var audio = new Audio(); // var canPlayOgg = !!audio.canPlayType && audio.canPlayType('audio/ogg; codecs="vorbis"') !== ""; var canPlayMP3 = !!audio.canPlayType && audio.canPlayType('audio/mp3') !== "";
次に、各サウンドにいくつかのデフォルトパラメータを設定する関数を作成し、ファイル名に適切な拡張子を追加しました。
// function createAudio (audioFile, loopSet) { var tempAudio = new Audio(); var audioExt; //, if (canPlayMP3) { audioExt = '.mp3'; } else if (canPlayOgg) { audioExt = '.ogg'; } tempAudio.setAttribute('src', audioFile + audioExt); //set the source file tempAudio.preload = 'auto'; // //, ( ) tempAudio.loop = (loopSet === true ? true : false); return tempAudio; } var audioMarioJump = createAudio("soundboard/smb3_jump"); //
あとは、アニメーションと同期して適切なタイミングでサウンドを再生するだけです。
animationstart
および
animationend
イベント(またはWebKit、
webkitAnimationStart
および
webkitAnimationEnd
)をリッスンするには、JSが必要でした。 それで、アニメーションの始まりと終わりの瞬間を捉え、状況に適した音を再生することを学びました。
イベントハンドラーが発生すると、
animationName
プロパティにアクセスできます。これは識別子として使用できます。
mario.addEventListener('animationstart', marioEventListener); function marioEventListener(e) { if (e.animationName === 'mario-jump') { audioMarioJump.play(); } }
1つの要素に対して複数の
animationstart
イベントがある場合(デモのマリオなど)、
switch
コンストラクトを使用して
animationName
を処理できます。
このデモを書いてから、
Joe Lambert
Keyframe Event JS shim
を使用すると、アニメーションの個々のキーフレームに集中できるので、さらに制御できることがわかりました。
ゲームの終わり
このデモについては、想像もしていなかったほど肯定的なレビューを受けました。 他のハック( hacks.mozilla.orgからの投稿、約Translator )と同様に、まだやるべきことがありますが、学んだことをすべて次のプロジェクトに入れることがより重要だと思います。 デモは、CSSアニメーションを使用して、非常に単純なコードを記述することで印象的な効果を作成できることを示したと思います。
複雑なCSSアニメーションはすでに優れたパフォーマンスを発揮できますが、作成は依然として非常に面倒です。 もちろん、Adobe Edge AnimateやSencha Animatorなど、タスクを簡素化するツールもありますが、JSでラップされたCSSアニメーションを生成します。 私にとっては、これは失敗です。CSSアニメーションの力は、他の技術に依存せずに動作できるという事実にあるからです。 それらを手動で記述する必要があるかどうかはわかりませんが、誰かがこのようなことを突然知ったら、コメントでそれを知りたいです。
キャンバスとSVGを使用してCSSアニメーションを比較した以前のコメントに戻りますが、アニメーション用のテクノロジーを選択する際には、それらすべてが価値ある候補であると思います。 それにもかかわらず、このような複雑なアニメーションを作成するための時間の障壁が早くなるほど、それらのアプリケーションに適した適切な場所が日常業務で開かれます。
はい、マリオが対角線上で宇宙に飛び込むところでアニメーションは終了します。 これはバグではなく、単純な欠陥です。