そろそろ本気で CSS3 アニメーション [その1 – Cap]

ブラウザ上でのちょっとしたアニメーション、っていうと長い間 jQuery がそのデファクトの座にいたわけですが、モバイルだと重いし、各ブラウザの CSS3 実装も進んできたし、そろそろ CSS3 だよね!
というわけで、重い腰を上げて本気で CSS3 アニメーション書いてみました。いろいろ調べながら書いているので、ダメな所とか突っ込み歓迎です。

今回は連載の第 1 回目、”Cap” と題して、アイコンに蓋をかぶせてみたいと思います。

※注意!
ページの下の方にループしているアニメーションがありますが、そのせいで長時間ページを開いたままにしていると、Chrome / Firefox 共に少しずつ重たくなってきます。特に Firefox の方が顕著です。
おそらく何らかのバグではないかと思うのですが、将来的に解消されるまでは、ページが重たくなったらタブを閉じて開きなおして下さい。リロードでも解消するかと思いきや、Chrome の場合リロードだけでは解消しない事があるようです。

とりあえず完成品を見てみる場合はこちら。

BASE

まずはベースとなる HTML です。本連載を通じて、この HTML の基本は変えずに行こうと思います。

<ul class="css3anime">
	<li></li>
	<li></li>
	<li></li>
	<li></li>
	<li></li>
</ul>

この HTML に CSS を適用します。

ul.css3anime {
	display: inline-block;
	margin: 0;
	padding: 10px;
	border: solid 1px #666;
	border-radius: 10px;
	background-color: #333;
}
ul.css3anime:after {
	display: block;
	content: "";
	clear: both;
}
ul.css3anime li {
	list-style-type: none;
	width: 128px;
	height: 128px;
	background-image: url(browser_logo_128.png);
	float: left;
}
ul.css3anime li:nth-child(2) {background-position: -128px 0;}
ul.css3anime li:nth-child(3) {background-position: -256px 0;}
ul.css3anime li:nth-child(4) {background-position: -384px 0;}
ul.css3anime li:nth-child(5) {background-position: -512px 0;}

とりあえずデザインする範囲に枠を付けて、li を横に並べて、CSS スプライトで背景をセットしただけです。
背景画像は透過 PNG を使っています。
ここで詰まってしまった方は、「clearfix」「float レイアウト」「CSS スプライト」「nth-child 疑似クラス」でググると幸せになれます。

見た目はこんな感じです。
大前提として、表示確認は Chrome Ver.21 と Firefox Ver.15 (いずれも現時点での最新版) で行っています。
それ以外のブラウザのサポートについては、とりあえず考えずに進みます。

STEP1

まずは、アイコンを丸く切り取りました。

HTML の基本を変えないと言いつつ、いきなり変えてしまっています。

<ul class="css3anime">
	<li><span></span></li>
	<li><span></span></li>
	<li><span></span></li>
</ul>

5 つのアイコンを 3 つに減らしたのは、デザイン上の幅の問題ですが、span を追加したのはやむにやまれぬ訳あっての事です。
このアイコンの上に「蓋」をかぶせていくために、当初 :before と :after でやろうとしたのですが、これらの疑似クラスに対しては、アニメーションやグラデーションのスタイルが中途半端にしか適用されなかったためです。
将来の実装では対応されるかもしれないので、そうなったら span は不要になりますね。

ul.css3anime li {
	background-color: #fff;
	margin-right: 5px;
	border: solid 1px #666;
	border-radius: 64px;
}

CSS についてはこの先、追加の差分だけ記載していきます。
ここはまだ、透過 PNG の後ろを白くして、アイコンのサイズに合わせて角を丸めただけですね。まだ全然アニメーションの話には入れません。ちゃっちゃと次に行きましょう。
ここで詰まってしまった方は、「CSS 角丸」でググると幸せになれます。

STEP2

アイコンに蓋をしました。

HTML は変えず、span にスタイルを適用しただけです。

