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

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

ただいまの
回答率

90.50%

  • JavaScript

    16440questions

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

  • 最適化

    14questions

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

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

解決済

回答 3

投稿

  • 評価
  • クリップ 3
  • VIEW 1,586

aaaaaaaa

score 469

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での環境での疑問です。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+6

描画のタイミング

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

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

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

コードの比較

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/13 20:15 編集

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

    キャンセル

  • 2017/11/14 19:18

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

    そしてrequestAnimationFrame()は、その自発的な?更新の前に図形を描画してくれるけれども、settimeout()だとそれがないので図形を描画しようとしている最中に頁が更新されてしまい
    再度、図形を描画して、最初の描画が無駄になってしまう、ということでしょうか。

    キャンセル

  • 2017/11/14 19:37

    例えばchromeというブラウザは仕様で、1秒間にMAX60回まで画面を書き換えます。

    > プログラムで動的に、或いはF5や更新ボタンを押す、javascriptの非同期通信で行える更新とは別のものですか?
    DOMツリー更新やCSSが変更された後のレンダリングし直しの話です。
    これが例えばChromeなら秒間最大60回、負荷が高かったり、バックグラウンド実行になると秒間1回程度まで落ち込む事があります。
    もちろんアニメーションやDOMツリーが変更されない場合は画面は更新されません。

    アニメーションは餅つきです。
    餅を捏ねる人と、杵で餅を叩く人が交互に操作しなければ意味がありません。
    settimeout()の場合、16msで強制的に次のDOM書き換えを行いますが、反映されるのは次の描画更新タイミングです。
    つまり、2回連続で餅をこねたり、2回連続で杵が振り下ろされ、
    結果としてまぁまぁ滑らかだけど、たまにカクつくなぁという評価のアニメーションになります。

    また移動するアニメーションの位置も16ms毎に更新されたら、次はこの位置にあるはずだという決め打ちで作られるので、
    描画タイミングとはチグハグで、まっすぐ進んでいる物体の速度が急に速くなったり、遅くなったり感じるはずです。

    requestAnimationFrame()の場合、更新タイミングの直後にはい実行していいよとなるので、
    この餅を2回捏ねたり、2回杵を叩きつけるというロスを極力抑える事が可能です。
    また前回の描画タイミングから経過時間で逆算して決定する為に極めてロスが少なく移動する物体の速度が変わったように感じる事もありません。

    このように2重の意味でrequestAnimationFrameは有利なので、
    結果として全然アニメーションの品質が違う!凄い!という感動が得られます。

    キャンセル

  • 2017/11/15 19:19 編集

    ご返答ありがとうございます。
    >>餅を捏ねる人と、杵で餅を叩く人が交互に操作しなければ意味がありません。
    >>settimeout()の場合、16msで強制的に次のDOM書き換えを行いますが、反映されるのは次の描画更>>新タイミングです。
    >>つまり、2回連続で餅をこねたり、2回連続で杵が振り下ろされ、
    >>結果としてまぁまぁ滑らかだけど、たまにカクつくなぁという評価のアニメーションになります。

    つまり、処理を行ってDOMを書き換えてそれがweb頁に反映されるのに必要なものがブラウザが行う何らかの変化があったときのみに発動する更新なわけですね。
    しかし、settimeoutは、指定した秒数後に処理を行うので、「処理→処理→更新(処理を反映)」のようになってしまいアニメーションとしては及第点になってしまう。
    でもrequestAnimationFrameだと更新されるすんでのところで処理を行うのでほぼほぼ「処理→更新→処理→更新」のような形になる、ということであっておりますか。
    間違っておったら申し訳ありません。

    キャンセル

  • 2017/11/15 19: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以下にして動かしたとしてもあまり変化はなく、結局カクカクします。

    キャンセル

  • 2017/11/20 19: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で描画するように設定しても何らかの理由で描画のタイミングがつかめなくなってしまうということでしょうか。

    キャンセル

  • 2017/11/20 20: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後に反映

    重い時は重い時なりに、軽い時は軽い時なりに、次の描画タイミングが(遅い方に)安定します。
    従って、ユーザーの目には等速で移動し続けるように見えます。

    キャンセル

  • 2017/11/21 19:32

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

    キャンセル

+4

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

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

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

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

コードにバグがあります

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


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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

checkベストアンサー

+3

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

最後の2行がおかしいです
約16ms毎に2回drawメソッドを叩き始めるので、1秒後には2^60回のdrawメソッドを呼びまくる計算になります。

そりゃ重くて落ちるわ。

setTimeout(draw,interval);の行を削除してもう一度確かめてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • JavaScript

    16440questions

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

  • 最適化

    14questions

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