JavaScriptで表示切り替えアニメーション
(エンドレス)。

〽️ 端まで行ってもイライラさせない。




アニメーションさせたい!

こんにちは、「ふ」です。
前回は画像切り替えをループさせる、方法を紹介しました⬇︎。

JavaScript、画像切り替えをループさせる。

2022.05.01
〽️ ユーザに親切な切り替えを実装。


そのとき作成したものは、瞬間的に画像が切り替わるものでした。しかし今回は、切り替え時に画像が横に流れていく、スライドアニメーションにしたい。もちろん先頭と最後尾をループさせながら。

作ってみたので紹介します。

仕組み。


今回もA〜Dと4つの要素を用意しました。
画像切り替え時に現在表示されている要素と次/前の要素がスライドして入ってくるようにします。

ここで要素を1つ1つ動かすとなると、それぞれにeventListenerやclass付与の処理をする必要があり、ちょっとめんどくさい。
そこで、要素を横並びに格納した親要素自体を左右に動かすことにしました。
さらにその親要素として可視領域を設け、要素が切り替わっているように見せます。

要素を配置。


要素を配置していきます。

「「3<!-- 1番外の可視領域 -->」」 <div id = "grandparent"> bb「「3<!-- 要素を並べる領域 -->」」 bb<div id = "parent"> bbbb<img src = "a.svg"> bbbb<img src = "b.svg"> bbbb<img src = "c.svg"> bbbb<img src = "d.svg"> bb</div> </div> 「「3<!-- ボタン部分 -->」」 <div id = "buttons1"> <button onclick = "modoru()"> ◀︎ </button> <button onclick = "susumu()"> ▶︎ </button> </div>

先述のとおり、1番外側に可視領域「#grandparent」を配置。その中に要素を横並びにする領域「#parent」を設けました。


#grandparent { width:18em; height:18em; margin:0 auto; 「「1overflow:hidden;」」 border:solid 1px; } #parent { 「「1width:400%;」」 height:100%; background-color:#fff; display:flex; } #parent img { 「「3//余白も含めて均等に配置する」」 「「1width:20%; margin:5%;」」 } #buttons { display:flex; width:20em; margin:0 auto; justify-content:space-around; } #buttons button { width:30%; padding:0.4em; font-size:1em; margin:1em; }

「#grandparent」はoverflow:hiddenとし、はみ出している部分が隠れるようにします。
また「#parent」内に配置した子要素は今回4つなので、widthを400%としました。

切り替える子要素は、余白も含めて「#parent」に均等に収まるようwidthとmarginを調整しています←このへんはJavaScriptを使ったほうがいいかもですね。



現在の状態⬆︎です。
ここから切り替えとアニメーションを実装していきましょう。

2段階のイベント処理。


表示切り替えとアニメーションの両方を実装するには、2段階のイベント処理を設ける必要があります。

1つ目は、進む/戻るボタンを押したときのイベント。
HTMLタグ内のonclick属性で参照している関数を用意します。今準備したclassを要素に付け加えることで、アニメーションを発火させます。

2つ目は、アニメーション終了に呼応させるイベント処理。
先ほど要素に付与したclassを削除することで、アニメーションのCSSを取り除きます。このための関数とイベントリスナも作成します。

順序切り替えのタイミング。

表示の切り替えに関しては、前回と同じく「要素の順序を入れ替える」ことによって表現します。

先ほど述べた2つのイベント発生時にその処理を行うのですが、進む/戻るの動作によって順序入れ替えのタイミングが変わってきます。アニメーションが途中で途切れたりしない、シームレスな表示をさせるためです。

「進む」処理。


進むボタンを押す → アニメーション発火
アニメーション終了 → classを削除 + 並び替え

①、「進む」場合には要素の塊をアニメーションで左へと移動させます。

②、しかしアニメーション完了後、塊は元の位置に戻ってしまう。可視範囲内には相変わらず「A」が表示されている状態です。この状態は見せたくありません。

③、そこでアニメーション完了のタイミングで、「A」の順番を最後尾に入れ替えて「B」が表示されるようにします。

「戻る」処理。


戻るボタンを押す → 並び替え + アニメーション発火
アニメーション終了 → classを削除

「戻る」場合。
こんどは要素の塊をアニメーションで右へと移動させるのですが、そのまま動かすと可視範囲に何もない状況になってしまいます。ここがシームレスな表示を妨げる。

そこで。

①、アニメーション開始前にあらかじめ、最後尾の「D」を先頭に移動させておきます。

