質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

最適化

最適化とはメソッドやデザインの最適な処理方法を選択することです。パフォーマンスの向上を目指す為に行われます。プログラミングにおける最適化は、アルゴリズムのスピードアップや、要求されるリソースを減らすことなどを指します。

Q&A

解決済

3回答

2192閲覧

描画のタイミングを最適化する、とは

aaaaaaaa

総合スコア501

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

最適化

最適化とはメソッドやデザインの最適な処理方法を選択することです。パフォーマンスの向上を目指す為に行われます。プログラミングにおける最適化は、アルゴリズムのスピードアップや、要求されるリソースを減らすことなどを指します。

0グッド

3クリップ

投稿2017/11/13 10:54

setTimeoutメソッドを利用すると、「指定した間隔」と「ブラウザが画面を更新するタイミング」に差が生じることになります。そうなると、ブラウザが一度画面を更新する間に、何度か描画処理を行ってしまう事態が起き、

パフォーマンスの低下につながる恐れがあります。それを解決するのが、requestAnimationFrameメソッドです。
<中略>
長所…毎回、ちゃんと描画できるタイミングで実行されるようになるため、「DOMやCSSを書き換えたが、描画できないタイミングだったので実際には動いていない」という無駄な処理が発生しない。
setintervalメソッドと同様に、ブラウザ上でタブが非アクティブな状態になると、処理が軽減される。
短所…処理が実行さえっるタイミングでブラウザの画面更新のタイミングに依存するため、狙ったときに実行できない。また、実行されるタイミングも必ずしも一定とは限らないため、決まった間隔で処理させたい場合には、setTimeoutメソッドやsetintervalを使用するといい。

ブレイクスルーjavascript70頁~71頁より

ここで質問がありなす。

①setTimeoutメソッドを利用すると、「指定した間隔」と「ブラウザが画面を更新するタイミング」に差が生じるとはどういうことなのでしょうか。更新すると何度が描画処理を行ってしまうとありますが、いまいち理解できません。

var canvas = document.getElementById("canvas"); //canvas要素から描画コンテキストの取得 var ctx = canvas.getContext("2d"); //16.6ミリ秒(0.01666...秒) var interval = Math.floor(1000/60); var x=5; var y=5; function draw() { ctx.clearRect(0,0,500,500); x+=5; y+=5; //パスを初期化。ここでいうパスとは、領域の境界線のようなものだという。 ctx.beginPath(); //塗りつぶす色を指定。 ctx.fillstyle = "#99ff66";//緑色 //長方形のパスを作成。第一引数がx座標(横)、第二引数がy座標(縦)、第三引数が横幅、第四引数が高さだ。座標というのは、ブラウザの表示領域のX軸とY軸のことだ。0,0にすれば、ブラウザの左上に表示される。 ctx.rect(x,y,100,200);//rectは、rectangel(長方形、矩形)の略である。 //円形のパスを作成。 //ctx.arc(100,100,40,0,Math.PI*2);//第一引数と第二引数が座標(横縦)、第三引数が半径、第四が円の始まりの角度、第五が円の終わりの角度、第六が描く向き //fillで長方形を塗りつぶす。何も指定しないと黒色となる。 ctx.fill();//fillは、容器や場所などをいっぱいにする、満たすという意味だ。 //最後にbeginPathで開始したパスを閉じる。 ctx.closePath(); setTimeout(draw,interval); } draw();

上記がrequestAnimationFrameメソッド無しで、下記がrequestAnimationFrameメソッドありのソースです。同書籍の71頁に

<中略>ブラウザを開いて、動いていることを確認しましょう。先ほどと同じ動作となっていれば大丈夫です。

とありましたが、requestAnimationFrameありだと図形の描画の速さが無しに比べて早く、タスクマネージャーを開いてみてみると無しに比べ負担が増えたように見えます。これは、先ほどと同じ動作とは違うという認識であっておりますか。またどうすると先ほどと同じ動作になるのでしょうか。

