1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

この記事について

背景

みなさんはフェードイン/フェードアウトアニメーションを実装する時はどのように実装しますか?

  • CSSのtransitionプロパティやanimationプロパティでopacityプロパティを変化させる
  • jQueryを使用していればfadeIn()fadeOut()メソッドを使用する

など、色々方法が出てくるかと思います。ではどの方法が最適でしょうか?

筆者は普段レガシー環境でエンハンス業務に従事していますが、なんの気なしに既存コードを流用して、後者のjQueryのメソッドを使ってアニメーションを実装しようとしていました。そこで先輩エンジニアに「jQueryのアニメーションを使うとCPUで動くからパフォーマンスがちょっと悪い、できればGPUで動くCSSアニメーションを使いたい」の一言をいただき、Webアニメーションの奥深さを知りました。

伝えたいこと

この記事では、先輩エンジニアの一言から筆者が調べたことをまとめています。

ではどの方法が最適でしょうか?

この問いに答えられるような、Webアニメーションを実装する際に一つの観点を与えるものになっていればと思います。

CPU処理とGPU処理

ここで冒頭の先輩コメントに登場した、CPU処理とGPU処理について簡単に特徴をまとめます。

CPU

  • CPU(Central Processing Unit)とは、コンピューターの制御・計算(演算)を行う中央演算処理装置。コア数により扱える処理の数が増えるが、GPUよりは少ない。大量の処理を行うのには向かない。一つの複雑な処理を行うのに向いている
  • JavaScriptやjQueryアニメーションはCPU上で動作する

GPU

  • GPU(Graphics Processing Unit)とは、画像処理装置のことでCPUに比べてコア数が多く、並列作業が得意。定型的で、膨大な処理を行うのに向いている
  • CSSアニメーションの特定のプロパティは、GPU上で動作する。CPUで動くアニメーションよりも滑らかに動作する

GPUで動くCSS3プロパティについて
transitionプロパティやanimationプロパティで設定した全てのプロパティがGPU上で動作するわけではなく、transformopacityなどの一部プロパティが対象になります。
詳しくは後述のCSSプロパティのコストを参照。

CSSプロパティのコスト

再レンダリングのフローやレイヤの概念を知ることで、なぜGPU処理の方がパフォーマンスがよいのか理解できます。

要素の再レンダリングフロー

アニメーション時において、要素が再レンダリングされる際は、以下のフローが発生します。

  1. style(スタイル再計算)
  2. layout(要素の配置・リフロー)
  3. paint(描画・リペイント)
  4. composite(レイヤ合成)

1と4は必ず行われますが、GPU上で動くプロパティは2と3の工程を省くことができ、低コストとなるためスムーズなアニメーションを実現できます。

GPUのレイヤ概念
GPUではレイヤ概念というものがあり、レイヤを作成、作成したレイヤごと移動させる(アニメーションさせる)ことで、2と3の工程を省いています。

まとめると、

  • CPUレンダリングの場合:要素の位置が変更される度にpaint処理が走る
  • GPUレンダリングの場合:レイヤ概念を用いて、レイヤを移動(2(layout),3(paint)処理を省ける)させて4(composite)が行われる

といった違いがあり、GPUレンダリングの方が低コストな理由がわかります。

CSSプロパティにより異なるコスト

プロパティの特性により、再レンダリングするコストがそれぞれ異なってきます。

  • 全ての過程を辿るため、コストが高いプロパティ
    • left, max-width, border-width, margin-left, font-sizeなど
  • 2(layout)の過程は省かれるプロパティ
    • colorなど
  • 1(style)と4(composite)のみのためコストは低いプロパティ
    • transform, opacityなど

GPU上で動作しないCSSプロパティも動作するプロパティと一緒に使えばレイヤにのせられる場合があります。
例えば、animationプロパティでkeyframsを作成する際は、アニメーション対象のプロパティを個別で設定するよりも、合わせて設定した方がパフォーマンスが向上する場合があります。

レイヤをあらかじめ作成するwill-changeプロパティ

