CSSトランジション

プロパティが変わったときにCSSトランジションを起動する仕組みです。

cssTransition

cssTransition関数は、CSSトランジションを起動する関数を作る関数です。作られた関数はプロパティの更新監視のコールバックとして使うことができます。

const transition = cssTransition("transition")
const show = (state) => ({
  ".modal": {
    "class-is-open": state.modalIsOpen, 
    "&class-is-open": transition
  }
})
.modal.is-open {
  display: block;
  opacity: 1;
}
.modal.transition-enter {  /* モーダルが開くとき */
  opacity: 0;
  display: block;
}
.modal.transition-enter-active {
  transition: opacity 0.2s;
  opacity: 1;
}
.modal.transition-exit {  /* モーダルが閉じるとき */
  display: block;
}
.modal.transition-exit-active {
  transition: opacity 0.2s;
  opacity: 0;
}
パラメーター
名前 デフォルト 説明
name 文字列 省略不可 付与するCSSクラスのベースとなる文字列。たとえば、この文字列を"transition"にすると、CSSクラス名は"transition-enter"、"transition-exit"などになります。
options オブジェクト | "size" | "details" | "dropdown" {} CSSトランジションの挙動を拡張するオプションをオブジェクトで指定します。
文字列を指定することもできて、その場合は組み込みの挙動(「プリセット」と呼びます)を指定します。プリセットは"size"、"details"、"dropdown"の3つがあります(後述)。
options.target 文字列 ":scope" CSSクラスを付与する対象のDOM要素をCSSセレクターで指定します。デフォルトは更新監視の対象となったDOM要素そのものです。
セレクターによるDOM要素の抽出は、更新監視の対象であるDOM要素が起点になります。
options.timeout 数値 10000 CSSトランジションを強制終了するまでの時間をミリ秒(1/1000秒)で指定します。デフォルトは10秒です。
通常、CSSトランジションはtransitionendイベントを受け取ることで終了します。が、なんらかの理由でそうならなかった場合に備えて、強制終了の仕組みがあります。ここで指定しているのは、その強制終了するまで何秒待つか、です。
options.onstart 関数 null CSSトランジションを開始する直前に呼び出されるコールバックです。コールバックのパラメーターおよび返却値は更新監視と同じです。
options.onfinish 関数 null CSSトランジションを終了する直前に呼び出されるコールバックです。コールバックのパラメーターおよび返却値は更新監視と同じです。
返却値
名前 説明
fn 関数 呼び出されるとCSSトランジションを起動する関数。更新監視のコールバックとして使えます。

CSSトランジションの仕組み

batonjsのCSSトランジションはReact Transition Groupを参考にして作られています。
cssTransition関数は、DOM要素に次の手順でCSSクラスの操作を行っていきます。それぞれのクラスに適切なCSSを定義することで、トランジションが実行されるようになります。

  1. transition-enter-beforeを追加
    この後、onstartコールバックが呼ばれます。
  2. transition-enter-beforeを削除、transition-enterを追加
    この後、ブラウザにDOM要素を描画させるために、少しだけ時間を空けます。
  3. transition-enter-activeを追加
    ここでCSSトランジションが始まります。transitionendイベントを受け取ったら次に進みます。
  4. transition-enter-activeおよびtransition-enterを削除
    この後、onfinishコールバックが呼ばれます。

次のように各クラスを使うと良いです。

transition-enterクラスでは、トランジション開始時のスタイルを定義します。モーダルなどdisplayプロパティがnoneのDOM要素の場合、それをblockに変えるのはここです。

transition-enter-activeクラスでは、トランジション終了時のスタイルを定義します。先の手順にもあるように、transition-enterの後にtransition-enter-activeが追加されるようになっていて、この順番でクラスを追加していくことでCSSトランジションを起動させる仕組みになっています。
transitionプロパティを定義するのはここです。

transition-enter-beforeクラスは、トランジションを始める前の準備段階で追加されるクラスです。ある種のUIでは、トランジションを始める前に一瞬だけスタイルを変更して、そのスタイルで何らかの処理を行う必要があります。transition-enter-beforeは、そのような場面のために用意されているクラスです。

transition-enterなど付与されるクラス名は引数によって変わります。
"transition"の部分はcssTransition関数の第1引数として渡した文字列です。
"enter"の部分は、更新監視コールバックの第3引数(newValue)が真っぽい(truthy)値の場合に"enter"になり、偽っぽい(falsy)値の場合に"exit"になります。たとえば、監視するプロパティがopenの場合、openプロパティがtrueなら"enter"、falseなら"exit"です。
このように、プロパティ値がどう変わったかによってクラス名も変わるようになっているので、それぞれで別のアニメーションを使うことができます。

sizeプリセット

CSSトランジションで面倒なのは、heightやwidthを変化させたいときです。javascriptであらかじめDOM要素のサイズを測っておき、それを変化させるようにしないといけないからです。

sizeプリセットを使えばこれを自動化できます。
sizeプリセットを使うと、DOM要素が表示されているときのサイズを測って、CSSカスタムプロパティとしてstyle属性にセットしてくれます。

sizeプリセットがセットするCSSカスタムプロパティは次の2つです。

--width
対象のDOM要素の幅
--height
対象のDOM要素の高さ

sizeプリセットはonstartコールバックの中でDOM要素のサイズを測ります。ユーザーは、onstartコールバックが呼ばれたときにDOM要素が表示されているようにする必要があります。
そのためには、transition-enter-beforeクラスを使います。

const transition = cssTransition("transition", "size")  // sizeプリセットを指定
const show = (state) => ({
  ".modal": {
    "class-is-open": state.modalIsOpen, 
    "&class-is-open": transition
  }
})
.modal.is-open, 
.modal.transition-enter-before {  /* サイズを測るためこの行が要る */
  display: block;
  opacity: 1;
}
.modal.transition-enter {  /* モーダルが開くとき */
  opacity: 0;
  height: 0;
  display: block;
}
.modal.transition-enter-active {
  transition: opacity 0.2s, height 0.2s;
  opacity: 1;
  height: var(--height);  /* --heightを利用 */
}
.modal.transition-exit {  /* モーダルが閉じるとき */
  display: block;
  height: var(--height);  /* --heightを利用 */
}
.modal.transition-exit-active {
  transition: opacity 0.2s, height 0.2s;
  opacity: 0;
  height: 0;
}

detailsプリセット

detailsプリセットはdetails/summaryの開閉にアニメーションを付けるためのプリセットです。
アニメーションの対象はdetails要素の2番目の子要素です。1番目の子要素がsummaryなので、2番目は開閉で見えなくなるボディ部です。

detailsプリセットでは、sizeプリセットと同じように、ボディ部のサイズを測ってカスタムプロパティとしてセットします。ですので、detailsプリセットのユーザーは、ボディ部に対してCSSトランジションを定義する必要があります。

detailsプリセットの使用例はサンプル "jQuery's slideUp/Down"にあります。

dropdownプリセットはドロップダウンのスタイルを定義するためのプリセットです。
ドロップダウンの表示位置を決めてサイズを測ってカスタムプロパティとしてセットしてくれます。

dropdownプリセットの使用例はサンプル "Dropdown"にあります。