高さ可変!CSSアニメーションでなめらかアコーディオン

つっちー


高さ可変!CSSアニメーションでなめらかアコーディオン

こんにちは。フロントエンドのつっちーです。

前回記事では、サポート終了(※1)となったIE9を切り捨てることで、CSSアニメーションを軸とするスライドショーを作成しました。今回も同様のコンセプトで、同じくよく使われているUI、アコーディオンを作成します。

内容の高さにそれぞれ違いがある場合でも、サイズを指定することなくアニメーションするところがポイントです。

※1 Windows Serverを除く

▼目次

今回つくるアコーディオン

まずは完成した状態を確認してみましょう。

See the Pen 1704-completed by ligdsktschy (@lig-dsktschy) on CodePen.

トリガーの要素をクリックすることで開閉する、一般的なアコーディオンです。

ちなみにこれを、CSSアニメーションを使わず実装したものがこちら。

See the Pen 1704-jquery by ligdsktschy (@lig-dsktschy) on CodePen.

jQueryを使用することで記述はシンプルになっていますが、実際の処理は高速で要素の状態を変化させていくという重たいものになっています。また、トリガーの要素を連打すると、クリックした回数だけ開閉が繰り返されてしまう問題も発生しています。

前回からの繰り返しとなりますが、アニメーションに関する処理を軽くしておくことで、他の処理にリソースを割く余裕が生まれます。JSでしか実現できない処理も多くあるため、これは大きなメリットと言えるでしょう。

ではさっそく作っていきます。次のように手順を分けて進めていきます。

  1. HTMLを準備し、開閉の切り替え処理をつくる
  2. トリガーとなる要素のスタイルを整える
  3. アコーディオンが開かれた状態のスタイルをつくる
  4. アコーディオンが閉じた状態のスタイルをつくる
  5. アニメーションを設定する

1.HTMLを準備し、開閉の切り替え処理をつくる

まずは、HTMLを準備し、アコーディオン開閉の切り替え処理を作りましょう。

See the Pen 170401 by ligdsktschy (@lig-dsktschy) on CodePen.

HTMLには、トリガーとなる要素と、アコーディオン部分となるul要素を用意しました。アコーディオン開閉の切り替えは、is-open というクラスをul要素に付け外しすることで、実施されるように書いています。

2.トリガーとなる要素のスタイルを整える

つぎに、トリガーとなる要素のスタイルを整えます。

See the Pen 170402 by ligdsktschy (@lig-dsktschy) on CodePen.

合わせて、今回登場する全要素のスタイルリセットも行いました。重要な部分が読みやすいよう、分離して最上部に記述しています。

3.アコーディオンが開いている状態のスタイルを整える

つづいて、アコーディオンが開いている状態をつくります。

See the Pen 170403 by ligdsktschy (@lig-dsktschy) on CodePen.

4.アコーディオンが閉じている状態のスタイルを整える

開いている状態ができたら、閉じている状態もつくりましょう。

開いている状態と閉じている状態とで、スタイルを書き分けなくてはなりません。
ul要素にis-oepnクラスが設定されている状態が「開いている状態」、設定されていない状態が「閉じている状態」です。両方の状態で共通のスタイルは、閉じている状態の方に記述することで共用されます。

このあとの手順でアニメーションさせるため、そのことを考慮して利用するプロパティを選択しています。ここが高さ可変のアコーディオンを実現するためのポイントです。

See the Pen 170404 by ligdsktschy (@lig-dsktschy) on CodePen.

アニメーションでは、li要素の高さが変動するはずなので、利用するプロパティとしては、まずheightプロパティが思い浮かぶでしょう。閉じている状態を0、開いている状態をautoとしておけば、高さの可変にも対応できそうに思えます。

しかし、autoという値をCSSアニメーションに使用することはできません。アニメーション対象のプロパティにautoが設定されている場合、アニメーションが設定されていない場合と同じ挙動となってしまいます。

そこで、高さを構築しているプロパティひとつひとつをアニメーション対象とすることで、heightをアニメーション対象にする代わりとします。
各項目の高さは、li要素のborder-top,padding-top,padding-bottom、p要素のline-heightによって決まっています。これらはすべて、値が特定されている、つまりautoでないため、問題なくCSSアニメーションの対象とすることができます。

テキストは、サイズではなく、opacityとvisibilityによって可視と不可視を切り替えています。font-sizeプロパティを0にすることが思い浮かぶかもしれませんが、それでは文字の大きさがアニメーションしてしまい、求める結果とならないためです。

max-heightやmargin-topをアニメーションさせることで可変を実現させる方法もあるようですが、それらの方法で各要素とも均等なアニメーションを実現しようとすると、各要素にそれぞれの高さを指定する必要があります。この記事の方法では、要素それぞれの高さが不明でも、各要素ともに均等なアニメーションが実現できます。

 

5.アニメーションを設定する

あとは、アニメーションを設定すれば完成です。

値の変動をアニメーションとするためには、transitionを使用します。開いている状態と閉じている状態との間で、値に変動があるプロパティが、その対象です。

See the Pen 1704-completed by ligdsktschy (@lig-dsktschy) on CodePen.

要素の高さは0.3秒で、テキストの可視状態は0.1秒で、それぞれアニメーションしながら切り替わります。

また、アコーディオンが開くときのテキスト可視状態切り替えには、0.1秒の遅れを設定しています。これは、開き始めるときすぐテキストが可視になってしまうと、要素の高さに収まっていないように見えてしまうためです。

JSで実装した場合に発生する「トリガーの要素を連打した場合に、クリックした回数だけ開閉が繰り返されてしまう」問題は、transitionでは発生しません。これは、transitionがアニメーション途中でアニメーションの向きを切り替えてくれるためです。

 

まとめ

今回は、JSで実装する場合と比べて記述がやや複雑になってしまいましたが、パフォーマンスの向上はやはり重要です。

CSSアニメーション、どんどん活用していきたいですね!

ではまた。つっちーでした!

つっちー
この記事を書いた人
つっちー

フロントエンドエンジニア

関連記事