2011年4月18日月曜日

CSS桜ひらひらはこうして作りました

都内の桜ももう散りましたね。

先月、jsdo.itというJavascript, HTML5, CSSをブラウザ上でコード書いて共有できるサイトに、桜の花びらが舞うCSSアニメーションのコードをアップしました(Webkitブラウザ限定ですが)。

さーくらー ひらーひらー - jsdo.it

これのbookmarklet版のコードも公開してます。
桜ひらひらbookmarklet - jsdo.it

今SafariかChromeを利用している方なら、下記のリンクをクリックすればこのページ内を桜の花びらが舞います。

桜ひらひらbookmarklet

これをどうやって実現しているか今日は書きたいと思います。
まあ僕がやることはいつもたいしたことじゃありません。多少手間がかかることをちまちまやってるだけです。


■花びら
まず花びら。半分ずつ作ります。
下の画像はdivを加工していく過程で、左から右にどんどん適用style増えていきます。(見やすさのために実際の2倍の大きさにしてます)


左端は、width: 9px, height: 6px のdivを作りまして、position: absolute にしておき、そしてborder-radius: 6px 0px 0px 0px として左上だけ丸めた状態です。
次に -webkit-transform: skew(-30deg) でちょっと歪めてます。
さらに、-webkit-transform: skew(-30deg) scale(1.2, 1)で横方向1.2倍、縦方向1倍、要するに横にちょびっと引き伸ばします。
さらにさらに、-webkit-transform: skew(-30deg) scale(1.2, 1) rotate(12deg) で少し傾けます。
これに background: -webkit-gradient(linear, left top, right bottom, from(rgba(224, 176, 192, 1)), to(rgba(255, 240, 240, 1))) で色着けます。
見やすさのために付けてたborder外したのが一番右の画像。
これを左半分とします。

同じようにもう半分を作りますが、skewとscaleの縦方向の値の符号を反転したものを作ってあげます。
width: 9px, height: 6px, position: absolute, border-radius: 6px 0px 0px 0px のdivは同じですが、 -webkit-transform: skew(30deg) scale(1.2, -1) rotate(12deg) とします。色もグラデの向きがちょっと違い、 background: -webkit-gradient(linear, left bottom, right top, from(rgba(224, 176, 192, 1)), to(rgba(255, 240, 240, 1))) です。まあこの辺は、向きとか形とか実際に確認しながらやってます。



これら半分ずつを合わせて1枚の花びらにしたいので、同じdivの中に2つを入れます。
position: absoluteが効いてて見事に重なってくれてますので、少し位置を合わせるために、下半分の div を top: 4px 指定します。
これに色着けた状態でborder外したのが左から3番目のものです。
見やすさのために倍の大きさにしてますが、本来の大きさは一番右のものです。



さて、ここまでを整理して、style設定をいくつかのclassに分けて、divで花びら作るhtmlはこうなります。

コード:
<style type="text/css">
/* 花びら(parent) */
.petal {
width: 15px;
height: 10px;
}

/* 花びらの半分基礎 */
.petalBase {
border-radius: 6px 0px 0px 0px;
position: absolute;
top: 0px;
left: 3px;
width: 9px;
height: 6px;
}

/* 花びらの左半分になるところ */
.petalLeft {
background: -webkit-gradient(
linear, left top, right bottom,
from(rgba(224, 176, 192, 1)),
to(rgba(255, 240, 240, 1))
);
-webkit-transform: skew(-30deg) scale(1.2, 1) rotate(12deg);
}

/* 花びらの右半分になるところ */
.petalRight {
top: 4px;
background: -webkit-gradient(
linear, left bottom, right top,
from(rgba(224, 176, 192, 1)),
to(rgba(255, 240, 240, 1))
);
-webkit-transform: skew(30deg) scale(1.2, -1) rotate(12deg);
}
</style>


<div class="petal">
<div class="petalBase petalLeft"></div>
<div class="petalBase petalRight"></div>
</div>



