2016/11/02
SVGの属性をjQueryで操作してアニメーションさせる(数値の値のみ)
インラインSVGの要素をアニメーションさせる方法は様々あります。
一つはSMILというXMLの言語をSVGで設定する方法。
SVGの規格に含まれているため、自然で無理のないアニメーションを実装できます。
ただ、これはIE、Edgeでサポートされておらず、現状では実用で使うのは難しいのが現実です。
もう1つはCSSでアニメーションさせる方法。
CSSの滑らかなアニメーションを、HTMLの通常の要素と同じようなアニメーションの指定方法でSVGにアニメーションを指定できるため、利用しやすいのがメリットです。
ただ、こちらもIE、Edgeでサポートされておらず、他のブラウザでも一部動作したりしなかったりします。
最後の1つはJavaScriptでSVGの属性を直接操作して、アニメーションさせる方法。
直接SVGを操作するため、インラインSVGをサポートしているブラウザであれば、どのブラウザでも同じようにアニメーションさせることができます。
ただ、SMILやCSSと比べると、若干、実装の難易度が高くなります。
jQuery3系では、インラインSVG内の要素を選択できるようにサポートされましたが、
残念ながら、アニメーションはサポートされていません。
ただ、属性の変更はサポートされているため、JavaScriptで直接操作するよりは簡単にアニメーションを実装できます。
そこで、jQueryを通じてSVGをアニメーションさせる方法を考えてみました。
ただし、今回は、xやwidthのような単独の数値の値を取る属性が対象で、pathのd属性やfillの色など、複雑な値を取るものには対応していません。
↓jQueryでSVGをアニメーションさせてみたもの
DEMO
jQueryでSVGをアニメーションさせるまで
- アニメーションにはrequestAnimationFrameを使う
- イージングをつけられるようにする
- jQueryのanimate()のように手軽に使えるようにする
今回は、以上の3つを実現できる方法を考えてみました。
まず、アニメーションにはHTML5で推奨されているrequestAnimationFrameを使いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //animation var startTime = performance.now(), loop = function(){ var nowTime = performance.now(), time = nowTime - startTime, //アニメーションの経過時間 timeRatio = time / duration; //durationの中の現在の時間の経過割合 if(timeRatio < 1) { //経過時間がdurationの100%に到達するまで実行 for(key in properties) { 経過時間のdurationに対する割合によって、属性の変更量を変える elm.attr(key, beforeValues[key] + ((properties[key] - beforeValues[key]) * timeRatio)); } window.requestAnimationFrame(loop); } else { //経過時間がdurationの100%に到達したら、最後の値を属性に設定してループをやめる。 for(key in properties) elm.attr(key, properties[key]); } } window.requestAnimationFrame(loop); |
このときポイントになるのは、経過時間をアニメーションのループ毎に取得して、
duration(アニメーション全体の時間)の中のどのくらいの割合まで経過時間が進んだかによって、属性の変更中の値を決めることです。
requestAnimationFrameでは、アニメーションのフレーム単位の時間を指定できないため、
経過時間の計測が必須になります。
続いて、現状だとアニメーションが直線的で不自然なので、イージングに対応させます。
イージングの関数は、以下で紹介いただいていたものをそのまま使わせていただきました。
Simple Easing Functions in Javascript – see https://github.com/gre/bezier-easing · GitHub
こちらを前もって読み込んでおいて、
先程のコードの属性変更部分を以下のように書き換えます。
1 2 3 | elm.attr(key, beforeValues[key] + ((properties[key] - beforeValues[key]) * timeRatio)); ↓ elm.attr(key, beforeValues[key] + ((properties[key] - beforeValues[key]) * EasingFunctions[easing](timeRatio))); |
こうすることで、イージングが実装され、アニメーションがより自然なものになります。
最後に、ここまでのコードをjQueryの関数として追加し、
使い勝手の良いものにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | $.fn.animateAttr = function(properties, duration, easing){ //reset var elm = this, beforeValues = {}; duration = duration ? duration : 400; //duration default easing = easing ? easing : "easeInOutQuad"; //easing default for(key in properties) { beforeValues[key] = Number(elm.attr(key)) ? Number(elm.attr(key)) : 0; } //animation var startTime = performance.now(), loop = function(){ var nowTime = performance.now(), time = nowTime - startTime, timeRatio = time / duration; if(timeRatio < 1) { for(key in properties) { elm.attr(key, beforeValues[key] + ((properties[key] - beforeValues[key]) * EasingFunctions[easing](timeRatio))); } window.requestAnimationFrame(loop); } else { for(key in properties) elm.attr(key, properties[key]); } } window.requestAnimationFrame(loop); } |
こうすることで、以下のような指定でアニメーションを実装できるようになりました。
1 2 3 4 5 6 7 8 9 | $("circle.example").animateAttr({ cx: 100, r: 64 }, 800, "easeOutQuad"); $("circle.example").animateAttr({ 「属性名」: 「属性の値(数値)」, ・・・ }, 「duration」, 「easing名」); |
プラグインにしたコードは、以下からダウンロードできます。
応用することで、色やもっと複雑な属性値のアニメーションも可能になると思います。
参考にさせていただいたサイト
Animating with Robert Penner’s Easing Functions | kirupa.com
Simple Easing Functions in Javascript – see https://github.com/gre/bezier-easing · GitHub
Author Profile
NINOMIYA
Webデザイナー兼コーダー出身のフロントエンド開発者です。 UXデザインやチーム開発の効率化など、勉強中です。
SHARE