ul.css3anime li span {
	display: block;
	width: 128px;
	height: 128px;
	border: 1px solid #333;
	border-radius: 64px;
	background-image: -webkit-linear-gradient(bottom, #999 49%, #333 50%, #999 51%);
	background-image: -moz-linear-gradient(bottom, #999 49%, #333 50%, #999 51%);
}

このあと蓋を傾ける予定なので、傾きが分かりやすいように横線を入れています。少し CSS3 っぽくなってきましたが、アニメーションはまだです。はい、次に行きましょう。
ここで詰まってしまった方は、「CSS グラデーション」でググると幸せになれます。

STEP3

蓋をちょっと傾けました。

span に対して、回転軸を指定した傾きを与えています。
回転軸の所にポッチを足すことで、視覚的にも分かりやすくしてあります。

ul.css3anime li span {
	position: absolute;
	-webkit-transform: rotate(-30deg);
	-moz-transform: rotate(-30deg);
	-webkit-transform-origin: 50% 8px; 
	-moz-transform-origin: 50% 8px;
}
ul.css3anime li:after {
	display: block;
	position: absolute;
	content: "";
	width: 8px;
	height: 8px;
	border-radius: 4px;
	background-color: #333;
	margin-top: 4px;
	margin-left: 60px;
	z-index: 9;
}

transform: rotate で、要素の傾きを指定することが出来ます。ここでは -30 度の傾きです。
そして、要素を傾けるときの中心を transform-origin で指定します。ここでは左右の真ん中、上から 8px の位置です。

ポッチについては li の after 疑似要素で作りました。
正確な位置に重ねるため、position: absolute を li:after と span の両方に加え、margin で位置を調整しています。
また、z-index で span の上に持ってくることも忘れません。この 9 はまた後で効いてきます。

さあ、いよいよ次はアニメーションします。張り切ってまいりましょう!
ここで詰まってしまった方は、「CSS 回転」「CSS 疑似要素」でググると幸せになれます。

STEP4

あれ?蓋が閉じてしまいました。大丈夫。オンマウスで蓋が傾きます。

やりました!とりあえずアニメーションしてますね。

ul.css3anime li {
	cursor: pointer;
}
ul.css3anime li span {
	-webkit-transform: rotate(0);
	-moz-transform: rotate(0);
	-webkit-transition: all 0.2s linear;
	-moz-transition: all 0.2s linear;
}
ul.css3anime li:hover span {
	-webkit-transform: rotate(-30deg);
	-moz-transform: rotate(-30deg);
	-webkit-transition: all 0.2s linear;
	-moz-transition: all 0.2s linear;
}
ul.css3anime li:nth-child(1) span {z-index: 3;}
ul.css3anime li:nth-child(2) span {z-index: 2;}
ul.css3anime li:nth-child(3) span {z-index: 1;}

まず、hover でアニメーションさせたので、さしあたってカーソルのスタイルを変えています。

そして通常状態での span の回転角度を 0 度に戻し、hover 時に角度を付けるようにします。
ここで、span:hover ではなく li:hover を使うのは、span が動いたときにマウスカーソルが span から外れて、ピクピクしてしまわないようにです。
動かす要素自体に hover 指定しないのがポイントです。

そしてキモになるのが transition です。ここでは、全てのスタイルに対して 0.2 秒かけて直線的にアニメーションせよ、という指定をしています。
:hover 側の指定は hover したときのアニメーションで、通常側の指定は hover から戻った時のアニメーションです。
どちらか一方だけの指定では、片方がアニメーションせずにスタイルが適用されてカクカクした動きになってしまうので、双方向で指定します。

z-index の指定は、蓋がずれて隣の蓋の上に重なったときの上下を指定しています。
デフォルトでは後にある要素が上に来て、右側に回転させて蓋を開けたときに右側の要素の下に蓋が滑り込んでしまうので、それを是正しています。

ここでの詳しい解説は、「CSS transition」でググると幸せになれます。

STEP5

おお、なにやら最初から動いていますね!これぞアニメーションです。

hover では、マウスを持ってくるまでそこがクリック可能である事が分からないので、最初からアニメーションでチラ見せするようにしてみました。

@-webkit-keyframes css3-cap-5-kf {
	  0% { -webkit-transform: rotate(0); }
	 40% { -webkit-transform: rotate(0); }
	 50% { -webkit-transform: rotate(-15deg); }
	 60% { -webkit-transform: rotate(0); }
	100% { -webkit-transform: rotate(0); }
}
@-moz-keyframes css3-cap-5-kf {
	  0% { -moz-transform: rotate(0); }
	 40% { -moz-transform: rotate(0); }
	 50% { -moz-transform: rotate(-15deg); }
	 60% { -moz-transform: rotate(0); }
	100% { -moz-transform: rotate(0); }
}
ul.css3anime li span {
	-webkit-animation-name: css3-cap-5-kf;
	-moz-animation-name: css3-cap-5-kf;
	-webkit-animation-duration: 2s;
	-moz-animation-duration: 2s;
	-webkit-animation-iteration-count: infinite;
	-moz-animation-iteration-count: infinite;
}
ul.css3anime li:hover span {
	-webkit-transform: rotate(-135deg);
	-moz-transform: rotate(-135deg);
	-webkit-animation-name: none;
	-moz-animation-name: none;
}

こういった自由なアニメーションを実現するためにはまず、CSS 上でキーフレームというものを定義する必要があります。
ここでは、css3-cap-5-kf という名前を付けてキーフレームを定義しています。
パーセントで時間の経過を表し、何パーセントの時に要素にどんなスタイルを適用するかを定義します。
0%〜40% までの間は角度 0 でじっとしていて、50% で 15 度までぴくっと動き、また 60%〜100% まではじっとしている、という感じです。

このように定義したキーフレームを要素に適用するには animation-name にキーフレーム名を指定します。
さらにアニメーションにかかる時間を animation-duration で、アニメーションを繰り返す回数を animation-iteration-count で指定します。
ここではアニメーションにかかる時間を 2 秒としているので、ちょうど 1 秒経ったところ (50% のところ) でぴくっと動きます。繰り返し回数は無限です。

また hover 時のスタイルについては、大きく蓋が開くように角度を 135 度まで広げ、その間はアニメーションをストップするよう、animation-name: none としています。

ここでの詳しい解説は、「CSS keyframes」「CSS animation」でググると幸せになれます。

※残念なことに Chrome の実装にバグがあるようで、hover の一度目は綺麗にアニメーションするのですが、二度目以降はアニメーションしません。残念ですね。
Firefox はちゃんと動いています。

STEP6

最初に戻る場合はこちら。

これで完成です。デザインが殺風景だったのでグラデーションとか影とか付けてみました。

見た目の問題なので詳しくは解説しません。最後に CSS の全体をまとめておくので興味のある方は読み下してみてください。

ul.css3anime {
	display: inline-block;
	margin: 0;
	padding: 10px;
	border: solid 1px #666;
	border-radius: 10px;
	background-color: #333;
}
ul.css3anime:after {
	display: block;
	content: "";
	clear: both;
}
ul.css3anime li {
	list-style-type: none;
	width: 128px;
	height: 128px;
	background-image: url(browser_logo_128.png);
	float: left;
	background-color: #fff;
	margin-right: 5px;
	border: solid 1px #666;
	border-radius: 64px;
	box-shadow: 0 10px 10px rgba(64, 64, 64, 0.5) inset, 0 2px 3px rgba(192, 192, 192, 0.8);
	cursor: pointer;
}
ul.css3anime li:nth-child(2) {background-position: -128px 0;}
ul.css3anime li:nth-child(3) {background-position: -256px 0;}
ul.css3anime li:nth-child(4) {background-position: -384px 0;}
ul.css3anime li:nth-child(5) {background-position: -512px 0;}
ul.css3anime li:nth-child(1) span {z-index: 3;}
ul.css3anime li:nth-child(2) span {z-index: 2;}
ul.css3anime li:nth-child(3) span {z-index: 1;}
ul.css3anime li span {
	display: block;
	position: absolute;
	width: 128px;
	height: 128px;
	border: 1px solid #333;
	border-radius: 64px;
	background-image: -webkit-linear-gradient(bottom, #666 0%, #ccc 100%);
	background-image: -moz-linear-gradient(bottom, #666 0%, #ccc 100%);
	box-shadow: 0 0 3px rgba(64, 64, 64, 0.8), 0 -5px 3px rgba(64, 64, 64, 0.8) inset, 0 -7px 2px rgba(255, 255, 255, 0.5) inset, 0 1px 2px #666 inset, 0px 8px 5px rgba(255, 255, 255, 0.7) inset;
	-webkit-transform: rotate(0);
	-moz-transform: rotate(0);
	-webkit-transform-origin: 50% 8px; 
	-moz-transform-origin: 50% 8px;
	-webkit-transition: all 0.2s linear;
	-moz-transition: all 0.2s linear;

	-webkit-animation-name: css3-cap-6-kf;
	-moz-animation-name: css3-cap-6-kf;
	-webkit-animation-duration: 2s;
	-moz-animation-duration: 2s;
	-webkit-animation-iteration-count: infinite;
	-moz-animation-iteration-count: infinite;
}
@-webkit-keyframes css3-cap-5-kf {
	  0% { -webkit-transform: rotate(0); }
	 40% { -webkit-transform: rotate(0); }
	 50% { -webkit-transform: rotate(-15deg); }
	 60% { -webkit-transform: rotate(0); }
	100% { -webkit-transform: rotate(0); }
}
@-moz-keyframes css3-cap-5-kf {
	  0% { -moz-transform: rotate(0); }
	 40% { -moz-transform: rotate(0); }
	 50% { -moz-transform: rotate(-15deg); }
	 60% { -moz-transform: rotate(0); }
	100% { -moz-transform: rotate(0); }
}
ul.css3anime li:hover span {
	-webkit-transform: rotate(-135deg);
	-moz-transform: rotate(-135deg);
	-webkit-transition: all 0.2s linear;
	-moz-transition: all 0.2s linear;

	-webkit-animation-name: none;
	-moz-animation-name: none;
}
ul#css3-cap-6 li:after {
	display: block;
	position: absolute;
	content: "";
	width: 8px;
	height: 8px;
	border-radius: 4px;
	background-color: #333;
	margin-top: 4px;
	margin-left: 60px;
	background-image: -webkit-linear-gradient(bottom, #333 0%, #999 60%, #fff 80%, #fff 100%);
	background-image: -moz-linear-gradient(bottom, #333 0%, #999 60%, #fff 80%, #fff 100%);
	box-shadow: 0 1px 2px #333 inset, 0 1px 4px rgba(255, 255, 255, 0.7), 0 -1px 1px rgba(0, 0, 0, 0.7);
	z-index: 9;
}

まとめ

「そろそろ本気で CSS3 アニメーション」連載第 1 回の “Cap” はいかがだったでしょうか?
こういったことが CSS で可能になった事は把握しながら、これまで手つかずだったので、個人的にはとても楽しみながら作ることが出来ました。
今後の構想としては、ありきたりなテーマではありますが、”Dock”、”3D Cube”、”Cover Flow” あたりをやってみたいなと思っています。

興味を持ってもらえたら期待を込めてシェア!よろしくお願いします。

コメントを残す

メールアドレスが公開されることはありません。