動くCSSのためのメモ。
スライドショー

表示を切り替える仕組みはタブコンテンツと全く同じ。違いは、タブボタンの代わりに、左右の矢印ボタンでコンテンツを切り替えるようにしてるところ。CSSでスライドショーをつくってみます。

矢印ボタンの仕組み

タブボタンはそれぞれのコンテンツ用のボタンに割り振ることができたけれど、矢印ボタンは左右のふたつだけで、次々コンテンツを切り替えてかなきゃなりません。
まずは、矢印ボタンとして機能させるための仕掛けについての解説です。

矢印ボタンはそれぞれ、左矢印が戻る(コンテンツを右方向にスライドさせる)、右矢印が送る(コンテンツを左方向にスライドさせる)となっていて、例えば、1つめのコンテンツが表示されている時に右矢印ボタンをクリックすると、2つめのコンテンツまでスライドし、そこからさらに右矢印ボタンをクリックすると、3つめのコンテンツまでスライドします。
コンテンツは、タブコンテンツの時と同じように、全コンテンツを包む.slideContentsをスライドさせてます。

コンテンツがスライドするイメージ

つまり、1つめのコンテンツが表示されている時の右矢印ボタンは#switch2をチェック、2つめのコンテンツが表示されている時の右矢印ボタンは#switch3をチェックするということ。クリックする度にチェックするターゲットが変わるわけですねー。
とはいえCSSでは、for属性だけ書き換えるとかできないので、最初からすべてのlabel要素を用意しておき、クリックする度にCSSで出し分けるようにします。

HTML

構造はタブコンテンツのものとほぼ同じですが、タブボタンの代わりに、左右の矢印ボタン用のp要素を設置して、その中に、label要素を5つずつ設置します。
<i class="ico">は矢印アイコン表示用の要素。

<p class="arrow prev">
	<i class="ico"></i>
	<label for="switch1"></label>
	<label for="switch2"></label>
	<label for="switch3"></label>
	<label for="switch4"></label>
	<label for="switch5"></label>
</p>
<p class="arrow next">
	<i class="ico"></i>
	<label for="switch1"></label>
	<label for="switch2"></label>
	<label for="switch3"></label>
	<label for="switch4"></label>
	<label for="switch5"></label>
</p>

CSS

矢印ボタンの中のlabel要素は、position: absoluteを指定して、すべてが重なるように配置しておきます。

.arrow,
.arrow label {
	position: absolute;
	top: 0;
	width: 50px;
	height: 100%;
}
.prev,
.arrow label {
	left: 0;
}
.next {
	right: 0;
}
左右の矢印の構造

pointer-eventsプロパティの値をnoneに指定すると、その要素のクリックやホバーなどのマウスイベントを無効にすることができます。※初期値はautoで、操作が有効の状態。
これを使って、label要素のマウスイベントを一旦、無効にしておきます。

.arrow label {
	pointer-events: none;
}

下記のように、ラジオボタンの状態に合わせて、label要素のマウスイベントを有効にすればできあがり!

#switch1:checked ~ #slideshow .next label[for="switch2"],
#switch2:checked ~ #slideshow .next label[for="switch3"],
#switch3:checked ~ #slideshow .next label[for="switch4"],
#switch4:checked ~ #slideshow .next label[for="switch5"],
#switch5:checked ~ #slideshow .next label[for="switch1"] {
	pointer-events: auto;
}

チェックされたラジオボタンのid属性と、label要素のfor属性が、順繰りになるように指定しています。
例えば、右矢印(進む)ボタンなら、
#switch1がチェックされている時は、<label for="switch2">を有効に、
#switch2がチェックされている時は、<label for="switch3">を有効に、
…という具合に、チェックされてるラジオボタンの次のラジオボタンがチェックされるように切り替えてます。

表示中のコンテンツに合わせて矢印ボタンを切り替える

左矢印(戻る)ボタンでは逆に、
#switch1がチェックされている時は、<label for="switch5">を有効に、
#switch2がチェックされている時は、<label for="switch1">を有効に、
…という具合に、チェックされてるラジオボタンの前のラジオボタンがチェックされるように切り替えます。

pointer-eventsプロパティについて詳しくは、下記ページを参照のこと。
pointer-events - CSS: カスケーディングスタイルシート | MDN

ページトップへ戻る

同じ方向にスライド