②、アニメーションは可視範囲に「A」が表示されている状態、つまり左に25%ずれたところから定位置に復旧させるようにします。

③、アニメーション完了すると、最後尾にあった「D」が可視範囲に表示されている状態が得られます。

切り替えとアニメーション。


アニメーションのCSS。


切り替えボタンを押したときに発生させる、アニメーションのCSSを用意しておきましょう。

.susumu { animation:susumu 0.4s; } @keyframes susumu { to { transform:translate(-25%); } } .modoru { animation:modoru 0.4s; } @keyframes modoru { from { transform:translate(-25%); }

「進む ▶︎」ボタンを押したときには「.susumu」、「◀︎ 戻る」ボタンを押したときには「.modoru」。
classList.add( )で要素にclass属性を付け加えることによって、アニメーションを発生させます。

JavaScript。

先づは「進む」操作から見ていきます。

「「3//親要素を取得」」「「1 ..①」」 const parent = document.getElementById("parent"); 「「3//進むボタンを押した時の関数」」「「1 ..②」」 function susumu() { 「「3//アニメーション発火」」「「1 ..③」」 parent.classList.add("susumu"); 「「3//アニメーション完了時のイベントリスナ」」「「1 ..④」」 parent.addEventListener("animationend",susunda,{once:true}); } 「「3//進むアニメーション完了時の関数」」「「1 ..⑤」」 function susunda() { 「「3//要素の順番を入れ替え」」「「1 ..⑥」」 parent.appendChild(parent.children[0]); 「「3//アニメーションのCSSを削除」」「「1 ..⑦」」 parent.classList.remove("susumu"); }


① 要素を並べている親要素の「.parent」を取得。

② button要素のonclick属性で指定した「susumu」関数です。

③ 「.parent」にclassを付与して、アニメーションを発火させます。

④ アニメーション終了後、「susunda」関数を呼び出すようにイベントリスナを仕込みます。
ここで注意点。

イベントリスナの重複を避ける。

要素の順序を変更した場合、何故かイベントリスナが呼び出される回数が蓄積されていく、という現象がおきました。 chiildren[0]をappendChildの対象にしているのですが、2番面の要素が先頭に入れ替わった場合、新たなchildren[0] ←つまり2番目の要素に対してもappendChildが働いてしまいます。

イベントリスナには第3引数としてオプションを記述することができます。{once:true}とすることで、イベントリスナの蓄積を回避します。

⑤ アニメーション完了時に呼び出される「susunda」を定義。

⑥ 「進む」操作の場合はここで要素の順序を入れ替えます。

⑦ 再度アニメーションできるよう、先ほど付与したclassを取り除いておきます。




次に「戻る」操作です。

「「3//戻るボタンを押したときの関数」」「「1 ..⑧」」 function modoru() { 「「3//アニメーション発火」」「「1 ..⑨」」 parent.classList.add("modoru"); 「「3//要素の順番を入れ替え」」「「1 ..⑩」」 parent.insertBefore(parent.children[parent.children.length-1],parent.children[0]); 「「3//アニメーション完了時のイベントリスナ」」「「1 ..⑪」」 parent.addEventListener("animationend",modotta,{once:true}); } 「「3//戻るアニメーション完了時の関数」」「「1 ..⑫」」 function modotta() { 「「3//アニメーションのCSSを削除」」「「1 ..⑬」」 parent.classList.remove("modoru"); }

「戻る」操作においても基本的な流れは同じですが、⑩要素の順序入れ替えのタイミングが違います。
理由は先述したとおりです。ボタンを押したときに呼び出される関数の中で、入れ替えを行うようにします。

完成。



お疲れ様です、これにて切り替えアニメーションが完成。

実際に「◀︎」「▶︎」ボタンを押してみてください⬆︎。要素がアニメーションしながら、しかもエンドレスに切り替わります。

やはりループすると便利。


最後までお読みくださり、ありがとうございます。

今回の切り替えアニメーション作成。少し複雑な手順をふむことになりましたが、1度作っておけばさまざまな場面で使い回すことができます。基本のコードをストックしておいて、状況によって好みのアレンジを加える。webデザインのテンプレートとして役立ててください。

ではまた〜 ♪



JavaScript、乱数の範囲や重複を指定〜Math.random使い方。

2021.11.17
整数/範囲/重複の有無を自在にあやつろう。

JavaScriptコードの「?」をまとめたページはこちら⬆︎。











「ふ」です。

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