10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

phina.jsAdvent Calendar 2017

Day 12

[phina.js] clipをする時、絶対にやってはいけないこと

Last updated at Posted at 2017-12-12

clip関数内では、必ずbeginPath()を実行しないといけない1

絶対に

clip関数内で!!

beginPath() を!!!

実行してください!

以上です。

......(続きます)

絶対にやってはいけないコード

例えば、こんなコードがあったとします。


// シーンinit内
var star = StarShape({
  x: 300,
  y: 300,
}).addChildTo(this);
star.clip = function(canvas) {
  canvas.arc(0, 0, 25, 0, 2 * Math.PI);
};

実行してから、1分後ぐらいにはただ星を一つ円でclipしているだけなのに、ものすごく重くなります。
意味がわかりませんね。

絶対にやってほしいbeginPath()を使用したコード


// シーンinit内
var star = StarShape({
  x: 300,
  y: 300,
}).addChildTo(this);
star.clip = function(canvas) {
  canvas.beginPath().arc(0, 0, 25, 0, 2 * Math.PI);
};


なぜbeginPath()を使わないと重くなるのか

beginPath() メソッド - Canvasリファレンス - HTML5.JP

context . beginPath()
現在のパスをリセットします。

beginPath()をするまで、パスがずっと残り続けているので、1分後には、1800個の円をclipしていることになります。2
それは重くて当然ですね...。
でも、phina.jsの中でいい感じに処理してるんじゃないかと思いますよね?
という勘違いがそもそもこれを引き起こした原因です。

save(), restore() しても、パスの状態は変わらない

phina.jsではclipの前後で、save()してrestore()することで、前後のオブジェクトがclipの影響を受けないようにしています。
しかし、clipされるされないとパスが消えるかどうかは別問題です。

save(), restore() メソッド - Canvasリファレンス - HTML5.JP

現在のパスと現在のビットマップは描画状態に含まれません。現在のパスは、beginPath() メソッドを使ってリセットするまで存在し続けます。現在のビットマップは canvas のプロパティであり、コンテキストではありません。

これに気づくまでにずっとsave(), restore()でパスの状態も変わってるだろうと勘違いしていました。
引用の通り、 beginPath() を使用するまでは、どんなことをしてもパスは残り続けます。

気づいた経緯

以下、ただの昔話です。

昔々、何かを破壊する演出等でclipを使用していた事があり、なぜかだんだん重くなっていくという謎のバグに悩まされていました。
最初は、clipが原因というところまでは、調べることができましたが、どうしてclipで重くなるのか全くわからず、clipを使わない方法で演出の処理を行うことにしました。

それから、しばらく経って、phina.jsアドベントカレンダー7日目を書くにあたって、clipを使用することがありました。
やはり、昔の経験があるので、clipを使うと重くなるだろうなと思いながらコードを書いていました。
案の定、重くなったのでちゃんと原因を探ることにしました...。

しかし、前回同様調べても、中々原因が判明しないので、手当たり次第、clipの仕方を変えてみることにしました。

まずは、パスを閉じないと前のパスが残り続けるのではないかと思い closePath() を実行。
当然変わりません。

次にパスが残り続けているかどうか調べるために、1フレーム目だけパスを設定をして、その後はパスをいじらないようにしました。(まだbeginPath()していません)
やっぱり、clipされ続けるのでパスが残ってるという認識は間違ってないことがわかりました。

その後、fill()とかstroke()とか色々試しましたが、改善しません。
canvas clip 重い とかでググるも関係ありそうなサイトを見つけることができず。

しょうがないので、clipのサンプルを漁っていると必ずbeginPath()が呼ばれていることに気づきました。
その時、心の中では、save()restore()してるから関係ないだろう...。と思いつつも、自分のコードでもbeginPath()を使ってみることにしました。

すると、信じられないことに今まで重くなっていたのが、嘘のように軽くなりましたとさ...。

めでたし、めでたし。

あとがき

ちゃんと、基礎は押さえておこう。
思い込みで試さないより、関係ないと思っても手当たり次第、試してみよう。

  1. 多少語弊がありますが、意図してやる分には全然構いません

  2. 30fps * 60秒 = 1800

10
7
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
10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?