コンテンツ1からコンテンツ5に切り替わる時と、コンテンツ5からコンテンツ1に切り替わる時、逆方向にスライドして戻るんじゃなくて、常に同じ方向にスライドした方がスマートですよね:)
下図みたく、スライドさせる要素を、.slideContentsじゃなく、その中のコンテンツに変えればできそうな気がします。

シームレスにスライドするイメージ

まずはコンテンツをposition: absoluteで、すべて同じ位置に絶対配置してから、transform: translateX(100%)スライドの横幅分右側にズラして、隠しておきます。

#slideshow .slideContents section {
	position: absolute;
	top: 0;
	left: 0;
	transform: translateX(100%);
	transition: transform .6s;
}

※transformプロパティのtranslateX()の値をパーセントで指定する場合、その要素の大きさが基準となります。なのでここでtranslateX(100%)は、section要素の横幅分、右方向へズレることになります。

タブボタンと同じように、#switch1:checkedの時はひとつめ<section>を真ん中に、#switch2:checkedの時はふたつめ<section>を真ん中に…という風に指定します。

#switch1:checked ~ #slideshow .slideContents #slide1,
#switch2:checked ~ #slideshow .slideContents #slide2,
#switch3:checked ~ #slideshow .slideContents #slide3,
#switch4:checked ~ #slideshow .slideContents #slide4,
#switch5:checked ~ #slideshow .slideContents #slide5 {
	z-index: 1;
	position: relative;
	transform: translateX(0);
}

この時にposition: relativeも一緒に指定するのがポイント。ぜんぶ絶対配置にすると.slideContents高さがなくなってしまうので、表示中のコンテンツだけは絶対配置じゃなくして、高さを保持します。

これで、矢印ボタンをクリックして:checkedが切り替わると、それに合わせてコンテンツがスライドするようになりました!けどこれだけだと下図みたいな感じ。右側から出たり入ったりするだけになっちゃってます…。

右矢印ボタンをクリックした時には、表示中のコンテンツは左側にスライドしないといけないですよね。
つまり下図みたく、コンテンツが順繰りローテーションするような構造になります。

順繰りスライドしてくイメージ

「コンテンツ2」表示中はコンテンツ5コンテンツ1、「コンテンツ3」表示中はコンテンツ2コンテンツ1という具合に、表示中のコンテンツの手前ふたつのコンテンツは左側に来るように指定すればいいっぽいです!

#switch1:checked ~ #slideshow .slideContents #slide5,
#switch1:checked ~ #slideshow .slideContents #slide4,
#switch2:checked ~ #slideshow .slideContents #slide1,
#switch2:checked ~ #slideshow .slideContents #slide5,
#switch3:checked ~ #slideshow .slideContents #slide2,
#switch3:checked ~ #slideshow .slideContents #slide1,
#switch4:checked ~ #slideshow .slideContents #slide3,
#switch4:checked ~ #slideshow .slideContents #slide2,
#switch5:checked ~ #slideshow .slideContents #slide4,
#switch5:checked ~ #slideshow .slideContents #slide3 {
	transform: translateX(-100%);
}

そしてできたのがこちら。むむ!何やらめまぐるしい…。

translateX(-100%)からtranslateX(100%)へ移動してくのが見えちゃってるんですね…。
端から端へ移動する時だけは、スライドじゃなくて、瞬間移動するようにしないといけません。つまりtransition-duration(変化にかかる時間)を0秒にすればいいって事ですね!

#switch1:checked ~ #slideshow .slideContents #slide3,
#switch1:checked ~ #slideshow .slideContents #slide4,
#switch2:checked ~ #slideshow .slideContents #slide4,
#switch2:checked ~ #slideshow .slideContents #slide5,
#switch3:checked ~ #slideshow .slideContents #slide5,
#switch3:checked ~ #slideshow .slideContents #slide1,
#switch4:checked ~ #slideshow .slideContents #slide1,
#switch4:checked ~ #slideshow .slideContents #slide2,
#switch5:checked ~ #slideshow .slideContents #slide2,
#switch5:checked ~ #slideshow .slideContents #slide3 {
	transition-duration: 0s;
}

そんなこんなで、できあがり!

ページトップへ戻る

6秒おきに切り替わる

スライドショーなら、自動でスライドさせてみたいです。
animationプロパティを使って、スライドするアニメーションを、各コンテンツに個別に指定して、タイミングよく順番にスライドさせれば、できそうな気がします:D

まずはアニメーションのキーフレームを作ります。
5つのコンテンツを順番にスライドさせるので、コンテンツひとつにつき、表示される時間は、全体の20%ずつ割り振れます。アニメーション全体の長さを100%秒とすると、各コンテンツのアニメーションの一連の流れは、下図みたいな感じになると思います。

