本記事ではJulian Shapiroにより開発された高速かつ強力なJavaScriptアニメーションエンジン、Velocity.js(以降、Velocity)を紹介します。記事のコードやデモすべてに目を通せば、Velocityを使って自分のアニメーションが作れるようになり、サイトはさらにインタラクティブで使いやすいものになるでしょう。jQueryには頼らず、すべて素のJavaScriptだけで作ります。
この記事は『CSSライクでデザイナーに優しい!anime.jsはDOMアニメーションの新定番だ!』『HTMLもSVGもテキストも自在!DOMアニメーションの決定版「KUTE.js」が登場』の続編です。
以前掲載した2つの記事では、以下のようなことを取り上げました。
『CSSライクでデザイナーに優しい!anime.jsはDOMアニメーションの新定番だ!』では、サイトにアニメーションを使うことがいかにすばらしいか、どのような場合にCSSのみのアニメではなくアニメーションライブラリーの使用を検討すべきかについて触れ、なかでも無料で軽量なAnime.jsに焦点を当てました。
『HTMLもSVGもテキストも自在!DOMアニメーションの決定版「KUTE.js」が登場』では、無料で高機能なアニメーションライブラリーKUTE.jsを紹介しました。
Velocityでアニメーション効果を付けられるもの
Velocityは強力なライブラリーで、DOM操作を手中にできます。
- 色を含めたCSSアニメーションプロパティ数値の変更
- 外観の変形
- SVGのプロパティ
- スクロールイベント、ページとの相対位置でもページ内のコンテナ要素でも指定可能
- フェード効果やスライド効果
一般的にVelocityでは、プロパティの数値を一度に1つずつ指定してアニメ―ション効果を付けます。例を挙げると、ある要素をX軸方向とY軸方向ともに移動(translate)させたいときに、たとえば、translate['10px', '15px']のような書き方はできません。そうではなくtranslateプロパティに指定する軸を付けてtranslateX: '10px', translateY: '15px'のように書くことで、複数の数値を同時に指定できます。またVelocityにはフォースフィーディング(forcefeeding、訳注:強制的に食べさせること)と呼ばれる機能があり、2つの値を同時に指定できます。この機能については記事の後半で紹介します。
オプション
Velocityではたくさんのオプションによって多彩なアニメーションが作れます。
以下は記事中のデモで使われているオプションのリストです。
- 持続時間(duration):アニメーションの継続時間で、単位はミリ秒
- イージング(easing、訳注:加減速などの緩急をつける):VelocityではjQueryのイージングの大半をサポート。ease-in、ease-out、ease-in-out、ベジェ曲線、ステップイージング、さらにバネ効果もある。実例はデモを参照
- 繰り返し(loop):アニメーションを何回繰り返し再生するかを設定。trueに設定すると無限に繰り返す
- 遅延(delay):アニメーションの開始をどれだけ遅らせるか
全オプションのリストはVelocityのドキュメントに掲載されています。
構文
jQueryを使っているなら、Velocityも分かりやすいはずです。実際VelocityはjQueryと同じAPIです。
最初に、Velocityをダウンロードしてincludeで自分のページに読み込みます。そしてjQueryの$.animate()のインスタンスをすべて$.velocity()と入れ替えます。こちらのドキュメントを参照してください。
Velocityを使うのにjQueryは不要で、記事中のデモでも使用しません。構文はjQueryを使う場合と比べて少し異なります。Velocityの構文は次のような感じです。
Velocity(element, {property: value}, {option: optionValue});
同じ要素に対して別のアニメーション効果を連続実行するには、単純にあとに続けてVelocityをもう1つ呼び出すだけです。
Velocity(element1, {property: value}, {option: optionValue});
Velocity(element1, {property: value}, {option: optionValue});
複数の要素に同時にアニメーションを付けるには、単純に各要素をすべて変数に格納して、変数に対してVelocityを実行します。自分でループを書く必要はありません。たとえば、次のようになります。
const elements = document.querySelectorAll('<div>');
Velocity(elements, {property: value}, {option: optionValue});
指定はpx、%、rem、em、vw/vh、degを使います。なにも単位を書かない場合、Velocityが解釈して通常はpxになります。
Velocityは演算子+、 –、*、/、さらに、たとえば'+=3em'のように等号(=)を付けた複合代入演算子も使えます。
Velocityの「フォースフィーディング」:初期値を渡す
次の構文のように、VelocityはDOMから要素の初期値を取得せず、自ら初期値を設定できます。
Velocity(element, {
translateX: [endValue, startValue],
backgroundColor: [endValue, easing, startValue]
}, {
//options here
});
この場合は、2つまたは場合により3つの値を入れた配列を渡します。
- 最初の値はアニメーション後の最終的な値(位置)
- 2番目の値はオプションで、プロパティに対して適用するイージング効果を指定
- 配列の最後の値は遷移アニメの初期状態で、アニメーション開始時の特定プロパティの値を明示的にセット
フォースフィーディングについて、詳しく知りたい場合は、Velocityのドキュメントを参照してください。
Velocityのアニメーションを操作する
次の構文で、要素に対するVelocityの実行はすべて中断、停止、再開できます。
- 停止:Velocity(elem, 'stop');
- 一時停止:Velocity(elem, 'pause');
- 逆向きに再生:Velocity(elem, 'reverse');
- 再開:Velocity(elem, 'resume');
基本的な命令を理解したら、いよいよ少し実用的な使い方をしていきます。
デモ:落ちるボール
シンプルな、ページの上部から落ちるボールから始めます。
const ball = document.querySelector('.ball');
Velocity(ball, {
translateY: '130px'
}, {
easing: [1000, 20],
duration: 2000
});
上のコードでは.ballクラスでHTML要素を選択し、2秒間でY軸方向に130px移動させています(2秒間というのはデモ用で、通常はもっと短い持続時間がおすすめ)。なおかつ、落ちるごとに加速して、最後には小さくバウンドするように揺れます。
この動きは、先に説明したイージングオプションの配列の値としてバネ効果(spring physics)を選べば簡単です。配列の最初の値は強さ(tension)、2つ目の値は摩擦(friction)を表します。強さを高い値(high tension)にすると全体の速度とバウンドの強さが増し(初期値は500)、低い摩擦(lower friction)にすれば、最後に振動する速度が速まります(初期値は20)。
ちょっとした遊びとして、ボールの背景色を初期の青みがかかった色から暗い色にしてみます。背景色の初期値をセットするには、先に説明したフォースフィーディングを使います。
Velocity(ball, {
translateY: '130px',
//array items: endValue, startValue
backgroundColor : ['#222', '#043d99']
}, {
//options here
});
Velocityの基本的な実装方法はこのようになります。CodePenでいろいろ試してみてください。
デモ:ボタンで操作できる、バウンドするボール
次のデモのゴールは、以下の連続アニメーションを作ることです。
- ボールがページ上端から落ちる
- ボールが地面(ページ下部)に当たると少し潰れる
- バウンドして跳ね上がる際にボールの形が元に戻る
- アニメーションは繰り返し再生される
- ボタンをクリックすればアニメーションの連続再生を停止・再開できる
このようなアニメーションは、いくつもの遷移をつなげて、全体をボタンでコントロールする必要があります。
理想的なツールは、すべての遷移アニメを包含し、開始と終了のタイミングをコントロールできるタイムラインです。Velocityは標準でタイムラインの機能はありませんが、選択肢はあります。
- Tweeneを使う:アニメーションプロクシで、Velocityを含むさまざまなJavaScriptアニメーションライブラリーで使えるラッパー。以前、試したが、残念ながらTweeneはVelocityがjQueryと一緒に使われていることが想定されていて、jQueryを使わないこの記事では適用できない。Tweeneのコードを改変すればうまくいくかもしれないが、理想的ではない
- JavaScriptのrequestAnimationFrame()を使う:執筆時点ではOpera Miniを除くすべての主要ブラウザーでサポートされている。CSSやcanvasなど、ブラウザー環境で滑らかで高効率なアニメーションを実現するネイティブAPIで、今回のような機能が欲しいときに使う
遷移アニメーションを機能ごとに分割する
コードをきれいに保つため、1つ1つのアニメーションに応じた関数を作る方法があります。requestAnimationFrame()メソッドを持つ関数(記事ではマスター関数と呼びます)で作成した関数を呼び出します。
次がそのコードです。
const changeBallPosition = (elem, propVal, easingVal, durationVal) => {
Velocity(elem, {
translateY: propVal
}, {
easing: easingVal,
duration: durationVal
});
};
const changeBallWidth = (elem, propVal, easingVal, durationVal) => {
Velocity(elem, {
scaleX: propVal
}, {
easing: easingVal,
duration: durationVal
});
};
上のコードにはES6のアロー関数の書き方の実例も掲載しています。functionキーワードを書かずに、括弧の後ろに太い矢印記号(=>)を付けます。
この関数はconstキーワードによる定数に格納されます。簡単に言えば定数なので値を更新できないということです。もしプログラム中で更新したい値を格納するなら代わりにletを使用してください。さらに詳しくは、Wes Bosの『ES6 let VS const variables(ES6のletとconstの比較)』が役に立つでしょう。
見ての通り、各関数にはVelocityの呼び出しがあり、ボールに動きを付けています。ボールを移動させたり、幅を変更したりするには、CSSのtopやwidthといったプロパティの値は変えていない点に注意してください。そうではなくtranslateやscaleプロパティの値を変えるようにすれば、さらに効率的なアニメーションが実現します。
次がタイマーがあるマスター関数です。上の関数はここから呼び出します。
let animationFrameID;
const launchBall = (elem) => {
changeBallPosition(elem, '130px', 'easeInQuart', 300);
changeBallWidth(elem, 1.2, 'linear', 50);
changeBallWidth(elem, 1, 'linear', 50);
changeBallPosition(elem, '-10px', 'easeOutQuart', 300);
changeBallWidth(elem, 1, 'linear', 50);
animationFrameID = requestAnimationFrame(
() => {
launchBall(elem);
});
};
グローバル変数のanimationFrameIDに注意してください。あとでこのコードから呼び出すcancelAnimationFrame()関数を使って、アニメーションを停止する際に必要な変数ですから、このままにしておいてください。
ボールを動かすには、PlayボタンのクリックイベントでlaunchBall()関数を呼び出してこれに引数ballを渡します。
btnPlay.addEventListener('click', function() {
launchBall(ball);
this.disabled = true;
btnStop.disabled = false;
});
クリックイベントを扱うコールバックを書くのに、今度はfunctionキーワードを使ったことに注目してください。その理由は、this.disabled = true;のように、クリックされたボタンを参照するためにthisキーワードを使うからです。もしここでアロー関数を使ったら、thisキーワードはグローバルのwindowオブジェクトを示すことになり、エラーになって意図した動作になりません。つまり動的なコンテクストのコールバック関数ではアロー関数は使うな、ということです。
いったんアニメーションが始まったら、ユーザーがPlayボタンをクリックし続けなくてよいように、次のコードでこのボタンを無効化し、さらにいつでもアニメーションを停止できるようにStopボタンを有効化します。
ボールを停止するには新しい関数が必要です。
const removeAnimFrame = () => {
if (animationFrameID) {
cancelAnimationFrame(animationFrameID);
}
}
ここではcancelAnimationFrame()関数を実行しますが、この関数に対してボールの連続アニメーションの参照用IDとして先ほど設定したanimationFrameIDを渡しています。
最後に、Stopボタンのクリックイベントを扱う処理です。
btnStop.addEventListener('click', function() {
removeAnimFrame();
Velocity(ball, 'stop', true);
this.disabled = true;
btnPlay.disabled = false;
});
上のコードを簡単に説明します。
- ループ実行中のアニメーションをはずす
- アニメーションを停止するためVelocity(ball, 'stop', true);を実行
- Playボタンを無効化
- Stopボタンを再度有効化
興味深いのはVelocityのstop()メソッドを、ブーリアン型の値(trueまたはfalse)の引数付きで呼び出していることです。アニメーション実行キューをクリアするにはこのようコードを書きます。もしこの引数を取り除いてStopボタンをクリックしても、ボールのアニメーションはすぐに止まらず、キューにあるすべてのVelocityコールを実行してからやっと停まるでしょう。Velocityの「Stopセクション内の、アニメーションのクリア方法」のドキュメントを参考にしてください。
コード全体を確認するため以下のデモを参照してください。
スクロールアニメーション
Velocityでは縦横どちらの方向でもスクロールするアニメーションを手早く実装できます。ページ全体に対するスクロールと、ある要素に対するスクロールがあります。どちらであっても、スクロールさせた結果を表示したい要素に対しVelocityをコールします。
下のデモには2つのリンクがあります。上のリンクをクリックすればコンテナを最後の段落までスクロールし、下のリンクをクリックすればコンテナを最初の段落までスクロールします。
スクロール動作はどちらも同じなので、同じコードを2度書かずに済むように関数にまとめます。下がその内容です。
const scrolling = (element, container, direction) => {
let offsetDistance = 0;
direction === 'up' ? offsetDistance = -200 : 200;
//velocity call
Velocity(element, 'scroll', {
container: container,
duration: 500,
offset: offsetDistance,
easing: 'ease-out'
});
};
- element引数は、スクロールした結果表示させたい要素が入る。スクロールの方向次第で、最初または最後の段落が該当する
- スクロール方向はdirectionパラメーターで指定し、その動作は三項演算子(ternary operator)により決まる。具体的には、もしdirectionの値が「up」であれば(direction === 'up' ?)、offsetDistanceの値が-200pxに決まり、ページはスクロールの結果表示させる要素の200ピクセル上(offsetDistance = -200)までスクロールする。「up」以外の場合であればoffsetDistanceの値は200px(: 200)となり、ページはスクロールの結果表示させる要素の200ピクセル下までスクロールする。このようにoffsetDistanceパラメーターにはVelocityのoffsetオプションプロパティの値が入り、最終的なスクロール位置を(対象要素からの)ピクセル単位の距離で調整できる
- デモのスクロールの基準はブラウザーウィンドウに対する位置ではなくページの要素に対する位置なので、Velocityの実行時に要素の情報が必要で、引数containerで指定している。対象要素のCSSのpositionプロパティはstaticだと動作せず、relative、absolute、fixedのいずれかにする
最後に、リンクをクリックした際のイベントハンドラを作り上の関数を呼び出します。たとえば、最後の段落までスクロールするには以下のようなイベントハンドラになります。
Link.addEventListener('click', (e) => {
e.preventDefault();
scrolling(lastSection, scrollerContainer, 'down');
});
こちらが全体のコードです。
SVGのアニメーション
Velocityは1つの値で表されるプロパティに対してならなににでもアニメーション効果を付けられるので、SVG図形のCSSプロパティやSVG特有のプロパティ(fill、stroke、stroke-width、stroke-color、rx、ryなど)もその対象です。
Velocityでアニメーションを付けられるSVGの全プロパティは、Velocityの「SVGアニメーションについて」のドキュメントを参照してください。
以下のデモでは笑っている魚のSVG図形です。泡が現れては消え、数秒おきにまばたきをします。Playボタンをクリックすると魚はコンテナの左へ動いて消えていき、戻ってきて向きを変えます。
バグについて:残念ながらIE/EdgeブラウザーではCSS transformsをサポートしておらず、Velocityでは互換性確保のための方法を提供していません。したがってデモはIE/Edgeブラウザーでは意図した通りに動きません。
SVG図形にアニメーションを付ける準備
コードを書く前にSVG図形にアニメーション効果を付ける準備の確認をしてください。
- 対象のSVGのパーツにクラスまたはID属性を付け、対象図形をJavaScriptから指定しやすくする
- 要素全体をまとめて動かしたい場合、動かしたい要素を<g></g>タグで囲んでラップしてクラスまたはIDを付け、JavaScriptから操作しやすくする
- 図形自体を簡素化・最適化しておく
VelocityとJavaScript
もう一度書いておきますが、コードはできるだけ関数にまとめて、柔軟性をもたせるとともに繰り返しを避けてください。
例として、下が魚を動かすための関数です。
const moveFish = (elem, moveBy, initialPos, btn, bool = false) => {
Velocity(elem, {
translateX: [moveBy, initialPos]
}, {
duration: 5000,
easing: 'linear',
complete: () => {
if(bool) {
btn.disabled = false;
}
}
});
};
SVGのアニメーションで使う構文はHTML要素と変わりません。上のコードを詳しく説明します。
- コードでは要素に対しVelocityを実行し、translateXプロパティに対しフォースフィーディングで初期値と最終的な値を指定。あとでこの関数を呼ぶ際は、引数elemに魚のSVG図形への参照を渡す
- Velocityの組み込みメソッドcomplete()を利用して、アニメーション終了後にPlayボタンを有効化している。アニメーションの途中で、ユーザーが何度もPlayボタンをクリックしても、キューにアニメーションは追加されない
- アニメーションが終了してPlayボタンが有効化されたら、再度アニメーションを再生できる。これは引数boolを通じて操作している。moveFish()が呼ばれた際、boolの値がtrueならアニメ―ション終了時にmoveFish()関数にあるVelocityのcomplete()メソッドが実行され、ボタンを再有効化する
- ES6ならではの機能としてデフォルト引数を使用する。今回はbool = falseというように引数に初期値を設定したので、moveFish()関数を呼ぶと対応する引数を省略して初期値を自動適用できる。あるいは引数の値を書けば、明示的に初期値から変更できる。上の関数を呼び出している下のコードを読めばこの動作が理解できる
異なる動作のアニメーションを実行するには、マスター関数の中で引数を変えながらmoveFish()関数を何回も呼びだします。
const play = () => {
moveFish(fish, '-1000', 0, btnPlay);
moveFish(fish, 0, '-1000', btnPlay, true);
//more functions here
};
moveFish()関数を実行する際の、コードの書き方を参考にしてください。1回目は5番目の引数無し、2回目は5番目の引数にをtrue入れています。最初の呼び出しではこの引数の値は、moveFish()関数を書く際に設定したデフォルト引数でセットした値になるのです。
最後はPlayボタンのクリックイベントで、play()関数を実行するだけです。
btnPlay.addEventListener('click', function() {
this.disabled = true;
play();
});
以下のデモで遊んで、説明してきたアニメーションの動作を確認してください。
Velocity UI Pack
Velocityをさらに補強するために、アニメーションの作業フローを改善できるプラグインVelocity UI Packを利用します。
Velocity UI Packをここからダウンロードして、Velocityメインライブラリーの下から参照します。
Velocity UI Packでは優れた効果が最初から使えるようになっており、bounceIn/Out(バウンスイン・アウト)、swirlIn/Out(スワールイン・アウト)、fadeIn/Out(フェードイン・アウト)などがあります。すぐに使えるすべての効果のリストはVelocity UI Packのドキュメントを参照してください。さらに、Velocity UI Packは自分のカスタム効果を追加で登録もできます。
下のデモではフォームを送信後に隠すとともに、送信成功を伝えるアイコンをユーザーに表示するためにVelocity UI Packを使っています。
フォームを隠すためのVelocityの呼び出しがどのようになっているか、hideForm()関数の書き方示します。
Velocity(formEl,'transition.bounceUpOut', 500);
- 最初の引数はアニメーション効果を付ける要素、つまりこの場合はフォーム
- 2番目の引数はVelocity UI Packに用意された効果の1つ、bounceUpOut。効果にはtransitions(移動)とcallouts(注意を惹く)があり、妥当な効果をプレフィックスとして前に付ける
- 最後の引数はアニメーション持続時間
詳しくは以下のデモを参照してください。
資料
以下は、Velocityの情報が掲載されているWebへのリンクです。
- Incredibly Fast UI Animation Using Velocity.js(Velocity.jsによる爆速のUIアニメーション)
- Faster UI Animations With Velocity.js(Velocity.jsによるさらに速いUIアニメーション)
- Improving UI Animation Workflow with Velocity.js(Velocity.jsでUIアニメーションワークフローを改善)
- Velocity.js: The Official Collection (CodePen)(Velocity.js:公式コレクション CodePen)
最後に
本記事ではVelocityを紹介し、それをjQueryの依存オブジェクト無しでどうやって使うかを解説してきました。
Velocityのライブラリーの構文は直感的でユーザーフレンドリーです。jQueryライブラリーに慣れた人ならなおさらそうでしょう。文献は揃っていて、機能を紹介するデモもたくさん用意されています。
以下はjQuery無しでVelocityを使ったり、SVG図形のアニメーションを使ったりする際に必要な若干の検討事項です。
- Velocity UI Packには$.Velocity.RunSequence()という関数が用意され、複数のVelocityの実行を1つの効果にまとめて参照できる。複雑なアニメーションを組み合わせるにはとてつもなく便利だが、残念なことにjQuery無しでは動作しなかった
- これほどパワフルなライブラリーなのだからタイムラインを使えるようにしてほしかった。Tweeneを使う方法があるものの、繰り返しになるがjQuery無しでTweeneとVelocityを両方使うのは簡単ではない
- IE/Edgeブラウザーが、いまのところCSS transformをサポートしていないことへの対応策がない点は、VelocityでSVGを扱う際の制約になっている
上に挙げたポイントはプロジェクトに携わる開発陣により、近い将来に実現するでしょう。一方で、Velocityはオープンソースです。もしなにか改善部分やバグ報告があれば、GitHubのVelocity.jpのリポジトリにプルリクエストを送信するか、issues sectionにコメントを残せます。
※著作権表示:SVGデモ素材はPixabayのGoldfishを改変して使用しています。先頭で使用した写真はUnsplashから、また最後のデモの女性カメラマンの写真はGratisographから入手したものです。
※本記事を査読してくれたJames HibbardとVelocity開発チームのみなさんに、この場を借りて感謝します。もちろん、長年Velocityに貢献してきたユーザーや開発者にもです。
(原文:Make Your Website Interactive and Fun with Velocity.js (No jQuery))
[翻訳:西尾健史/編集:Livit]