var canvas = document.getElementById("canvas"); //canvas要素から描画コンテキストの取得 var ctx = canvas.getContext("2d"); //16.6ミリ秒(0.01666...秒) var interval = Math.floor(1000/60); //よくわからないが、settimeoutで指定した間隔とブラウザが画面を更新する時機に差が生じ、それによって不都合なことがおきるのを防ぐのがrequestAnimationFrameという。 //しかし"ブラウザが画面を更新する時機"というのがどういうことなのかさっぱり理解できない。 window.requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(cb) {setTimeout(cb,17);}; var x=5; var y=5; function draw() { ctx.clearRect(0,0,500,500); x+=5; y+=5; //パスを初期化。ここでいうパスとは、領域の境界線のようなものだという。 ctx.beginPath(); //塗りつぶす色を指定。 ctx.fillstyle = "#99ff66";//緑色 //長方形のパスを作成。第一引数がx座標(横)、第二引数がy座標(縦)、第三引数が横幅、第四引数が高さだ。座標というのは、ブラウザの表示領域のX軸とY軸のことだ。0,0にすれば、ブラウザの左上に表示される。 ctx.rect(x,y,100,200);//rectは、rectangel(長方形、矩形)の略である。 //円形のパスを作成。 //ctx.arc(100,100,40,0,Math.PI*2);//第一引数と第二引数が座標(横縦)、第三引数が半径、第四が円の始まりの角度、第五が円の終わりの角度、第六が描く向き //fillで長方形を塗りつぶす。何も指定しないと黒色となる。 ctx.fill();//fillは、容器や場所などをいっぱいにする、満たすという意味だ。 //最後にbeginPathで開始したパスを閉じる。 ctx.closePath(); requestAnimationFrame(draw); setTimeout(draw,interval); } draw();

ブラウザがchrome61.0.3163.100でosがwindows10での環境での疑問です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

描画のタイミング

テレビで流れる「動画」でも、実際には1秒に60コマの映像を送っている、というように、ブラウザでも表示は連続しているわけではなく、1秒間に何回か書き換えています。requestAnimationFrameは、その書換の直前のタイミングで実行されます。

時には更新回数が1秒に数回まで落ちることもあるので、固定時間でsetTimeoutしていると、1描画の間に何回も書き換えることなって、表示されず無駄です。

Google ChromeのDeveloper Toolsにある「Performance」を測定してみると、JavaScriptの実行や画面描画のタイミングが見えて、わかりやすいと思います。

コードの比較

これは、requestAnimationFrame版のコードがおかしいです。

  • 連続して描くのに、requestAnimationFrameの所要時間を計っていない
  • requestAnimationFramesetTimeoutを両方呼び出しているために、実行回数がねずみ算式に増えていく

投稿2017/11/13 11:09

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyabi-sun

2017/11/13 11:16 編集

あー、確かに… requestAnimationFrameだと開始時刻と現在時刻、終了時刻のハンドリングが必要ですね。 まぁ、それに見合った素晴らしい動作をするものが確認出来ますけどね。
aaaaaaaa

2017/11/14 10:18

ご返答ありがとうございます。 >>ブラウザでも表示は連続しているわけではなく、1秒間に何回か書き換えています。 間違っておったら申し訳ありませんが、表示は連続して…というのは、あるウェブの頁を表示後、何らかの更新が起きるまでの状態のことであっておりますか。 また、1秒間に何回も書き換えているというのは、プログラムで動的に、或いはF5や更新ボタンを押す、javascriptの非同期通信で行える更新とは別のものですか? また>>更新回数が1秒に数回まで落ちることも…とあるので、CPUやメモリなどの状況によってそのコウシンの頻度が変わるのですか? ブラウザが自発的にそれも1秒間に何回も頁全体を更新するというのは初耳だったのでおどろきました。 そしてrequestAnimationFrame()は、その自発的な?更新の前に図形を描画してくれるけれども、settimeout()だとそれがないので図形を描画しようとしている最中に頁が更新されてしまい 再度、図形を描画して、最初の描画が無駄になってしまう、ということでしょうか。
miyabi-sun

2017/11/14 10:37