前述のGPUレンダリングの場合レイヤが生成されるタイミングはアニメーション開始の直前になりますが、事前にレイヤを生成しておく方法もあります。あらかじめブラウザに変化することを伝えているため実際に変化があった時にスムーズに動作できます。

MDNにも警告が載っていますが、事前にレイヤを作成する方法は、GPUで動作するCSSアニメーションを使ってもカクつく場合など、やむを得ない場合にのみ使用すべきです。乱用すると、事前に大量のレイヤを生成・グラフィック情報保持していることになり、メモリーを消費するためかえってパフォーマンスが悪くなる可能性があります。

事前にレイヤを作成しておくため、ハック的にtransformZ(0)プロパティを使う方法もあるようですが、will-changプロパティと同様の理由で、必要ない限りは使用しない方がよいでしょう。

デベロッパーツールでの確認(Chrome)

再レンダリングはどの処理が走っているのか、レイヤ生成されてるのか、についてはデベロッパーツールから確認することができます。

再レンダリングの確認

デベロッパーツール > パフォーマンス > 記録ボタン実行 > イベントログを確認することで、レンダリング処理を確認できます。
イベントログ.png

また、デベロッパーツール > その他のツール > レンダリング > ペイント点滅にチェックするとリペイントされている要素がわかります。以下はレイヤが作成されないleftプロパティを用いてサンタ画像を左から右へ移動させています。移動の度にリペイントの処理が入っているのがわかります。
ペイント処理.png

.santaImg { // サンタ画像の要素
  position: absolute;
  width: 200px;
  height: auto;
  animation: animation-santa;
  animation-duration: 2s;
  animation-iteration-count: infinite;
}

@keyframes animation-santa {
  0% {
    left: 0;
  }
  100% {
    left: 300px;
  }
}

レイヤ確認

デベロッパーツール > その他のツール > レイヤを選択するとレイヤパネルが開きます。以下の例ではtransform: translateX()でサンタ画像を移動させています。レイヤパネルではページ全体の枠の中にサンタ画像の部分に枠がつき、レイヤになっていることがわかります。
レイヤパネル.png

.santaImg { // サンタ画像の要素
  width: 200px;
  height: auto;
  animation: animation-santa;
  animation-duration: 2s;
  animation-iteration-count: infinite;
}

@keyframes animation-santa {
  0% {
    transform: translateX(0);
  }
  100% {
    transform: translateX(300px);
  }
}

このように、レンダリング状況を確認することができます。位置移動のアニメーションをleftプロパティにした場合は、リペイント処理が走り、レイヤ枠は表示されません。transformプロパティの場合にはリペイント処理は走らずに、レイヤ枠が表示されます。
また、leftプロパティの場合でもopacityプロパティと一緒に使用した場合やwill-changeプロパティを使用した場合は、リペイント処理は走らず、レイヤ枠が表示されます。

Web Animations APIの利用

ここまで「アニメーションは極力GPUで動くCSSアニメーションで実装する方がよい」というような内容を書いてきましたが、時にはJavaScript側で動的な値を取得し、アニメーションをつけたい場合が出てくるかと思います。
こういった時に Web Animations APIを使用すれば、JavaScript操作と親和性高く、かつ通常のCSSアニメーションと同じように動作するので、GPU側でアニメーションを動作させることが可能です。

Web Animations APIは外部ライブラリではないため、読み込み負荷かがない点でもパフォーマンス的に優れています。

まとめ

パフォーマンスに留意したアニメーションの実装

今回調べたことをもとに、筆者は以下の観点を持つようにしています。

  • アニメーション処理はハードウェアアクセラレーション(グラフィック関連の処理をGPUに任せること)を目指す
    • 可能な限り、GPU上で動作するCSSプロパティを使用して実装する
    • GPU上で動作しないCSSプロパティも動作するプロパティと一緒に使えばレイヤに乗せてくれるので、animationプロパティのkeyframesなどはあまり細かく区切らずに書く
  • JavaScriptから動的な操作が必要な場合はWeb Animations APIなど標準のAPIを使用する

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?