Re: jQueryで要素を点滅させる

[jQueryで要素を点滅させる - Qiita / soeda_jp](http://qiita.com/soeda_jp/items/776ec1b97f65f477754c) にてjQueryの`fadeOut` / `fadeIn`をつかった要素の点滅方法が紹介されていました。
CSSアニメーションをつかったほうが負荷が小さいんじゃないだろうかと思い、色々調べてみました。
途中で飽きて、すごくふわふわした記事になっています。


#各実装での測定

以下の環境で検証しています。

* jQuery 1.11.3
* Google Chrome 43.0 (PC版・2015/06現在最新版)

## setInterval+jQueryをつかった点滅

```script1.js
$("div").on('click', function(){
  $self = $(this);
  setInterval(function(){
    $self.fadeOut(500).fadeIn(500);
  },1000)
});
```

測定しやすいよう、元記事のスクリプトを少し改変したスクリプトです。
要素がクリックされると、その要素を1秒間隔で消したり出したりするアニメをjQueryで実装してみました。

以下の図は、要素をクリックしてしばらく点滅させている間のメモリ消費を、Chromeのデベロッパーツールで測定したものです。

![Screenshot_from_2015-06-04 20:25:05.png](https://qiita-image-store.s3.amazonaws.com/0/41635/e0cded65-771e-ceea-9da0-d30b2f8f5c20.png)

なんかよくわからないですが、**User JS Heap**が増えたり減ったりしていますね。ヒープって言うからにはきっと、動的に確保しているメモリ容量とか何でしょう。知りませんが。
ときどきかっくんと落ちているのはGCが動いているのでしょう。一回目の山より、二回目の山が大きいのは、「大きく育ちそうなリストのGC頻度を減らす」とかなんかそんなjsの最適化があるのではないだろうか。
１分くらい放置していると、パソコンのファンが唸り始めたりしました。怖い。

## CSSをつかった点滅

```style.css
.pikapika{
  animation: pika2 1s infinite alternate;
}
@keyframes pika2{
  from{opacity: 1;} 
  to{opacity:0;}
}
```
```script2.js
$("div").on('click', function(){
  $(this).addClass('pikapika');
});
```

細かい調整が面倒くさいので、厳密におなじアニメーションではありませんが、点滅アニメーションをCSSによって実装してみました。
javascriptではクリックした際にクラスを付与することしかしていません。

jQueryオンリーの場合と同様に、要素をクリックしてから点滅している間をしばらく、Chromeのデベロッパーツールで測定しました。

![Screenshot_from_2015-06-04 20:53:40.png](https://qiita-image-store.s3.amazonaws.com/0/41635/da86cff1-a223-a5e7-5434-2fbc3ef19628.png)

**User JS Heap**はクリック時に一度だけ増加し、その後で要素が点滅している間もずっと一定です。パソコンのファンも静かで、お隣さんに怒られる心配をしなくて良い。
演出周りは極力CSSを利用したほうが良さそうです。ただし、`animation`プロパティはIE9以下では利用できません。レガシーなIEを使う場合は`<marquee>`などを使うといいのではないでしょうか。

# jQueryの`fadeOut` / `fadeIn`について

もう一度、javascriptの方を見てみます。

```script1.js
$("div").on('click', function(){
  $self = $(this);
  setInterval(function(){
    $self.fadeOut(500).fadeIn(500);
  },1000)
});
```

fadeIn / fadeOutがどのような実装になっているかは、以下が参考になります

1. [jquery/jquery 1.11-stable/src/effects.js#L592-L593- GitHub](https://github.com/jquery/jquery/blob/1.11-stable/src/effects.js#L595-L596) ... `fadeOut` / `fadeIn`の定義
2. [jquery/jquery 1.11-stable/src/effects.js#L470-L487- GitHub](https://github.com/jquery/jquery/blob/1.11-stable/src/effects.js#L470-L487) ... `fadeOut` / `fadeIn`が呼ぶ`jQuery#animation`メソッドの実装。GitHub上ではインデントが崩れているかもしれないので、気合いで読む。
3. [jquery/jquery 1.11-stable/src/effects.js#L429-L459- GitHub](https://github.com/jquery/jquery/blob/1.11-stable/src/effects.js#L429-L459) .. `jQuery#animation`から呼ばれる`jQuery.speed`の定義。アニメーション速度・Easing(描画モード)、アニメーション終了時のコールバック関数の定義などから、アニメーション全体の定義を作って、アニメーションキューにいれる

上記を踏まえると、`script1.js`では

1. アニメーションキューに「500msかけて消える」のイベントが格納
2. アニメーションキューに「500msかけて出る」のイベントが格納
3. 1のアニメが終わったら、キューが空か判定する。→2がまだあるので実行
4. 2のアニメがおわったら、キューが空か判定する。→もう無いので終了
5. 1〜2のキュー登録が1000ms毎に行われる→実は常にアニメーションキューが空にならない→ヒープが積み重なっていく？

試しに元記事のように`setInterval`を3000msにしたら、パソコンの唸りがなくなったり、Heapの青い線の増加のしかたが上記よりも顕著ではなくなりました。なるほど。

んでは、CSSのアニメーションはどんな実装になっているのか…というのを[firefoxのソースコード](https://developer.mozilla.org/ja/docs/Developer_Guide/Source_Code/Downloading_Source_Archives)でも参考に読み解いてみようと思ったのですが、挫折しました。たぶんGPUが処理しているから速い（小学生並みの理解）

10年くらい前だと、CSSを多用したページはかくかくした動きをしていた気がするのですが、いろいろ進化しているようです。

## 補遺
ちなみに`script1.js`ですが、おなじ要素を何度もクリックしたり、別の`<div>`要素をクリックした場合、点滅の仕方がおかしくなります。（元ネタの記事のスクリプトは、おそらく画面描画時に一度だけ呼ばれる事を前提にしているため問題ありません）
当記事のスクリプトは検証用なので気にしませんが、あえて実用に修正するなら以下のような二度押し防止策が必要でしょう。

* `setInterval()`の戻り値を変数に持ち、その変数がnullの時だけ`setInterval()`する
* `Unserscore#once`などをつかって、一度しか動かさない



# 参考

* [JavaScript アプリケーションのメモリー・リークを理解する - IBM Developer Work](http://www.ibm.com/developerworks/jp/web/library/wa-jsmemory/) ... 2012年の記事。ガーベッジ・コレクションという言い方がおしゃれ。
* [Web制作にGPU処理を取り入れる](https://w3g.jp/blog/web_gpu_adopt)