スライドショーの一連の流れ
  1. 0%秒から16%秒までは真ん中でじっとしてる。

  2. 16%秒から20%秒にかけて、真ん中から左側へスライド。

  3. 右側へ瞬間移動。

  4. 22%秒から96%秒までは右側でじっとしてる。

  5. 96%秒から100%秒にかけて、右側から真ん中へスライド。

@keyframes autoplay {
	0% { transform: translateX(0); }
	16% { transform: translateX(0); }
	20% { transform: translateX(-100%); }
	20.001% { transform: translateX(100%); }
	96% { transform: translateX(100%); }
	100% { transform: translateX(0); }
}

瞬間移動のところは、@keyframesでは同じパーセント値を指定すると、後に書いた指定が優先されてしまうので、パーセント値をほんのちょっとズラして、瞬間的に移動するようにしてます。

そして、上で定義したautoplayを、スライドさせるコンテンツに指定します。

#slideshow .slideContents section {
	animation: autoplay 30s infinite;
}

autoplayというアニメーションを、30秒かけて、繰り返し再生すよう指定しました。なので20%秒がちょうど6秒になります:)
ただし、このままだと全部のコンテンツが重なってスライドしてる状態。6秒後には全コンテンツが一緒にスライドして、何もなくなっちゃいます…X(。24秒後にまた戻ってきます。

各コンテンツのアニメーションの開始位置をちょっとずつズラせば、同じキーフレームを使って、コンテンツを順番にスライドさせることができます。
animation-delayを使って、下記のように指定します。

#slideshow .slideContents #slide1 { animation-delay: 0; }
#slideshow .slideContents #slide2 { animation-delay: -24s; }
#slideshow .slideContents #slide3 { animation-delay: -18s; }
#slideshow .slideContents #slide4 { animation-delay: -12s; }
#slideshow .slideContents #slide5 { animation-delay: -6s; }

animation-delayプロパティにマイナスの値を指定すると、その分進んだ時点からアニメーションを開始するので、上記指定で、下図のように開始位置がズレるわけですねー。

開始位置が6秒ずつ後ろにズレる

できましたー!

けど、矢印ボタンをクリックしても何も起こりません…。
矢印ボタンではtransform: translateX()の値を切り替えてスライドさせたんですが、アニメーション中は@keyframesルールで定義したスタイルの方が優先されるので、矢印ボタンで切り替えてもびくともしないんですね…。自動スライドと矢印ボタンの併用はできなさそう…¦(。

そんなわけで、矢印ボタンをなくして、できあがり!

ページトップへ戻る

演出を加える

スライドしてきた時にテキストだけ後から出てくるとか、イラストだけチロチロ動いてるとか、切り替わる時にほんの少しの動きを加えてみます。より印象的になること請け合いです:)

上のサンプルでは、これまでのスライドショーに、h1要素とp要素を追加してみました。
<img>の上に<div class="contents">を重ねて、その中に<h1><p>を入れてる構造になります。

コンテンツの構造

コンテンツと、その中のh1要素p要素、それぞれの動きを個別に変えることで、スライドしてくる時のコンテンツにちょっとしたアクセントを付けてます。
コンテンツ1〜3まではtransitionプロパティを使って、コンテンツ4と5はanimationプロパティを使って、動きの違いを表現しています。
それでは、順番に(おおざっぱに)解説です!

奥行きがある感じ

h1要素とp要素の初期位置(左右に隠れてる時の位置)を、コンテンツの中心よりズラして配置し、コンテンツのスライドに合わせて徐々に中央に来るようスライドしてます。

h1要素とp要素は、コンテンツの中で、別にスライドしてる

遠くからスライドしてくる要素ほど速く動くので、やんわり遠近感が感じられる表現になるってわけで、これがパララックスってやつです。

よりパララックスがじっくり感じられるように、コンテンツ自体とh1要素とp要素それぞれに、transition-timing-functionで、ゆっくり始まってゆっくり終わる「easeInOutCubic」というイージングを適用したりしてます。

遅れて出てくる

コンテンツ自体の動きは変えずに、h1要素とp要素の動きをちょっと変えてるだけ。

上のサンプルと同じように、h1要素とp要素の初期位置をコンテンツの中心よりズラして配置し、transition-delayで、0.3秒ずつ遅れて出てくるように指定したり、transition-durationで、コンテンツよりも、変化にかかる時間を長めに指定してます。

さらに、変化の仕方transition-timing-functionで、ちょっと行き過ぎて戻る、既存の「easeOutBack」というイージングを、緩めにアレンジしたものを適用。

要素を立体的に

コンテンツの初期状態(左右に隠れてる時の状態)を、コンテンツの表面が内側を向くように、横に90°回転した状態にしてます。
その時にちゃんと立体的な表現になるように、親要素<div class="slideContents">perspectiveプロパティを追加。

#slideshow .slideContents {
	perspective: 1000px;
}

