SVGでボタンアニメーション【Web Animations API】。

〽️画像とstyleの整備。 〽️timingとkeyframe。 〽️要素を取得、アニメーション。 〽️「btn3」につづく。





⬆︎SVGついてのまとめページはこちら。

こんにちは、「ふ」です。
前回はSVGを使ってボタンのon/offを表現しましたが、今回はさらにアニメーションを加えていきたいと思います。

イメージは以下の通り。

クリックすると、一瞬サイズが変わるアニメーションとともに「ON」の状態。ボタンとテキストの色が入れ替わります。

再度クリックすると、同様に一瞬サイズが変わるとともに「OFF」の状態に戻るようにしたい。

今回はWAAPI(Web Animations API)を使って実装してみました。
実際のコーディングを行うとき、ボタンは1つとは限りません。将来的に複数のボタンにも対応できるよう、汎用性を持たせる、というのが狙いです。

前回の記事と、WAAPIを紹介した記事はこちら⬇︎です。ぜひ参考にしてください。

SVG、ボタンのon/offを表現。

2020.12.03
イベントにも効率よく対応。



SVGでWeb Animations API。

2020.11.12
〽️ ネイティブJavaScriptでのアニメーション。


画像とstyleの整備。


◼︎ btn.svg

このようなボタン画像を用意しました。

 ・テキストの部分はアウトライン化し、グループになっています。
 ・ボタンの外枠。stroke(線)がマゼンタ、初期状態のfill(塗り)は白にしてあります。

画像をhtmlの <body> 〜 </body> 内にインラインで表記しましょう。

◼︎ HTML

<body> <svg viewBox="0 0 286.3 128.2">   <rect・・・/>   <g>     <path・・・/>     <path・・・/>     <path・・・/>   </g> </svg> </body>

ここからアニメーションで各オブジェクトを操作するのですが、要素には「document.getElement〜」などのグローバルな場所からアクセスするのではなく、クリックイベントの「target」を起点に取得するようにします。

今後ボタンの数を増やしたとき。グローバルからのアクセスだと、その都度アニメーションのメソッドを追加し続けることになり、コードが肥大してしまうからです。

ユーザがボタン画像をクリックしたとき、イベントのtargetは末端要素であるオブジェクトに成増。実際にどのオブジェクトをクリックした事になるのかは、こちらからは分かりません。

どのオブジェクトがtargetにされても、的確にアニメーションしたい要素にアクセスさせる方法を考えました。
アニメーションさせる対象については、targetから親要素である <svg> を取得し、そこから再び子要素にアクセスする方法をとります。

そのためには、各オブジェクトの階層を統一する必要があります。

現在ボタン枠である <waku> は <svg> 直下の子要素。
一方アウトライン化されたテキストの各部品はグループ化されているため、親は <g> 要素、 <svg> の孫要素に位置しています。

グループを解体し、テキストを構成している各 <path> も <svg> 直下に配置してやりましょう。

◼︎ HTML

<svg viewBox="0 0 286.3 128.2"> 「「1<!--ボタン枠を1つ目の子要素に配置。-->」」   <rect・・・/> 「「4<!--text部分のグループ解除。-->」」   <path・・・/>   <path・・・/>   <path・・・/> </svg>

各オブジェクト〜ボタン枠やテキスト部品〜の取得は、「親要素の〇〇番目の子要素」という形で指定します。
わかりやすいよう、コード上で1つ目の子要素にボタン枠、2番目以降にテキストのオブジェクトを配置しておきます。

◼︎ HTML

<svg class = 「「5"btn" 」」viewBox="0 0 286.3 128.2">   <rect class = 「「1"waku"」」・・・/>   <path class = 「「4"text"」」・・・/>   <path class = 「「4"text"」」・・・/>   <path class = 「「4"text"」」・・・/> </svg>

そして前回と同じくCSSの整備。
オブジェクトごとにclass属性を指定し、スタイルシートの内容は <head> 〜 </head> 内に移しておきます。ボタンの数を増やしたときやJsで動的にCSSを操作した時に、指定が衝突するのを防ぐためです。



◼︎ CSS(HTMLの <head> 〜 </head> 内に記述)

