スライドショー

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

矢印ボタンの仕組み

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

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

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

つまり、

  • ひとつめのコンテンツが表示されている時の右矢印ボタンは#switch2がチェックできる状態
  • ふたつめのコンテンツが表示されている時の右矢印ボタンは#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プロパティを使います。
pointer-eventsプロパティは、ポインティングデバイスで起こる、クリックマウスオーバータッチなどのポインターイベントを制御することができるプロパティ。この値をnoneで指定すると、その要素のポインターイベントを無効にすることができます。※初期値はautoで、操作が有効の状態。
これを使って、label要素のポインターイベントを一旦、無効にしておきます。

.arrow label {
	pointer-events: none;
}

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

#switch1:checked ~ .next label[for="switch2"],
#switch2:checked ~ .next label[for="switch3"],
#switch3:checked ~ .next label[for="switch4"],
#switch4:checked ~ .next label[for="switch5"],
#switch5:checked ~ .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

コンテンツの数を変えたい時

コンテンツを5つじゃなくて4つとか6つにしたい時ももちろんあるでしょう。コンテンツの数によって変更しなきゃいけないHTMLとCSSのポイントをまとめます。
ここでは、5つから6つに増やしたい時の変更点を見てゆきます。

HTML

まずHTMLは、コンテンツがひとつ増えた分、.slideContentssection要素をひとつ増やします。id属性も違う値に変える点に注意(ここではslide6にします)

<div class="slideContents">
	<section id="slide1"><img src="../img/slide_01.png"></section>
		⋮
	<section id="slide5"><img src="../img/slide_05.png"></section>
	<section id="slide6"><img src="../img/slide_06.png"></section>
</div>
section要素をひとつ追加

同じように、ラジオボタンと、左右矢印内のラベルもひとつずつ増やします。それぞれ、id属性for属性も合わせて変更する点に注意(ここではswitch6にします)

<input type="radio" name="slideshow" id="switch1" checked>
	⋮
<input type="radio" name="slideshow" id="switch5">
<input type="radio" name="slideshow" id="switch6">
ラジオボタンをひとつ追加
<p class="arrow prev">
	<i class="ico"></i>
	<label for="switch1"></label>
	⋮
	<label for="switch5"></label>
	<label for="switch6"></label>
</p>
<p class="arrow next">
	<i class="ico"></i>
	<label for="switch1"></label>
	⋮
	<label for="switch5"></label>
	<label for="switch6"></label>
</p>
label要素をひとつずつ追加

CSS

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

  • スライドするコンテナの横幅とスライドひとつ分の横幅
  • コンテナをスライドさせる位置
  • 矢印ボタンの出し分け方

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

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

.slideContents {
	width: 600%;
}
.slideContents section {
	width: calc(100% / 6);
}
コンテナの横幅と、スライドひとつ分の横幅

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

#switch1:checked ~ .slideContents {
	transform: translateX(0);
}
#switch2:checked ~ .slideContents {
	transform: translateX(calc(-100% / 6));
}
#switch3:checked ~ .slideContents {
	transform: translateX(calc(-100% / 6 * 2));
}
#switch4:checked ~ .slideContents {
	transform: translateX(calc(-100% / 6 * 3));
}
#switch5:checked ~ .slideContents {
	transform: translateX(calc(-100% / 6 * 4));
}
#switch6:checked ~ .slideContents {
	transform: translateX(calc(-100% / 6 * 5));
}
スライドさせる位置

最後に、矢印ボタンの出し分けの部分。左矢印(戻る)ボタンの最初と、右矢印(進む)ボタンの最後だけ、ちょっと変更します。
戻るボタンは、「コンテンツ1」が表示されている時に<label for="switch5">ではなく<label for="switch6">をチェックできるようにして、「コンテンツ6」が表示されている時に<label for="switch5">をチェックできるようする指定を追加します。

#switch1:checked ~ .prev label[for="switch6"],
#switch2:checked ~ .prev label[for="switch1"],
	⋮
#switch6:checked ~ .prev label[for="switch5"],
戻るボタンの変更点

進むボタンも同じように、「コンテンツ5」が表示されている時に<label for="switch1">ではなく<label for="switch6">をチェックできるようにして、「コンテンツ6」が表示されている時に<label for="switch1">をチェックできるようにする指定を追加します。

#switch1:checked ~ .next label[for="switch2"],
	⋮
#switch5:checked ~ .next label[for="switch6"],
#switch6:checked ~ .next label[for="switch1"] {
	pointer-events: auto;
}
進むボタンの変更点

以上で6つコンテンツのスライドショーに変更完了ー!:D
矢印ボタンの変更点がちょっとややこやしいけれど、「最初の戻るボタンは最後のコンテンツへ行き、最後の進むボタンは最初のコンテンツへ行く」と覚えると良いです:)

同じ方向にスライド

コンテンツ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 ~ .slideContents #slide1,
#switch2:checked ~ .slideContents #slide2,
#switch3:checked ~ .slideContents #slide3,
#switch4:checked ~ .slideContents #slide4,
#switch5:checked ~ .slideContents #slide5 {
	z-index: 1;
	position: relative;
	transform: translateX(0);
}

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

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

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

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

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

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

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

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

端から端へ移動する時がいつかというと、「コンテンツ1」が真ん中にスライドしてくる時のスライドの位置を見てみましょう。
「コンテンツ5」表示中に進むボタンを押して「コンテンツ1」が真ん中へ移動すると、左側のコンテンツは、3/4から4/5に切り替わるので、3が右側へ移動することになります。
「コンテンツ2」表示中に戻るボタンを押して「コンテンツ1」が真ん中へ移動すると、左側のコンテンツは、5/1から4/5に切り替わるので、4が右側へ移動することになります。
つまり、真ん中へ移動するコンテンツのふたつ手前とみっつ手前のコンテンツを瞬間移動させればいいっぽいです!

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

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

シームレスでもコンテンツの数を変えたい時

同じ方向にスライドさせるタイプでも、コンテンツが4つになったり6つになったりもする事もあるでしょう。そんな時に変更しなきゃいけないHTMLとCSSのポイントをまとめます。
さっきと同じく5つから6つに増やしたい時の変更点を見てゆきます。

HTML

HTMLは、さっきと全く同じ。コンテンツがひとつ増えた分、section要素input要素と、矢印の中のlabel要素ををひとつずつ増やします。id属性for属性も違う値(ここもさっきと同じslide6switch6に変えます。

<div class="slideContents">
	<section id="slide1"><img src="../img/slide_01.png"></section>
		⋮
	<section id="slide5"><img src="../img/slide_05.png"></section>
	<section id="slide6"><img src="../img/slide_06.png"></section>
</div>
section要素をひとつ追加(※その他の要素もさっきと同様に追加)

CSS

矢印ボタンの出し分け方は、さっきと一緒。
あとは、コンテンツが左側にある時と、コンテンツを瞬間移動させる時を指定し直します。

左側に配置するコンテンツは、表示中のコンテンツの手前ふたつのコンテンツという事だったので、「コンテンツ1」を表示中の時は#slide6#slide5、「コンテンツ2」を表示中の時は#slide1#slide6…という具合に修正。そして「コンテンツ6」を表示中の時は#slide5#slide4ということになります。

#switch1:checked ~ .slideContents #slide5,
#switch1:checked ~ .slideContents #slide6,
#switch2:checked ~ .slideContents #slide6,
#switch2:checked ~ .slideContents #slide1,
	⋮
#switch6:checked ~ .slideContents #slide4,
#switch6:checked ~ .slideContents #slide5 {
	transform: translateX(-100%);
}
表示中の手前ふたつを左側へ移動する

瞬間移動させなきゃいけないコンテンツは、真ん中へ移動するコンテンツのふたつ手前とみっつ手前のコンテンツという事だったので、「コンテンツ1」が真ん中にくる時は#slide5#slide4、「コンテンツ2」が真ん中にくる時は#slide6#slide5…という具合。そして「コンテンツ6」が真ん中にくる時は#slide4#slide3を瞬間移動するようにします。

#switch1:checked ~ .slideContents #slide4,
#switch1:checked ~ .slideContents #slide5,
#switch2:checked ~ .slideContents #slide5,
#switch2:checked ~ .slideContents #slide6,
#switch3:checked ~ .slideContents #slide6,
#switch3:checked ~ .slideContents #slide1,
	⋮
#switch6:checked ~ .slideContents #slide3,
#switch6:checked ~ .slideContents #slide4 {
	transition-duration: 0s;
}
表示中の手前ふたつを左側へ移動する

コンテンツを6つにできましたね;D

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では同じパーセント値を指定すると、後に書いた指定が優先されてしまうので、パーセント値をほんのちょっとズラして、瞬間的に移動するようにしてます。
transform: translateX(0)という事はtransform: noneという事だし、同じ内容のところはまとめちゃえるので、以下のような書き方でも大丈夫です。

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

アニメーション

そして、上で定義した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ルールで定義したスタイルの方が優先されるので、矢印ボタンで切り替えてもびくともしないんですね…。自動スライドと矢印ボタンの併用はできなさそう…¦(

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

ドロワーメニュー