最近、会社のモバイルプロジェクトで、ランニングトラックを走る人のアニメーションがありました。モバイルデバイスでより良いパフォーマンスを発揮するために、すべてCSS3でアニメーションを作成することにしました。 初めての試みだったので、多くの問題に遭遇しました。ここに記録しておきます。
完成イメージは次のとおりです。
分析
完成イメージを見て、まず分析してみましょう。アニメーションは以下の4つの部分に分けられます。
- 走る人
- 動く道
- 左側の道路標識
- 収益の看板
走る人
走る人は簡単に解決できます。ランニングの分解画像を使用し、
background-position
をフレームごとに変更するだけで済みます。本当に簡単ですね。
.person {
width: 62px;
height: 110px;
background: url(../imgs/plan/person1.png) -7px 0 no-repeat;
background-size: auto 110px;
animation: run .5s infinite; // 各ブラウザのプレフィックス、特に-webkit-を追加することに注意してください。ここでは省略します。
position: absolute;
top: 40%;
left: 110*.84px;
}
@keyframes run {
0% {
background-position: -7px 0
}
25% {
background-position: -79px 0
}
50% {
background-position: -150px 0
}
75% {
background-position: -216px 0
}
100% {
background-position: -283px 0
}
}
しかし、走る人はこのようになってしまいました。
見るに堪えません。原因を分析したところ、
keyframes
がデフォルトでトランジションを持っているためでした。つまり、background-position
が-7から-283まで線形にグラデーションしていたのです。これはもちろん私が望む結果ではありません。これはアニメーションタイミング関数の問題だと考え、animation-timing-function
の値を調べました。
linear: 線形トランジション。ベジェ曲線(0.0, 0.0, 1.0, 1.0)と同等。 ease: 滑らかなトランジション。ベジェ曲線(0.25, 0.1, 0.25, 1.0)と同等。 ease-in: ゆっくり始まり、速くなる。ベジェ曲線(0.42, 0, 1.0, 1.0)と同等。 ease-out: 速く始まり、ゆっくりになる。ベジェ曲線(0, 0, 0.58, 1.0)と同等。 ease-in-out: ゆっくり始まり、速くなり、またゆっくりになる。ベジェ曲線(0.42, 0, 0.58, 1.0)と同等。 step-start:
steps(1, start)
と同等。 step-end:steps(1, end)
と同等。 steps([, [ start | end ] ]?): 2つの引数を受け取るステップ関数。最初の引数は正の整数でなければならず、関数のステップ数を指定します。2番目の引数はstart
またはend
のいずれかの値を取り、各ステップの値が変化する時点を指定します。2番目の引数はオプションで、デフォルト値はend
です。 cubic-bezier(, , , ): 特定のベジェ曲線タイプ。4つの数値は[0, 1]の範囲内である必要があります。
step
というプロパティこそが私たちが求めていたものです。これにより、ある状態から別の状態へ直接ジャンプさせることができ、トランジションは発生しません。上記の.person
にこのプロパティを追加するだけで済みます。
animation-timing-function: step-start;
これで走る人の問題は解決しました。
道
道については、最初は良い方法が思いつきませんでした。当初は、大きなセクションを直接切り取り、2枚の画像を作成して、それらを交互に表示することを考えていました。しかし、これでは画像が非常に大きくなり、ネットワークリソースを消費するだけでなく、非常に非効率な方法だと感じました。ランニングトラックを見て分析した結果、良い方法を思いつきました。
ランニングトラックの最小構成要素を切り取り、縦方向に繰り返し配置し、
background-position
を画像の高さの半分ずつ移動させるだけで済みます。
これで道の問題も解決しました。
左側の道路標識
これは非常に簡単で、絶対位置を設定し、top
値を変更するだけで済みます。
注意すべき点は、最後の標識が中央に到達した後に停止することです。これには、このプロパティを追加するだけで対応できます。
animation-fill-mode: forwards;
animation-fill-mode
プロパティは、アニメーションが再生される前または後に、そのアニメーション効果が表示されるかどうかを規定します。 値 none: デフォルトの動作を変更しません。 forwards: アニメーションが完了した後、最後のプロパティ値(最後のキーフレームで定義されたもの)を保持します。 backwards:animation-delay
で指定された期間中、アニメーションが表示される前に、開始プロパティ値(最初のキーフレームで定義されたもの)を適用します。 both: 前方と後方の両方のフィルモードが適用されます。
収益の看板
左側の道路標識と同様に、非常に簡単です。ただ現れて消えるだけです。
組み合わせ
アニメーションの4つの部分を解決した後、残りはそれらを組み合わせることです。
上の図は、最初の道路標識が画面に現れてから消えるまでのプロセスフローチャートです。他の2つの道路標識のプロセスは、アニメーションを対応する時間だけ遅延させるだけで済みます。
この間に、もう一つ問題がありました。それは、走る人と道を特定の場所で停止させることです。
これはCSS3では実現できないようだったので、次善の策としてJavaScriptで制御することにしました。
webkit-animation-play-state: paused
とwebkit-animation-play-state: running
というプロパティを設定することで、停止後に再び動かすことができます。
最後に、アニメーションが完了した後にポップアップを表示する必要があります。これはanimationEnd
イベントを検出することで実現します。以下にその実装方法を示します。
/* From Modernizr */
function whichAnimationEvent(){
var t;
var el = document.createElement('fakeelement');
var animations = {
'animation':'animationend',
'OAnimation':'oAnimationEnd',
'MozAnimation':'animationend',
'WebkitAnimation':'webkitAnimationEnd'
}
for(t in animations){
if( el.style[t] !== undefined ){
return animations[t];
}
}
}
/* Listen for a transition! */
var transitionEvent = whichAnimationEvent();
whichAnimationEvent && e.addEventListener(whichAnimationEvent, function() {
//doSomething
});
/*
The "whichTransitionEvent" can be swapped for "animation" instead of "transition" texts, as can the usage :)
*/
効果
これが最終的な完成効果です。 こちらをクリックしてアニメーションを見る
この記事は 2015年5月21日 に公開され、2015年5月21日 に最終更新されました。3791 日が経過しており、内容が古くなっている可能性があります。