■花びら回転
花びらを回転させましょう。
さきほど花びらを作ったとき、半花びら2つを内包する親divを作りましたので、そいつをCSSアニメーションで動かせばいいのです。
アニメーションの設定はこんな感じで、まずは3D-X回転のみです。

コード:
<style type="text/css">
/* 花びら回転 */
.petalRotate {
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 1s;
-webkit-animation-name: rotateX;
}

@-webkit-keyframes rotateX {
0% {-webkit-transform: rotate(0deg) rotateX(0deg);}
100% {-webkit-transform: rotate(0deg) rotateX(360deg);}
}
</style>


<div class="petal petalRotate">
<div class="petalBase petalLeft"></div>
<div class="petalBase petalRight"></div>
</div>


petalRotateクラスではアニメーションは終わりなしの無限Loop、動作の加速度はリニア、アニメーション1回にかかる時間は1秒、アニメーションの細かい設定名はrotateX、という指定です。
keyフレームは、アニメーションがn%進んだ位置でのアニメーションしたい属性とその値を書きます。間の値は補完されます。今回はwebkit-transformで2次元の回転(rotate)と3次元縦方向回転(rotateX)を指定して、rotateは値変えてないので実質アニメーションしませんが、3D縦方向は1sでくるくるします。

このアニメーション設定クラスpetalRotateを親divのclassに追加すればくるくるしだします。rotateは後で使います。
(すいません、実サンプル載せてなくて。)


■花びらの移動
回転できたら回転する花びらを移動させたいところです。ひらひら舞い落ちるんで。
しかし回転のアニメーションは1sです。一緒のclassに移動するアニメーション設定まで突っ込んだら、1sで移動開始点から終了点まで動くことになり、風情がない落ち方になります。

「ねぇ、知ってる?秒速5センチなんだって。桜の花の落ちるスピード。」

ということで、回転とは独立したアニメーションを設定したいわけですが、そのために「回転する花びら」をdivで囲んで、そいつにアニメーションを適用します。
花びらの構造がこんなふうになるわけです。
<div class="parentPetal petalTrans"> <!-- 移動アニメーション用-->
<div class="petal petalRotate"> <!-- 回転アニメーション用 -->
<div class="petalBase petalLeft"></div> <!-- 花びら半分A -->
<div class="petalBase petalRight"></div> <!-- 花びら半分B -->
</div>
</div>


CSS含むコードはこんな感じ:
<style type="text/css">
/* 花びら(parent) */
.petal {
width: 15px;
height: 10px;
}

/* 花びらの半分基礎 */
.petalBase {
border-radius: 6px 0px 0px 0px;
position: absolute;
top: 0px;
left: 3px;
width: 9px;
height: 6px;
}

/* 花びらの左半分になるところ */
.petalLeft {
background: -webkit-gradient(
linear, left top, right bottom,
from(rgba(224, 176, 192, 1)),
to(rgba(255, 240, 240, 1))
);
-webkit-transform: skew(-30deg) scale(1.2, 1) rotate(12deg);
}

/* 花びらの右半分になるところ */
.petalRight {
top: 4px;
background: -webkit-gradient(
linear, left bottom, right top,
from(rgba(224, 176, 192, 1)),
to(rgba(255, 240, 240, 1))
);
-webkit-transform: skew(30deg) scale(1.2, -1) rotate(12deg);
}

/* 花びら回転 */
.petalRotate {
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 1s;
-webkit-animation-name: rotateX;
}

@-webkit-keyframes rotateX {
0% {-webkit-transform: rotate(-58deg) rotateX(0deg);}
100% {-webkit-transform: rotate(-58deg) rotateX(360deg);}
}

/* 花びら移動 */
.animationArea {
position: relative;
overflow: hidden;
width: 640px;
height: 640px;
border: 1px solid red;
}

/* 移動アニメ適用div */
.parentPetal {
position: absolute;
}

/* 花びら落下 */
.petalTrans {
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 10s;
-webkit-animation-name: transPetal;
}

