CSSだけでスライダーのレイアウトを作る
ふとCSSだけでスライダーを作れるのではと思ったので作ってみます。
CSS3では四則演算や変数だったりアニメーションがありますのでそれを使いながら作ってみたいと思います。
未対応なブラウザもあるかもしれませんが、ひとまず気にせず使っていきます。
書いてみる
とりあえずHTMLから書いていきましょう、ひとまずJSのスライダーでもよくある書き方で
基本的にはposition:absolute
で画像を重ねてCSSアニメーションで透過をコントールしてあげようと思います。
wrapperで囲む必要があったのかは謎ですが精神衛生上囲みたくなるのでご容赦下さい。
<div class="css-slider">
<div class="slider-wrapper">
<ul>
<li><img class="img01" src="img/slider-img01.jpg" alt=""></li>
<li><img class="img02" src="img/slider-img02.jpg" alt=""></li>
<li><img class="img03" src="img/slider-img03.jpg" alt=""></li>
<li><img class="img04" src="img/slider-img04.jpg" alt=""></li>
<li><img class="img05" src="img/slider-img05.jpg" alt=""></li>
</ul>
</div>
</div>
ちょっと面倒くさいですか画像ごとに個別クラスを割り振ってあげてください。
これがないとアニメーションのキーフレームを制御できなくなります。
CSSの基本レイアウト
ひとまず基本レイアウトとしてスライドの高さなどサイズの調整
変数を定義することで数値の流用や計算に使用します。
これによりちょっとconfigっぽく変数を更新するとスライダーのレイアウトを変えられるようにしています。
枚数などを変える場合は別途キーフレームの間隔を調整しないといけないので不完全ですが。
わざわざスライダーのサイズに合わせた画像を作るのが面倒だったのと、サイズが変わってもきれいに表示されるよう
object-fit: cover;
を使用していますのでトリミングの方法を変更したい方はそちらを変更して下さい。
:root {
--slider-width: 100%; /* スライダーの横幅 */
--slider-height: 500px; /* スライダーの高さ */
--slider-total-time: 30s; /* スライダーの一周の間隔 */
--slider-total-page: 5; /* スライダーの枚数 */
--slider-per-seconds: calc(var(--slider-total-time) / var(--slider-total-page));
}
.css-slider {
width: var(--slider-width);
height: var(--slider-height);
}
.slider-wrapper,.slider-wrapper ul,
.slider-wrapper li,.slider-wrapper img {
width: 100%;
height: 100%;
}
.slider-wrapper ul {
position: relative;
}
.slider-wrapper li {
position: absolute;
top: 0;
}
.slider-wrapper img {
/* トリミング方式を変更すうる場合はこちら */
object-fit: cover;
}
.css-slider ul {
list-style-type: none;
margin: 0;
padding: 0;
}
おそらくここまで書くとスライドが一箇所にまとめられて表示エリアがはっきりすると思います。
基本レイアウトの設定が終わったら次はアニメーションの設定に入ります。
アニメーションの実装
次にCSSアニメーションでスライドの動きを実装します。
スライダーって言ってたのにフェードイン・アウトなのは触れないで下さい。
(一応この方法を応用すれば普通のスライダーも作れると思います)
そしてここが一番わかりづらいかと思いますが、画像が表示されている時間はKeyframeの 80%~100% の間だけです。
厳密に言うと 80%~85% , 95%~100% の間はトランジションの時間なので実際の時間はもっと短く感じます。
そしてこのキーフレームのタイミングは
(スライドの画像枚数/100%となるように設定して下さい)
今回だと画像5枚なので、(5/100%)で20%です。そのうち前後5%をトランジションの時間にしています。
@keyframes slide {
0% {
opacity: 0;
}
80% {
opacity: 0;
}
85% {
opacity: 1;
}
95% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.slider-wrapper img {
opacity: 0;
-webkit-animation: slide var(--slider-total-time) ease-in-out infinite normal forwards;
animation: slide var(--slider-total-time) ease-in-out infinite normal forwards;
}
.slider-wrapper .img01 {
animation-delay: calc(var(--slider-per-seconds) * 0)
}
.slider-wrapper .img02 {
animation-delay: calc(var(--slider-per-seconds) * -1);
}
.slider-wrapper .img03 {
animation-delay: calc(var(--slider-per-seconds) * -2);
}
.slider-wrapper .img04 {
animation-delay: calc(var(--slider-per-seconds) * -3);
}
.slider-wrapper .img05 {
animation-delay: calc(var(--slider-per-seconds) * -4);
}
アニメーションのタイミングをずらす
このままだと重ねた5枚の画像が同じタイミングで透明度が変化して戻るため1枚の画像しか見えません
animation-delayにマイナス値を設定することでアニメーションの開始地点をオフセットできるのでそちらの設定をします。
:root {
--slider-total-time: 30s; /* スライダーの一周の間隔 */
--slider-total-page: 5; /* スライダーの枚数 */
--slider-per-seconds: calc(var(--slider-total-time) / var(--slider-total-page));
}
上記のように変数--slider-per-seconds
は
calc(var(--slider-total-time) / var(--slider-total-page))
という風に定義されていますので
それぞれ代入して 30÷5=6 となります。
これを下記のように-(枚数)分を掛けてanimation-delay
でアニメーションの開始地点をオフセットさせます。
.slider-wrapper .img02 {
animation-delay: calc(var(--slider-per-seconds) * -1);
/* --slider-per-seconds: calc(var(--slider-total-time) / var(--slider-total-page)); */
}
このように設定した場合はanimation-delay:-6s
ですのでアニメーションが6s進んだ状態で開始されます。
これを各画像ごとに20%分ずらしていくことでタイミングを揃えます。
ただこの場合だと5枚目に設定したスライドから始まってしまうので気になる方は順番を入れ替えてもいいかもしれません。
完成イメージ
全体だと長すぎてGIFのサイズが大きくなってしまうのでトランジションの場所だけ
このようにCSSだけでもスライダーを実装することができました。
まとめ
こちらのデモはあくまでCSSのみでスライダーを作成することをメインにしていますので
保守性などは高くないコードにはなってしまったかと思います。
特にキーフレームを別途指定しないといけない点など。
自分でもなんで作ったのか分からないですがどちらにしろ遊びで作ったものなので
今どきのCSSはこんな事もできるんだ的な感じで楽しんでもらえればありがたいです。
特に四則演算と変数の組み合わせはかなり面白いと思います。
GitHub
こちらのソースコードはGitHubでも公開しています。
変更を加える可能性があるのでこの記事と内容が異なるかもしれません。
なおQiitaへの投稿は初めてですので、書き方など含め改善点などありましたらお教えいただけるとありがたいです。
github - ken7253