【jQuery】snowfallプラグインをカスタマイズする

Java Scriptでよく使われているプラグイン、snowfall。
一発で、雪が降っているエフェクトをページ全体にかけられるし、
雪以外の画像を指定することもできて、とても便利なのですが、
そのままではできないことがあり、いくつかカスタマイズをしたので、
それを紹介します。

[snowfallの説明とダウンロードはこちら
](http://www.north-geek.com/entry/jquery-snow)

#プラグインそのままではできないこと
- **ある一定個数の雪を撒いて、そのまま画面の下に消えたらおしまい、という処理ができない。（雪が降り出す→画面全体に雪が降っている状態→消す時は一気に消すしかない）**
- **雪の代わりに、紙吹雪にしたときに、ひとつひとつの紙をくるくる（X軸、Y軸、Z軸）回転させることができない。**

上記のような用途の場合は、そもそもプラグインを使わずに、０からコードを書く、という道もあるのですが・・・あえて、今回は、snowfallプラグイン自体のコードを改変して、上記の解決を目指してみました。

#その１、snowfall単発イベント化

snowfallプラグインには、もともと、雪を一気に消す命令として ``$(document).snowfall('clear');``があります。
でも、これを使うと、画面全体に出ている雪が一気に全部消えます。
今回目指したのは、雪が降り始めて、画面の下に消えていって終わる、という処理なので、``$(document).snowfall('clear');``は使いません。

そもそもsnowfallプラグインのフローがどうなっているかというと・・・

**１）``flakeCount ``で指定した数の雪を生成する
↓
２）ひとつの雪が、Y方向で、画面の下まで行くと、X座標とY座標をリセットする
↓
３）また画面の上のほうに表示される**

という処理を繰り返してしています。これにより、指定した一定の量の雪が、常に画面上にあることを実現しています。

snowfallプラグインの中で２）の処理をしているのが、171行目のここ。

```snowfall.jquery.js
if(this.y > (elHeight) - (this.size  + 6)){
this.reset();
}
```
なので、この``this.reset();``を書き換えます。

```snowfall.jquery.js
if(this.y > (elHeight) - (this.size  + 6)){
this.element.style.display ="none";
}
```
``reset()``ではなく``this.element.style.display ="none";``として、画面のいちばん下まで行った雪を見えない状態にしています。

ただ、気をつけなければならないのは、見えないだけで、存在はしている、ということ。このまま、ほうっておくと、メモリを食い過ぎる要因にもなります。

なので、この改変したプラグインを使う場合は、すべての雪が画面の外に出たタイミングで、
``$(document).snowfall('clear');``を行って、（見えてはいない）雪を消すことを忘れずにするようにしましょう。（もっといいやり方がある気もしますが、一応、これで一つ目は解決できました）

#その2、snowfall回転化

snowfallプラグインのコードの中で、位置を決めているのは、168行目からのこの部分です。

```snowfall.jquery.js
this.update = function(){
     this.y += this.speed;

     if(this.y > (elHeight) - (this.size  + 6)){
         this.reset();
     }

     this.element.style.top = this.y + 'px';
     this.element.style.left = this.x + 'px';
```

ここに、位置だけではなく、回転もいれてしまおうと思いました。
ちゃんとやると、回転度と回転軸の傾きを指定する変数を設定するべきだと思うのですが、なんとなく、回転していればよい、と思い、下記の二行を足しました。

```snowfall.jquery.js
   this.element.style.transform = 'rotateY('+(this.x+this.y)%360 + 'deg)';
   this.element.style.transform = 'rotateX('+(this.x+this.y)%360 + 'deg)';
```
x座標とy座標をもとに、えいやでX軸とY軸の回転角度を決めています。
回転角度を決めたあとは、``.transform``で、CSSのスタイルシートを書き換えて、回転させています。
かなり雑なやりかたですが（笑）、動きはなぜかかなり自然です。

で、これでうまくいった、と思いきや、PCでは表示されているのに、iPhoneだと、おかしな画面表示に！
いろいろ調べてみたら、

[iOS8 Safariで-webkit-transform: rotateX/rorateYが効かない/要素が消える](https://qiita.com/ttake/items/47c710142cbf57ff2adc)

と、ありました。

**親要素にCSS perspectiveを追加すればいい**

ということなのですが、雪の要素には、親要素がないので、親要素を作ってあげないといけません。
下記のjavascriptコードを、プラグインを呼び出す側のプログラムの中にいれることで解決しました。

```javascript

    $(".snowfall-flakes").wrapAll('<div>');
    $("div:has(.snowfall-flakes)").addClass("wrapper");
```
雪の要素には、``"snowfall-flakes"``というクラスがついているので、それを束ねる``<div>``をつくり、そのあと、その``<div>``に``"wrapper"``というクラスを指定しています。

と同時に、CSSで下記を設定します。

```css
.wrapper{
-webkit-perspective:500;
perspective:500;
}
```
これでiPhoneでもちゃんと動くようになりました。
でも、これ、３Dをリアルタイムで計算しているので、**かなり重い**です。
``flakeCount ``の数は、多くても120個ぐらいが限界だと思います。

（参考）ちなみに、snowfallプラグインで、複数の画像を設定する方法があるので、それはこちらを読んでください。

[jQueryのSnowfall Pluginで、複数の画像を利用できるようにしてみました。](https://wispblog.tree-web.net/data/1/page_1_1951.html)

#まとめ

こうやって書いてみると、プラグインのコードを変更したり、追記したりしたのは、２箇所だけ。
でも、やりたかったことが実現できました。

ちょうどいいプラグインがないときに、０からつくる手もありますが、ライセンスに気をつけながら、プラグインを改変する、というのも手ではないでしょうか？

（[知らないと損をする６つのライセンスまとめ](http://mycode.snow69it.net/998/)）
※ちなみに、snowfallプラグインは、使用や頒布、修正、派生版の頒布、ライセンスの継承に関して制限がないApache License 2.0です。本当にありがたいです。