@-webkit-keyframes transPetal {
0% {top: -5%; left: 90%;}
5% {top: -5%; left: 90%;}
95% {top: 105%; left: 10%;}
100% {top: 105%; left: 10%;}
}
</style>


<div class="animationArea">
<div class="parentPetal petalTrans">
<div class="petal petalRotate">
<div class="petalBase petalLeft"></div>
<div class="petalBase petalRight"></div>
</div>
</div>
</div>


ひらひらアニメーションする「エリア」を設けるために、さらに親divをひとつ作ってます。この領域内を花びらが舞い、領域の外では見えなくなるようにするために、overflow:
hiddenです。実際のjsdo.itのコードではこの領域をJavaScriptで画面いっぱいになるようにブラウザ幅等取得して調整してます。

移動アニメーション適用のためのdivにもposition:
absolute指定付けときます。そしてアニメーションさせるstyleとして、positionのtop, leftを用います。

アニメーション時間は10秒間にしました。視界の外から入って外に出て行く、ために、top: -5% 位置から入って、top:
105%位置に抜けてるようにしてます。leftは90%からスタートして10%位置へ。右上から入って左下に抜ける道筋になります。

アニメーション進行時間の0-5%、95-100%が同じ位置です。これは、実際のアニメーションは5-95%の間で、前後は画面外で静止しているようにしてます。次から次へと同じルート上を落ちてくるとせわしないかな、ということで、ちょっとしたWaitが入ってるようなもんです。

ついでに、ちゃっかり回転の方のアニメーション設定の、rotate(rotateXではない)を -58 deg
に設定しています。これは花びらの落ちるコースの角度に合わせたもので、topとleftの移動距離を使ってatan(アークタンジェント)で計算して出せます。


■ひらひら
ここまでは花びら1枚の、作成方法とアニメーションの設定について書きました。
複数の花びらがひらひら舞うようにするには、JavaScript使って花びらの量産し、その1枚ずつにそれぞれ回転・道筋のアニメーション設定、をしています。
このあたりはJavaScriptの説明になりさらに長くなるので、詳細は割愛しますが、ざっと以下のような手順です。
  1. 花びら1枚ずつにid振って、たくさん作ります(jsdo.itのコードでは48枚)
    回転用divと移動用divにそれぞれid振ります。
  2. それをアニメーション領域のdivの子として追加します
  3. アニメーション設定(回転と道筋)をJavaScriptで48個分書き出します
    まず先に道筋を、topは-5%スタートの105%エンドで固定で、leftを、スタートは20-150%くらいの範囲、エンドを0-130%くらいの範囲で、乱数用いて決定します。
    これでアニメーション領域の幅(px数)と%で、縦横の移動距離わかるので、atan使って、花びらのコースに沿った角度を求めて、回転の方のアニメーション設定に活かします。
    (今回は面倒だったので、これらを<style>に生テキストとして突っ込んでます)
    この段階ではまだアニメーションしてません。
    まだkeyframeを用意しただけで、それぞれの花びらに対して、-webkit-animation-なんとか、のstyleを適用してません。
  4. すべての花びらのアニメーション時間設定を、これも多少のバリエーション持たせたstyleを用意します。
    回転は0.8s - 1.2s、移動は9 -13sの範囲からランダムに設定されます。これで他よりややゆったり落ちる花びら、他より多めにくるくる回ってる花びら、などが生まれます。道筋も、基本的に右上方面から左下方面、ってだけで角度はバリエーションありますので、1枚ずつは同じ道筋・回転の速さでループしていても、複数枚が舞うことで、すぐには飽きないようにというのを狙ってます。
  5. 最後にすべての花びらに生成したstyleを適用します。


最初の方にも書きましたが、やってることは別に難しいことでもなんでもなくて、ちまちまと多少面倒なことを積み重ねているだけです。
もし気になることなどあれば、jsdo.itの方には質問等かくとこあるので、そちらへ。

0 件のコメント: