今回のお題は、画像を縦のパネルに分けて切り替えるアニメーションだ。数字のボタンを押すと、分かれたパネルが伸縮してクロスフェードする(サンプル1 ) 。「 Sliding Image Panels with CSS3 」の表現を参考にして、HTMLやCSSの構成を改めるとともに、とくにCSSのコードはできるかぎり絞り込んだ。仕組みがわかりやすくなるよう心がけた。
サンプル1 CSS3: Sliding image panels
画像とキャプションを切り替える
<body>
要素に書くコードは、つぎのように単純な構成で始めよう。type
属性を"radio"とした<input>
と<label>
の要素がボタンとなる。キャプションはh3
要素の中に、<span>
要素でタイトル(class
属性"img-title")と説明書き(class
属性"img-caption")をそれぞれ加えた。これらの<h3>
要素はさらに<div>
要素(class
属性"img-titles")に含めてある。注目してほしいのは<img>
要素がないことだ。画像は空の<div>
要素(class
属性"bg-img")にCSSで背景として加えるからだ。
<div class="container">
<input id="select-img-1" name="radio-set-1" type="radio" checked/>
<label for="select-img-1" class="label-img-1">1</label>
<input id="select-img-2" name="radio-set-1" type="radio" />
<label for="select-img-2" class="label-img-2">2</label>
<input id="select-img-3" name="radio-set-1" type="radio" />
<label for="select-img-3" class="label-img-3">3</label>
<input id="select-img-4" name="radio-set-1" type="radio" />
<label for="select-img-4" class="label-img-4">4</label>
<div class="bg-img">
</div>
<div class="img-titles">
<h3><span class="img-title">Child</span><span class="img-caption">Single emperor penguin chick</span></h3>
<h3><span class="img-title">Adult</span><span class="img-caption">Single penguin on a piece of ice</span></h3>
<h3><span class="img-title">Family</span><span class="img-caption">Emperor penguin with chicks</span></h3>
<h3><span class="img-title">Children</span><span class="img-caption">Emperor penguin chicks</span></h3>
</div>
</div>
この<body>
要素の記述に対して、後に掲げるコード1 の<style>
要素を与える。これで、ボタンを押せば画像とキャプションはとりあえず切り替わる(図1 ) 。ただし、切り替えの効果はまだ加えていない。
図1 ボタンで画像とキャプションが切り替わる
画像とキャプションが切り替わる仕組みを、先に確かめておこう。選ばれたラジオボタン(type
属性"radio"の<input>
要素)は、:checked
擬似クラス でとれる。そこで、つぎに抜き書きしたコードのとおり、対応した画像のURLをurl()
関数 でbackground-image
プロパティ に定めた。
#select-img-1:checked ~ .bg-img {
background-image: url(images/image_001.png);
}
#select-img-2:checked ~ .bg-img {
background-image: url(images/image_002.png);
}
#select-img-3:checked ~ .bg-img {
background-image: url(images/image_003.png);
}
#select-img-4:checked ~ .bg-img {
background-image: url(images/image_004.png);
}
キャプションの要素(class
属性"img-title"と"img-caption")は、つぎのように初めはすべてopacity
プロパティで透明にしておく。そして、やはり選ばれたラジオボタンに応じたキャプションの<h3>
要素を一般兄弟(間接)セレクタ~
と:nth-child()
擬似クラス で取り出して不透明にすればよい。
.img-title, .img-caption {
opacity: 0;
}
#select-img-1:checked ~ .img-titles h3:nth-child (1) span,
#select-img-2:checked ~ .img-titles h3:nth-child (2) span,
#select-img-3:checked ~ .img-titles h3:nth-child (3) span,
#select-img-4:checked ~ .img-titles h3:nth-child (4) span {
opacity: 1;
}
<style>
要素の定めは、つぎのコード1 にまとめたとおりだ。なお、CSSにベンダープレフィックスを付けなくて済むように、<script>
要素で-prefix-free を読み込んでいる(第1回の「ベンダープレフィックスと-prefix-free 」の項参照) 。また、Google Fonts のOswaldを使った(第5回の「Google Fontsと回り込みの解除 」参照) 。
コード1 ボタンのクリックで画像とキャプションを切り替える
<script src="lib/prefixfree.min.js" type="text/javascript"></script>
<link href="https://fonts.googleapis.com/css?family=Oswald:400,700" rel="stylesheet" type="text/css">
<style>
body {
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
background: aliceblue;
}
.container {
width: 400px;
height: 267px;
position: relative;
margin: 0 auto;
text-align: center;
border: 15px solid white;
box-shadow : 1px 1px 3px rgba(0, 0, 0, 0.1);
}
.container label {
font-style: italic;
width: 100px;
height: 30px;
cursor: pointer;
color: white;
line-height: 24px;
font-size: 20px;
float: left;
position: relative;
margin-top: 230px;
z-index: 100;
}
.container label::before {
content: '';
width: 24px;
height: 24px;
background: rgba(135, 206, 235, 0.9);
position: absolute;
left: 50%;
margin-left: -12px;
border-radius : 50%;
box-shadow : 0px 0px 0px 4px rgba(255, 255, 255, 0.3);
z-index: -1;
}
.container label:after {
content: '';
width: 1px;
height: 267px;
background: linear-gradient (to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
position: absolute;
bottom: -20px;
right: 0px;
}
.container input {
display: none;
}
.bg-img {
width: 400px;
height: 267px;
position: absolute;
}
#select-img-1:checked ~ .bg-img {
background-image: url(images/image_001.png);
}
#select-img-2:checked ~ .bg-img {
background-image: url(images/image_002.png);
}
#select-img-3:checked ~ .bg-img {
background-image: url(images/image_003.png);
}
#select-img-4:checked ~ .bg-img {
background-image: url(images/image_004.png);
}
.img-title, .img-caption {
font-weight: normal;
color: white;
z-index: 100;
position: absolute;
width: 100%;
left: 0px;
opacity: 0;
top: 90px;
}
.img-title {
left: 0px;
font-family: "Oswald", sans-serif;
font-size: 48px;
letter-spacing: 3px;
}
.img-caption {
margin-top: 100px;
letter-spacing: 0px;
background: rgba(130, 195, 217, 0.9);
font-size: 14px;
padding: 4px 0px;
font-style: italic;
}
#select-img-1:checked ~ .img-titles h3:nth-child(1) span,
#select-img-2:checked ~ .img-titles h3:nth-child(2) span,
#select-img-3:checked ~ .img-titles h3:nth-child(3) span,
#select-img-4:checked ~ .img-titles h3:nth-child(4) span {
opacity: 1;
}
</style>
擬似要素::beforeと::afterで加えたデザイン
前掲コード1 には擬似要素::before
と::after
で加えたデザインがあるので補っておく。円形のボタンの外枠と画像を4等分する縦線だ(図2 ) 。どちらも<label>
要素に与えている。
図2 擬似要素::beforeと::afterで加えたボタンの丸い枠と画像の4等分線
::before
擬似要素がボタンの丸い枠だ。border-radius
プロパティ を50%に定めるとかたちが円になる。そのうえで、box-shadow
プロパティにより外枠の太さと色を定めた(第3回の「メニューバーに外枠を加える 」参照) 。
.container label::before {
content: '';
width: 24px;
height: 24px;
background: rgba(135, 206, 235, 0.9);
border-radius : 50%;
box-shadow : 0px 0px 0px 4px rgba(255, 255, 255, 0.3);
}
::after
擬似要素には縦線を定めた。色はlinear-gradient()
関数 で、下に向かって透明から白のグラデーションとした(第2回の「メニューに角の丸みと影を加える 」参照) 。
.container label:after {
content: '';
width: 1px;
height: 267px;
background: linear-gradient (to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
}
キャプションをクロスフェードさせる
前述のとおり、キャプションの要素(class
属性"img-title"と"img-caption")はopacity
プロパティ で表示と非表示を切り替えた。したがって、つぎのようにtransition
プロパティさえ加えれば、滑らかにクロスフェードする。
.img-title, .img-caption {
opacity: 0;
transition : 0.8s ease-in-out;
}
さらに、どの画像が表示されているのかわかるように、クリックされたボタンの色も変えよう。つぎのようにして、選ばれた<label>
要素の文字と、外枠(::before
擬似要素)の色を定めた。こちらはtransition
プロパティを加えていないので、クリックすればただちに切り替わる。
#select-img-1:checked ~ label.label-img-1,
#select-img-2:checked ~ label.label-img-2,
#select-img-3:checked ~ label.label-img-3,
#select-img-4:checked ~ label.label-img-4 {
color: deepskyblue;
}
#select-img-1:checked ~ label.label-img-1::before ,
#select-img-2:checked ~ label.label-img-2::before ,
#select-img-3:checked ~ label.label-img-3::before ,
#select-img-4:checked ~ label.label-img-4::before {
background: white;
box-shadow : 0px 0px 0px 4px rgba(0, 191, 255, 0.6);
}
これらの手を加えたCSSの定めが以下のコード2 だ。クリックしたボタンの色と画像が切り替わるとともに、キャプションはクロスフェードする(図3 ) 。
図3 キャプションがクロスフェードしてボタンの色も切り替わる
コード2 クリックしたボタンの色と画像を切り替えてキャプションはクロスフェードさせる
body {
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
background: aliceblue;
}
.container {
width: 400px;
height: 267px;
position: relative;
margin: 0 auto;
text-align: center;
border: 15px solid white;
box-shadow : 1px 1px 3px rgba(0, 0, 0, 0.1);
}
.container label {
font-style: italic;
width: 100px;
height: 30px;
cursor: pointer;
color: white;
line-height: 24px;
font-size: 20px;
float: left;
position: relative;
margin-top: 230px;
z-index: 100;
}
.container label::before {
content: '';
width: 24px;
height: 24px;
background: rgba(135, 206, 235, 0.9);
position: absolute;
left: 50%;
margin-left: -12px;
border-radius : 50%;
box-shadow : 0px 0px 0px 4px rgba(255, 255, 255, 0.3);
z-index: -1;
}
.container label::after {
content: '';
width: 1px;
height: 267px;
background: linear-gradient (to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
position: absolute;
bottom: -20px;
right: 0px;
}
#select-img-1:checked ~ label.label-img-1,
#select-img-2:checked ~ label.label-img-2,
#select-img-3:checked ~ label.label-img-3,
#select-img-4:checked ~ label.label-img-4 {
color: deepskyblue;
}
#select-img-1:checked ~ label.label-img-1::before ,
#select-img-2:checked ~ label.label-img-2::before ,
#select-img-3:checked ~ label.label-img-3::before ,
#select-img-4:checked ~ label.label-img-4::before {
background: white;
box-shadow : 0px 0px 0px 4px rgba(0, 191, 255, 0.6);
}
.container input {
display: none;
}
.bg-img {
width: 400px;
height: 267px;
position: absolute;
}
.bg-img div {
width: 100px;
height: 100%;
position: relative;
float: left;
overflow: hidden;
}
#select-img-1:checked ~ .bg-img {
background-image: url(images/image_001.png);
}
#select-img-2:checked ~ .bg-img {
background-image: url(images/image_001.png);
}
#select-img-3:checked ~ .bg-img {
background-image: url(images/image_001.png);
}
#select-img-4:checked ~ .bg-img {
background-image: url(images/image_001.png);
}
.img-title, .img-caption {
font-weight: normal;
color: white;
z-index: 100;
position: absolute;
width: 100%;
left: 0px;
opacity: 0;
top: 90px;
transition : 0.8s ease-in-out;
}
.img-title {
left: 0px;
font-family: "Oswald", sans-serif;
font-size: 48px;
letter-spacing: 3px;
}
.img-caption {
margin-top: 100px;
letter-spacing: 0px;
background: rgba(130, 195, 217, 0.9);
font-size: 14px;
padding: 4px 0px;
font-style: italic;
}
#select-img-1:checked ~ .img-titles h3:nth-child(1) span,
#select-img-2:checked ~ .img-titles h3:nth-child(2) span,
#select-img-3:checked ~ .img-titles h3:nth-child(3) span,
#select-img-4:checked ~ .img-titles h3:nth-child(4) span {
opacity: 1;
}
画像をパネルに分けてクロスフェードする
いよいよ画像を4つのパネルに分けて、伸縮しつつクロスフェードさせる。まず、HTMLにパネルの要素をつぎのように加える。4つのパネルは<div>
要素にして、それぞれに4つの画像用の<span>
要素を含めた。要素の中身は空なので、CSSで画像や位置を定める。
<div class="bg-img">
<div><!--Slice 1 -->
<span><!-- Image 1 --> </span>
<span><!-- Image 2 --> </span>
<span><!-- Image 3 --> </span>
<span><!-- Image 4 --> </span>
</div>
<div><!-- Slice 2 -->
<span><!-- Image 1 --> </span>
<span><!-- Image 2 --> </span>
<span><!-- Image 3 --> </span>
<span><!-- Image 4 --> </span>
</div>
<div><!-- Slice 3 -->
<span><!-- Image 1 --> </span>
<span><!-- Image 2 --> </span>
<span><!-- Image 3 --> </span>
<span><!-- Image 4 --> </span>
</div>
<div><!-- Slice 4 -->
<span><!-- Image 1 --> </span>
<span><!-- Image 2 --> </span>
<span><!-- Image 3 --> </span>
<span><!-- Image 4 --> </span>
</div>
</div>
それぞれの<span>
要素には、つぎのようにやはりbackground-image
プロパティで画像のURLを定め、パネルごとに位置決めした。さらに、transform
プロパティに与えたscale()
関数で拡大している。けれど、opacity
プロパティが透明(0)なので表示されず、ここまでは目に見える動きは変わらない。
.bg-img div {
width: 100px;
height: 100%;
position: relative;
float: left;
overflow: hidden;
}
.bg-img div span {
position: absolute;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
transform : scale (1.5);
opacity: 0;
}
#select-img-1:checked ~ .bg-img,
.bg-img div span:nth-child(1) {
background-image: url(images/image_001.png);
}
#select-img-2:checked ~ .bg-img,
.bg-img div span:nth-child(2) {
background-image: url(images/image_002.png);
}
#select-img-3:checked ~ .bg-img,
.bg-img div span:nth-child(3) {
background-image: url(images/image_003.png);
}
#select-img-4:checked ~ .bg-img,
.bg-img div span:nth-child(4) {
background-image: url(images/image_004.png);
}
.bg-img div:nth-child(1) span {
background-position: 0px 0px;
}
.bg-img div:nth-child(2) span {
background-position: -100px 0px;
}
.bg-img div:nth-child(3) span {
background-position: -200px 0px;
}
.bg-img div:nth-child(4) span {
background-position: -300px 0px;
}
そこで、画像の読み込まれた<span>
要素に、つぎのようにtransition
プロパティでアニメーションを加えた。そして、ボタンで選んだパネルの<span>
要素は、つぎのようにopacity
プロパティの値を1の不透明にすれば、画像もクロスフェードする。さらにその画像の<span>
要素は、transform
プロパティのscale()
関数に渡す値を実寸(1)に戻すことによりアニメーションに伸縮も加えた(図4 ) 。
.bg-img {
z-index: 1;
}
.container input:checked ~ .bg-img div span {
transition : 0.5s ease-in-out;
}
#select-img-1:checked ~ .bg-img div span:nth-child(1),
#select-img-2:checked ~ .bg-img div span:nth-child(2),
#select-img-3:checked ~ .bg-img div span:nth-child(3),
#select-img-4:checked ~ .bg-img div span:nth-child(4) {
opacity: 1;
transform : scale (1);
z-index: 100;
}
図4 パネルの画像が伸縮しながらクロスフェードする
これで、前掲サンプル1 の表現ができあがった。<body>
要素への記述とCSSの定めはつぎのコード3 にまとめている。CSSのコードは150行を超えて、少々長めとなった。だが、参考にした「Sliding Image Panels with CSS3 」と比べればかなり絞り込んである。
コード3 パネルに分けた画像を伸縮させながらクロスフェードする
<div class="container">
<input id="select-img-1" name="radio-set" type="radio" checked/>
<label for="select-img-1" class="label-img-1">1</label>
<input id="select-img-2" name="radio-set" type="radio" />
<label for="select-img-2" class="label-img-2">2</label>
<input id="select-img-3" name="radio-set" type="radio" />
<label for="select-img-3" class="label-img-3">3</label>
<input id="select-img-4" name="radio-set" type="radio" />
<label for="select-img-4" class="label-img-4">4</label>
<div class="bg-img">
<div><!--Slice 1 -->
<span><!-- Image 1 --> </span>
<span><!-- Image 2 --> </span>
<span><!-- Image 3 --> </span>
<span><!-- Image 4 --> </span>
</div>
<div><!-- Slice 2 -->
<span><!-- Image 1 --> </span>
<span><!-- Image 2 --> </span>
<span><!-- Image 3 --> </span>
<span><!-- Image 4 --> </span>
</div>
<div><!-- Slice 3 -->
<span><!-- Image 1 --> </span>
<span><!-- Image 2 --> </span>
<span><!-- Image 3 --> </span>
<span><!-- Image 4 --> </span>
</div>
<div><!-- Slice 4 -->
<span><!-- Image 1 --> </span>
<span><!-- Image 2 --> </span>
<span><!-- Image 3 --> </span>
<span><!-- Image 4 --> </span>
</div>
</div>
<div class="img-titles">
<h3><span class="img-title">Child</span><span class="img-caption">Single emperor penguin chick</span></h3>
<h3><span class="img-title">Adult</span><span class="img-caption">Single penguin on a piece of ice</span></h3>
<h3><span class="img-title">Family</span><span class="img-caption">Emperor penguin with chicks</span></h3>
<h3><span class="img-title">Children</span><span class="img-caption">Emperor penguin chicks</span></h3>
</div>
</div>
body {
font-family: "Palatino Linotype", "Book Antiqua", Palatino, serif;
background: aliceblue;
}
.container {
width: 400px;
height: 267px;
position: relative;
margin: 0 auto;
text-align: center;
border: 15px solid white;
box-shadow : 1px 1px 3px rgba(0, 0, 0, 0.1);
}
.container label {
font-style: italic;
width: 100px;
height: 30px;
cursor: pointer;
color: white;
line-height: 24px;
font-size: 20px;
float: left;
position: relative;
margin-top: 230px;
z-index: 100;
}
.container label::before {
content: '';
width: 24px;
height: 24px;
background: rgba(135, 206, 235, 0.9);
position: absolute;
left: 50%;
margin-left: -12px;
border-radius : 50%;
box-shadow : 0px 0px 0px 4px rgba(255, 255, 255, 0.3);
z-index: -1;
}
.container label::after {
content: '';
width: 1px;
height: 267px;
background: linear-gradient (to bottom, rgba(255, 255, 255, 0), rgba(255, 255, 255, 1));
position: absolute;
bottom: -20px;
right: 0px;
}
#select-img-1:checked ~ label.label-img-1,
#select-img-2:checked ~ label.label-img-2,
#select-img-3:checked ~ label.label-img-3,
#select-img-4:checked ~ label.label-img-4 {
color: deepskyblue;
}
#select-img-1:checked ~ label.label-img-1::before ,
#select-img-2:checked ~ label.label-img-2::before ,
#select-img-3:checked ~ label.label-img-3::before ,
#select-img-4:checked ~ label.label-img-4::before {
background: white;
box-shadow : 0px 0px 0px 4px rgba(0, 191, 255, 0.6);
}
.container input {
display: none;
}
.bg-img {
width: 400px;
height: 267px;
position: absolute;
z-index: 1;
}
.bg-img div {
width: 100px;
height: 100%;
position: relative;
float: left;
overflow: hidden;
}
.bg-img div span {
position: absolute;
width: 100%;
height: 100%;
top: 0px;
left: 0px;
transform : scale (1.5);
opacity: 0;
}
#select-img-1:checked ~ .bg-img,
.bg-img div span:nth-child(1) {
background-image: url(images/image_001.png);
}
#select-img-2:checked ~ .bg-img,
.bg-img div span:nth-child(2) {
background-image: url(images/image_002.png);
}
#select-img-3:checked ~ .bg-img,
.bg-img div span:nth-child(3) {
background-image: url(images/image_003.png);
}
#select-img-4:checked ~ .bg-img,
.bg-img div span:nth-child(4) {
background-image: url(images/image_004.png);
}
.bg-img div:nth-child(1) span {
background-position: 0px 0px;
}
.bg-img div:nth-child(2) span {
background-position: -100px 0px;
}
.bg-img div:nth-child(3) span {
background-position: -200px 0px;
}
.bg-img div:nth-child(4) span {
background-position: -300px 0px;
}
.container input:checked ~ .bg-img div span {
transition : 0.5s ease-in-out;
}
#select-img-1:checked ~ .bg-img div span:nth-child(1),
#select-img-2:checked ~ .bg-img div span:nth-child(2),
#select-img-3:checked ~ .bg-img div span:nth-child(3),
#select-img-4:checked ~ .bg-img div span:nth-child(4) {
opacity: 1;
transform : scale (1);
z-index: 100;
}
.img-title, .img-caption {
font-weight: normal;
color: white;
z-index: 100;
position: absolute;
width: 100%;
left: 0px;
opacity: 0;
top: 90px;
transition : 0.8s ease-in-out;
}
.img-title {
left: 0px;
font-family: "Oswald", sans-serif;
font-size: 48px;
letter-spacing: 3px;
}
.img-caption {
margin-top: 100px;
letter-spacing: 0px;
background: rgba(130, 195, 217, 0.9);
font-size: 14px;
padding: 4px 0px;
font-style: italic;
}
#select-img-1:checked ~ .img-titles h3:nth-child(1) span,
#select-img-2:checked ~ .img-titles h3:nth-child(2) span,
#select-img-3:checked ~ .img-titles h3:nth-child(3) span,
#select-img-4:checked ~ .img-titles h3:nth-child(4) span {
opacity: 1;
}