例えばchromeというブラウザは仕様で、1秒間にMAX60回まで画面を書き換えます。 > プログラムで動的に、或いはF5や更新ボタンを押す、javascriptの非同期通信で行える更新とは別のものですか? DOMツリー更新やCSSが変更された後のレンダリングし直しの話です。 これが例えばChromeなら秒間最大60回、負荷が高かったり、バックグラウンド実行になると秒間1回程度まで落ち込む事があります。 もちろんアニメーションやDOMツリーが変更されない場合は画面は更新されません。 アニメーションは餅つきです。 餅を捏ねる人と、杵で餅を叩く人が交互に操作しなければ意味がありません。 settimeout()の場合、16msで強制的に次のDOM書き換えを行いますが、反映されるのは次の描画更新タイミングです。 つまり、2回連続で餅をこねたり、2回連続で杵が振り下ろされ、 結果としてまぁまぁ滑らかだけど、たまにカクつくなぁという評価のアニメーションになります。 また移動するアニメーションの位置も16ms毎に更新されたら、次はこの位置にあるはずだという決め打ちで作られるので、 描画タイミングとはチグハグで、まっすぐ進んでいる物体の速度が急に速くなったり、遅くなったり感じるはずです。 requestAnimationFrame()の場合、更新タイミングの直後にはい実行していいよとなるので、 この餅を2回捏ねたり、2回杵を叩きつけるというロスを極力抑える事が可能です。 また前回の描画タイミングから経過時間で逆算して決定する為に極めてロスが少なく移動する物体の速度が変わったように感じる事もありません。 このように2重の意味でrequestAnimationFrameは有利なので、 結果として全然アニメーションの品質が違う!凄い!という感動が得られます。
aaaaaaaa

2017/11/15 10:20 編集

ご返答ありがとうございます。 >>餅を捏ねる人と、杵で餅を叩く人が交互に操作しなければ意味がありません。 >>settimeout()の場合、16msで強制的に次のDOM書き換えを行いますが、反映されるのは次の描画更>>新タイミングです。 >>つまり、2回連続で餅をこねたり、2回連続で杵が振り下ろされ、 >>結果としてまぁまぁ滑らかだけど、たまにカクつくなぁという評価のアニメーションになります。 つまり、処理を行ってDOMを書き換えてそれがweb頁に反映されるのに必要なものがブラウザが行う何らかの変化があったときのみに発動する更新なわけですね。 しかし、settimeoutは、指定した秒数後に処理を行うので、「処理→処理→更新(処理を反映)」のようになってしまいアニメーションとしては及第点になってしまう。 でもrequestAnimationFrameだと更新されるすんでのところで処理を行うのでほぼほぼ「処理→更新→処理→更新」のような形になる、ということであっておりますか。 間違っておったら申し訳ありません。
miyabi-sun

2017/11/15 10:55

それが一点ですね。 後者も大事です、というかこっちの方がrequestAnimationFrameを使うべき理由かもしれません。 settimeoutの場合、約16ms毎に更新されるはずだ!という決め打ちで画面更新を行いますので、 Chromeの次の画面更新までほぼ0〜16msの間で揺れます。 例えば1秒間で300px動く物体を1/60秒の更新タイミングで描画すると、約16ms毎に物体は5px動きます。 しかし、次の描画タイミングが制御できないので、物体が1pxしか動かなかったり9pxも一気に飛んだりします。 対するrequestAnimationFrameは描画と同時に次の処理が動くので、 Chromeの次の画面更新までの時間がsetTimeoutと比べて綺麗に揃います。 なので毎回の描画で動く距離は5pxに近い数値を維持出来るわけですね。 でもでも、settimeoutでも4msみたいな十分に小さいタイミングで連発しまくれば、効率はともかく綺麗に動きそうじゃん? ここでJavaScriptのイベントループの優先度的な問題に入ってきます。 https://qiita.com/s-shin/items/7a04e7c0fe877509d55d requestAnimationFrameはsettimeoutより優先度が高く実行されます。 settimeoutは低い優先度で実行されるので、タイムアウトしてから0〜4ms程度の揺れが発生します。 settimeoutで実際に試しましたが10ms以下にして動かしたとしてもあまり変化はなく、結局カクカクします。
aaaaaaaa

2017/11/20 10:25

ご返答ありがとうございます。 >>Chromeの次の画面更新までほぼ0〜16msの間で揺れます。 ブラウザが行う何らかの変化があったときのみに発動する更新は、必ずしも1/60秒で更新されるわけではないけれども、 settimeoutで「1/60秒」と指定するというのは、先述の更新が必ず1/60秒毎に発生する、という仮定のもとにある、ということですよね。 ところで、"揺れる"というのは、どういうことなのでしょうか。0~1/60秒の間のどこかで描画を行う、ということでしょうか。 >>しかし、次の描画タイミングが制御できないので、物体が1pxしか動かなかったり9pxも一気に飛んだりします。 つまり、ブラウザが1/60秒のタイミングで更新するとして、5pxずつ動くけれども、5pxの次、つまり10px以後の描画タイミングが制御できない、ということでしょうか。 重ね重ね申し訳ありませんが、一つ質問させてください。更新のタイミングが1/60秒で、描画もほぼほぼ同じですが、同じ1/60秒で描画されるはずの処理のタイミングが制御できない、ということですよね。 settimeoutで1/60で描画するように設定しても何らかの理由で描画のタイミングがつかめなくなってしまうということでしょうか。
miyabi-sun

2017/11/20 11:23

ChromeはPC全体の負荷を考慮に入れつつ、勝手に画面更新タイミングを1/60〜1/1秒の間で調整します。 そして、setTimeoutやrequestAnimationFrameによって実行された関数は、 この描画タイミング云々を考慮に入れて作られた関数ではありません。 最初の関数実行時の時間を元に、「1秒掛けて300px動作か…現在の時刻は0.5秒経過しているから、150px先まで瞬間移動させればいいな」と判断しています。 従って、このような流れになります 500ms: 150px先へ移動よろしく → 15ms後に反映 516ms: 155px先へ移動よろしく → 16ms後に反映 532ms: 160px先へ移動よろしく → 0ms後に反映 ← この一瞬だけ10px移動したように見えたぞ? 548ms: 165px先へ移動よろしく → 1ms後に反映 このように、ユーザーの目には16ms毎に5pxずつ移動しているように見えてほしいのにフラフラ動いているように見えるわけです。 ユーザーは1/60秒のごく短い時間を絶対的な位置ではなく前後の流れで判断します。 等間隔で動く物体は速度が安定していると判断しますし、 急に移動速度が上がったり、下がったりする様を見て、「このアニメーションしている物体は揺れている」と錯覚することでしょう。 ではなぜrequesrAnimationFrameが安定しているかというと、描画完了直後に必ず関数が走るからです。 500ms: 150px先へ移動よろしく → 14ms後に反映 516ms: 155px先へ移動よろしく → 13ms後に反映 532ms: 160px先へ移動よろしく → 15ms後に反映 548ms: 165px先へ移動よろしく → 14ms後に反映 重い時は重い時なりに、軽い時は軽い時なりに、次の描画タイミングが(遅い方に)安定します。 従って、ユーザーの目には等速で移動し続けるように見えます。
aaaaaaaa

2017/11/21 10:32

ご返答ありがとうございました。ここまで付き合っていただいて感謝しております。
guest

0

①setTimeoutメソッドを利用すると、「指定した間隔」と「ブラウザが画面を更新するタイミング」に差が生じるとはどういうことなのでしょうか。

setTimeoutメソッドによる処理のスケジューリングは「ブラウザが画面を更新するタイミング」とは無関係に行われます. つまり, 画面を更新する直前かもしれませんし, 直後かもしれません.
一方requestAnimationFrameメソッドによる処理のスケジューリングは「ブラウザが画面を更新する直前」であることが保証されます.

canvas要素でアニメーションを実現する場合, スクリーンが書き換わる前に一度だけグラフィックを書き換えれば良いので, requestAnimationFrameメソッドのほうが適していることになります.

NOTE:
とは言え, JavaScriptでは何れのメソッドを用いたとしても確実に指定した時刻に処理を行わせることは出来ません. あくまで「処理の実行を予約」するだけです. 直前に重い処理が挟まると容易に実行時刻はズレます. requestAnimationFrameメソッドでは次の次のフレーム更新時かもしれません.

コードにバグがあります

JavaScript

1requestAnimationFrame(draw); 2setTimeout(draw,interval);

上記コードにより処理のスケジューリングが倍々ゲームで増えています. 後者をコメントアウトして下さい.

投稿2017/11/13 11:20

defghi1977

総合スコア4756

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

JavaScript

requestAnimationFrame(draw); setTimeout(draw,interval);
最後の2行がおかしいです 約16ms毎に2回drawメソッドを叩き始めるので、1秒後には2^60回のdrawメソッドを呼びまくる計算になります。 そりゃ重くて落ちるわ。 `setTimeout(draw,interval);`の行を削除してもう一度確かめてください。

投稿2017/11/13 11:02

miyabi-sun

総合スコア21158

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問