1. はじめに
div要素をスムーズに追加するアニメーションを作りたくて、少し調査しました。
ソースは下記のitem_insert。(ほかのcssの練習をすることができるように、適当に大きな名前にしてあります。)
2. 課題
下記のように、黄色の内側に青があるという包含関係にあるDIV要素に対して、box1のさらに上へbox2、box3・・・と追加していくことを考えます。
このときbox2を追加したら、box2は上から降りてきて、box0とbox1は下に押し出される、というアニメーションを作りたいです。
こうかなーと適当にやると、box2を追加したとき、すでにあるbox0とbox1が最終的な位置に瞬間移動してしまいます。その押し出されるアニメーションをどうやるか。
3. 解決方法
結果的にこういう方法にしました。
See the Pen Untitled by yoichiro ikeda (@yo16) on CodePen.
3.1. 解決方法の概要
div要素を追加したら、上方向に瞬間的に飛ばします。CSSとしては、top
にマイナス値を入れます。そのあとtop
をゼロにします。CSSのtransition
を使って、マイナス→ゼロの変化は時間をかけることで、アニメーションで降りてきます。
3.2. 工夫した点
ポイントが3つあります。
1つ目は、CSSのtop
の値を更新するタイミング。追加したときのtop
のマイナス値への変化時にはtransition
は使わず、ゼロにするときには生かしたいです。なので、それぞれ生かしたいときに設定し、使いたくないときにはゼロ秒にしてます。
// 追加した直後の、マイナスの位置設定
function initializeMoveBoxes() {
const boxes = document.querySelectorAll(".box");
boxes.forEach(box => {
box.style.top = `${initial_top}px`;
box.style.transition = "top 0s"; // マイナス位置に、すぐ変更してほしい
});
}
// じんわりと移動した結果の位置設定
function smoothMoveBoxes() {
const boxes = document.querySelectorAll(".box");
boxes.forEach(box => {
box.style.top = "0px";
box.style.transition = "top 0.5s ease"; // 0pxに、じんわりと変更してほしい
});
}
2つ目は、top
への値の代入を上から順番に連続的に呼ぶだけだと、最後に入れた値になってしまって、transition
が効かないです。なのでsetTimeout()
を使って、非同期でちょっと後に呼び出していること。こうすることで、2回目のtransition
が効きます。
initializeMoveBoxes();
setTimeout(()=>{smoothMoveBoxes();}, 10); // 10ms後に非同期呼び出し
3つ目は、初期位置の座標値。height
とpadding
とborder
とmargin
の関係で、上へずらす座標値が変わります。ちょっとでも間違ってると、ひっかかったような動作になってしまいます。margin
は要素と要素の間の距離なので1カウント、それ以外は2カウント。CSSに合わせてJavaScriptでconst
で定義して計算してます。忘れないように。。
const box_height = 32;
const box_padding = 10;
const box_border = 8;
const box_margin = 5;
const initial_top = (-1)*(
box_height +
box_padding *2 +
box_border *2 +
box_margin
);
4. まとめ
アニメーションのライブラリを使ったいい方法があるかもしれないですが、こういう書き方を編み出すのも勉強になるし、読み込むコード量は少ないはずです。位置調整のために数字をいじらないといけないことになったものの、一応ロジックで定義できたし、position:absolute
でやらないといけないかも・・嫌だ・・・とずっと思いつつ、回避でできたので、自分的には及第点です。
JavaScriptとcssで、高さの数字とかを二重で持っちゃってる部分は、JavaScriptからcssを設定すればいいと思います。ここではわかりやすいようにしてあるのと、実際に使うときは、汎用的に書かず、数字直打ちでもいいかもしれない。
あとアニメーションの技術は、検索する方法が難しいですね。今回の課題をググるにはどうしたらいいか、なかなか苦戦しました。いい方法があったら是非コメント欄で教えてください!