回転する様子がよく見れるように、初期位置をいつもより手前に指定したり、回転の最中に透明になるように、透明度だけtransition-durationを短めにしたり、アニメーション具合を確認しながら、満足な動きになるまで適宜、微調整。
したりなんだりで、できあがり。

アニメーションする

スライドする時の動きは変えずに、スライドした後、一度だけ、h1要素とp要素をアニメーションさせてます。
h1要素がぽよんと出てくる「h1Appear」というアニメーションと、同じくp要素がぽよんと出てくる「pAppear」というアニメーションを用意し、#switch4チェックされた時に一度だけ再生するよう、指定します。

#switch4:checked ~ #slideshow .slideContents #slide4 h1 {
	animation: h1Appear 1.5s;
}
#switch4:checked ~ #slideshow .slideContents #slide4 p {
	animation: pAppear 1.3s;
}

#switch4:checkedのh1要素とp要素だけにanimationを指定する事で、真ん中にスライドする時だけ、アニメーションが適用されます。※キーフレームの定義は46行め〜

いろいろアニメーション

これも、スライドする時の動きは変えず、h1要素とp要素に、個別にアニメーションを適用しています。
h1要素にはその中にspan要素を入れてて、h1には上下に跳ねるアニメーションを指定し、spanには前後に回転するアニメーションを指定することで、宙返りするアニメーションを表現しています。

p要素は、translate()で上下左右に移動と、rotate()で傾きを、アニメーション具合を確認しながら、良い感じなランダムになるよう適宜、微調整。

以上!ほとんど適宜、微調整です…。ちょっと考えて、ちょっと試して、ちょっと考えて、ちょっとずついじりながら動きを作ってゆくと楽しいですよー;)

Comment & Pingback

2 Comments! for スライドショー

  1. ono

    こんにちは! 最近になってですがサイト制作を始めた身としては大変参考になります。とてもありがたいです!
    そこで質問をさせていただきたいことがあるのですが、スライドショーの枚数を5枚以上に増やすにはどのような記述が必要となりますでしょうか?
    当サイト様に載せられたcssを自己流に改変して6枚のスライドショーをさせようとしてみたのですが、画像が表示されません。
    原文の通り5枚で試した際にはしっかり動作できたのですが、6枚の場合の問題点がわかりませんでした。
    厚かましいお願いであることは承知の上ですがどうかご教示お願い致します。

    Reply
    • _watercolor

      > onoさん
      コメントありがとうございます!リニューアルしてからの初コメントうれしいな:)
      さっそくご質問いただいた、スライドショーの画像の増やし方について。
      試しに、一番最初の「矢印ボタンの仕組み」のサンプルで、画像をひとつ増やす場合の変更点を、解説してゆきたいと思います:)

      まずHTMLの方は、以下の要領で、画像と矢印ボタン用の要素をひとつずつ増やします。
      増やす箇所は全部で4箇所。スイッチの役割をするinput要素と、スライドするsection要素と、左右の矢印ボタンをそれぞれひとつずつ追記します。
      input要素のid属性と、矢印ボタンのfor属性はそれぞれ、switch6にしておきます。
      それから、section要素のid属性slide6としておきます。

      1. <input type="radio" name="slideshow" id="switch5">
      2. <input type="radio" name="slideshow" id="switch6">
      3. <div class="slideContents">
      4. <section id="slide5">
      5. <img src="../img/slide_05.png">
      6. </section>
      7. <section id="slide6">
      8. <img src="../img/slide_06.png">
      9. </section>
      10. </div>
      11. <p class="arrow prev">
      12. <label for="switch5"></label>
      13. <label for="switch6"></label>
      14. </p>
      15. <p class="arrow next">
      16. <label for="switch5"></label>
      17. <label for="switch6"></label>
      18. </p>

      続いて、CSSも、HTML要素に合わせて修正しなきゃなのですが、ちょっと面倒くさいです…:(
      修正するのは大きく3箇所。以下の箇所を6つ用に変更していきます。

      • スライドするコンテナの横幅スライドひとつ分の横幅
      • スライドショーがスライドする位置
      • 矢印ボタンの出し分けの部分

      スライドするコンテナの横幅は、画像が5つから6つになることで、500%から600%に変更になります。
      スライドひとつ分の横幅は、コンテナの横幅を100%として、6つ画像が並ぶので、calc(100% / 6)という風に指定します。つまり、100%を6等分するということ。画像が5つの時、width: 20%としていたのは、width: calc(100% / 5)ということだったわけですねー:o

      calc()関数は、数値を計算してそのまま値としてくれる、CSSの特別な構文です。詳しくはこちらを参照のこと。

      1. /* :::::: slideContents :::::: */
      2. .slideContents {
      3. display: flex;
      4. position: relative;
      5. width: 600%;
      6. }
      7. .slideContents section {
      8. width: calc(100% / 6);
      9. }

      次に、スライドする位置の調整。こちらも、5等分ずつ移動していた位置を、6等分ずつ移動するように、スライドひとつ分の横幅と同じ要領で修正します。
      5等分ずつ移動していた時、2つめのコンテンツを表示するためにtransform: translateX(-20%)としていました。これははつまり、translateX(calc(-100% / 5))ということなので、6等分になるとtranslateX(calc(-100% / 6))ということになります。


      3つめのコンテンツを表示するときのtranslateX(-40%)は、translateX(calc(-100% / 5 * 2))ということなので、translateX(calc(-100% / 6 * 2))という風に、修正します。
      4つめ、5つめ、6つめの時も同じ要領で修正します。

      1. /* :::::: mechanism :::::: */
      2. #switch1:checked ~ .slideContents {
      3. transform: translateX(0);
      4. }
      5. #switch2:checked ~ .slideContents {
      6. transform: translateX(calc(-100% / 6));
      7. }
      8. #switch3:checked ~ .slideContents {
      9. transform: translateX(calc(-100% / 6 * 2));
      10. }
      11. #switch4:checked ~ .slideContents {
      12. transform: translateX(calc(-100% / 6 * 3));
      13. }
      14. #switch5:checked ~ .slideContents {
      15. transform: translateX(calc(-100% / 6 * 4));
      16. }
      17. #switch6:checked ~ .slideContents {
      18. transform: translateX(calc(-100% / 6 * 5));
      19. }

      最後に、矢印ボタンの出し分けの部分。ここは、input要素の#switch6がひとつ増えたことで、順繰りになっていたlabel要素とfor属性の関係を、ひとつズラします。
      右矢印(進む)ボタンは、#switch5がチェックされている時、<label for="switch1">ではなく<label for="switch5">を有効にして、#switch6がチェックされている時に、<label for="switch1">を有効にするよう変更します。

      1. #switch1:checked ~ .next label[for="switch2"],
      2. #switch2:checked ~ .next label[for="switch3"],
      3. #switch3:checked ~ .next label[for="switch4"],
      4. #switch4:checked ~ .next label[for="switch5"],
      5. #switch5:checked ~ .next label[for="switch6"],
      6. #switch6:checked ~ .next label[for="switch1"] {
      7. pointer-events: auto;
      8. }

      同じく、左矢印(戻る)ボタンは、#switch1がチェックされている時、<label for="switch5">ではなく<label for="switch6">を有効にして、#switch6がチェックされている時に、<label for="switch5">が有効になるようにします。

      1. #switch1:checked ~ .prev label[for="switch6"],
      2. #switch2:checked ~ .prev label[for="switch1"],
      3. #switch3:checked ~ .prev label[for="switch2"],
      4. #switch4:checked ~ .prev label[for="switch3"],
      5. #switch5:checked ~ .prev label[for="switch4"],
      6. #switch6:checked ~ .prev label[for="switch5"] {
      7. pointer-events: auto;
      8. }

      …という修正を経て、以下のようになりました!


      こんな感じで、CSSでのスライドショーは画像をひとつ増やすだけでも、いろいろと面倒なのがたまきずXp
      実は、サンプルの画像の数を5つにしたのには理由があって、計算しやすいからだったんです…X|。CSSで作るスライドショーは、その時の画像の枚数で決め打ちで作るしかないので、柔軟性に欠けるんですね。
      なので、スライドショーを作るならば、特別な理由がない限り、JavaScriptで実装した方が、使い勝手はよくなりますX)

      画像が6つの場合のサンプルをダウンロードする

      てなもんで、今後ともLopan.jpをどうぞよろしくです!

      Reply

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください