Mobile Factory Tech Blog

技術好きな方へ!モバイルファクトリーのエンジニアたちが楽しい技術話をお届けします!

生 Canvas を触って分かったこと

こんにちは、モバイルファクトリー Advent Calendar 2019 17日目担当の yunagi_n です。

さて、2019年も暮れになった今、 PixiJSKonva など、便利な Canvas フレームワークがありますが、
このたび初めて生の Canvas (Context2D) を触ることになりまして、
動作確認中などで気がついた点などがいくつかあったので紹介します。
基本的にはドキュメントをしっかり読めば良いのですが、普段は気がつかない部分もありましたので、記事にしました。
これから生の Canvas を触るぞ!という方の参考になれば嬉しいです。

requestAnimationFrame の実行頻度は端末依存

Canvas でアニメーションをさせたい場合、 Window.requestAnimationFrame を使ってループを実装します。
ここで指定するコールバック関数は一般的には秒間60回呼ばれるのですが、ここでドキュメントを引用すると

このコールバックの回数は、たいてい毎秒 60 回ですが、一般的に多くのブラウザーでは W3C の勧告に従って、ディスプレイのリフレッシュレートに合わせて行われます。

とあるように、実際には端末のリフレッシュレートに依存しています。
そのため、1秒間に60回程度呼ばれる前提で処理を記述すると、一部のハイエンド端末では、
倍速再生されてしまう、という事態が発生します。

実際に社内の一部の端末では倍速に再生されてしまうという事態が発生していました。
最近では、ハイエンド端末ではリフレッシュレートが 120Hz ということも度々ありますので、 気をつける必要があります。

高解像度ディスプレイへの対応

Canvas で描画したものは、一般的な画像と同じように描画されます。
つまりは、こちらも画像と同様に、 Retina / HiDPI などのような高解像度ディスプレイへの対応が必要になります。
対応方法としては、 CSS などでの指定サイズに対して2倍のサイズで予め Canvas を生成するか、
Window.devicePixelRatio を使用して、動的に描画サイズを変更する方法があります。
なお、後者の方法では一部の Android 端末で落ちるバグがあるようなので、注意が必要です。

Canvas の解放

Canvas で使用した画像リソースや OffScreen Canvas などは、描画していた DOM を消し飛ばしただけでは行われません。
例えば、 Vue の v-if などで何度も表示・非表示を繰り返した場合、
使用してたメモリリソースにも寄りますが数回程度繰り返しただけでメモリリークが発生してしまいます。

メモリ解放は下のようにすることで出来ます。

// Context2D を作成するとき
const context = canvas.getContext("2d", { storage: "discardable" });

// Canvas を破棄するとき
delete context;

canvas.height = 0;
canvas.width = 0;
canvas.remove();
delete canvas;

まとめ

  • requestAnimationFrame は (最近の端末では特に) 秒間60回呼ばれるわけでは無いので気をつけよう
  • 高解像度ディスプレイへの対応は画像同様に行おう
  • 使用したリソースはしっかり解放しよう

でした。
特に最後のメモリ解放については、普段使っている分にはなかなか気がつかないので、お気を付けください。