「「5.btn」」 { display:block; width:40%; margin:0 auto; 「「5transform-origin:center;」」 } 「「1.waku」」 { fill:#fff; stroke:#E4007F; stroke-width:4; stroke-miterlimit:10; } 「「4.text」」 { fill:#E4007F; }

SVG全体を指す「btn」の配置はお好みで。
あとはtransform-originで変形の基準を中心にしておきましょう。

timingとkeyframe。


timingオブジェクトを描いていきます。
アニメーションはSVG全体(btn)、ボタン枠、テキストそれぞれに施しますが、時間に関する内容は全て同じであるため、1つのtimingオブジェクトを使い回すようにします。

◼︎ JavaScript

let timing = { duration:290, iterations:1, 「「1fill:"forwards"」」 }        ;

 ・duration(持続時間):0.29s
 ・iteration(繰り返し):1回
 ・fill(終了状態):終了の状態をキープ

3つ目の「fill」は、アニメーションの終了状態を指定します。
デフォルトの指定では、アニメーション終了後に開始前の状態に戻ってしまいます。それではボタン切り替えが表現できません。
「forwards」に指定すると、終了時の状態を保持します。

次にkeyframeを作っていきます。
始めにボタン全体のアニメーション。一瞬大きさが変わり、すぐに元に戻します。

let sizeChange = [    {transform:"scale(1)"},    {transform:"scale(1.1)"},    {transform:"scale(1)"}, ];

この「sizeChange」は親要素であるSVG全体(btn)に施し、on/off両方で使い回します。




let toMagenta = [    {fill:"#FFF"},    {fill:"#FFF"},    {fill:"#E4007F"}, ];

オブジェクトに色を付けるための「toMagenta」。
要素の塗りを白からマゼンタにします。




let toWhite = [    {fill:"#E4007F"},    {fill:"#E4007F"},    {fill:"#FFF"}, ];

「toWhite」はその逆です。
アニメーション終了時にマゼンタから白に成増。

こうして3つのパターンのkeyframeを用意しておきました。

要素を取得、アニメーション。


ではアニメ〜ション笑させていきましょう。

◼︎ JavaScript

「「3// on/offのフラグ。」」 let flag = 1; 「「3//SVG全体(btn)を取得。」」 const btn = document.getElementsByClassName("btn");

前回と同様、現在のon/offを判別するためのフラグ変数「flag」を宣言しておきます。
次にSVG全体(btn)を取得。

「getElementsByClassName」を使ってclass要素を取得すると、HTMLCollectionという配列のようなものが返されます。ここから全ての子要素にonclick属性を付けたいと思います。

現在「btn」classには用意した「btn.svg」の1つだけしか属していないのですが、将来的にボタンが複数になっても大丈夫なようにしておきたい。
子要素へのアクセスは「 btn[0].children 」とはせずに、複数ボタンを想定したfor文インfor文で行う事としました。

for (i=0; i<btn.length; i++) { for(j=0; j<btn[i].childElementCount; j++) { 「「1btn[i].children[j].setAttribute("onclick","onoff()")」」; } }

入れ子になっているfor文の「childElementCount」は子要素の数を返します。
「btn」classに属する全ての要素の全ての子要素にonclick属性を指定。呼び出される関数名は「onoff( )」としました。

function onoff( )

function onoff() { 「「3//targetの親を取得。」」 let 「「5oya」」 = event.target.parentElement; 「「1①」」 「「3//アニメーションさせる。」」 「「5oya」」.animate(「「5sizeChange」」,timing); 「「1②」」 ・・・・

onoff関数です。先づ、SVG全体(btn)のアニメーション。

① 変数「oya」を宣言し、targetの親である <svg> を呼び出します。
ユーザがクリックした場所は、ボタンのどの部分(ボタン枠なのか、テキストパーツなのか)は分かりません。どのオブジェクトがtarget扱いされてもいいように、全てを <svg> 直下の子要素にしておいたのです。

② 一瞬サイズが変わって元に戻るkeyframe、「sizeChange」をSVG全体に仕込みます。

親要素が取得できたところで、子要素であるボタン枠とテキストにアクセスしましょう。
ボタン枠は1番目の子要素に配置しておきましたよね。これは、

oya.children[0]

で大丈夫です。

2番目以降の子要素はテキストを形成している <path> です。テキストの内容によって、要素の数は違ってきます。ここはfor文を使えば、個数に関係なくそれぞれにアニメーションを仕込むことができます。

for(i=1; i<oya.children.length; i++) { //処理内容 }

「i =1」とすることでボタン枠以外の全ての子要素にアクセスすることができます。

でははじめに宣言しておいた変数「flag」を利用して条件分岐・アニメーションを付けていきましょう。

function onoff( )

function onoff() { let 「「5oya」」 = event.target.parentElement; 「「5oya」」.animate(sizeChange,timing); 「「3//click時、offの状態なら。」」 if(flag == 0) { 「「3//ボタン枠はマゼンタに。」」 oya.「「1children[0]」」.animate(toMagenta,timing); 「「3//テキストの皆さんは白に。」」 for(i=1; i<oya.children.length; i++) { oya.「「4children[i]」」.animate(toWhite,timing); } 「「3//フラグは「onになった」を表現。」」 flag = 1; } 「「3//click時、onの状態なら。」」 else { 「「3//ボタン枠は白に。」」 oya.「「1children[0]」」.animate(「「1toWhite」」,timing); 「「3//テキストの皆さんはマゼンタに。」」 for(i=1; i<oya.children.length; i++) { oya.「「4children[i]」」.animate(「「4toMagenta」」,timing); } 「「3//フラグは「offになった」を表現。」」 flag = 0; } }

これでコードは完成です。実行してみましょう。

完成品です。実際にクリックしてみてください⬆︎。
うまくできたでしょうか?

「btn3」につづく。


最後までお読み下さり、ありがとうございました。
今回はWAAPIを使ったボタンアニメーションという、かなりマニアックなことをやってみました。

通常ボタンアニメーションを作る際にはCSS内に@keyframesを記述し、JavaScriptのclassList.add( )でon/offのアニメーションを切り替える、という方法が考えられます。
確かに手軽な方法ではあります。でもそれだと、ボタンが増えるたびコードを追加していかなければなりません。

今回作成したWeb Animations APIでのボタン制御。少し手間は掛かりますが、「新たにボタンを追加したときに一括で制御したい」との野望のもと、作ってみた次第です。

扨(さて)次はいよいよ、複数ボタンのコントロールに挑戦していきます。お楽しみに〜 ♫



関連記事

CSSアニメーション、発火のタイミングを制御する。

2020.05.31
〽️ 好きなタイミングで発火/消火。


SVG、線を描くアニメーション。

2020.09.23
〽️ 破線のプロパティをつかいます。


SVG、use要素を使ったモーションパス。

2020.09.07
〽️ ポイントは‥「マイナスの遅れ」。


SVG、モーションパスで複数の要素を動かす。

2020.08.29
〽️ 小技を集結させて実装します。


SVGアニメーション05、アニメーションしている部分をクリッピング(マスク)する。

2020.07.06
〽️ テキストの内部だけアニメーション。












「ふ」です。

swift、web、ガジェットなど。役立つ情報や観ていてたのしいページを書いていきたいと思います。