1 分間で円を描くアニメーションを作成する場合、rAF を使用する必要はありません。これは、ほとんどの場合に個人的に rAF をお勧めしますが、余分な負荷が発生するだけだからです。
ただし、このような場合、モニターの同期がそれほど重要ではないsetInterval
(およびsetTimeout
) 負荷に関しては、おそらくより良い選択です。
これは、1 分ごとに円を描くように変更されたコードです。実際のタイムスタンプに基づいているため、タイミングはかなり正確です。ここでの間隔は 120 ミリ秒に設定されていますが、これは実際には円の円周に関連している必要があります。これは、オーバーラップするピクセルがそれほど見えないため、その時間枠内に描画されるピクセル数を決定するためです (ここではサブピクセル化を無視します)。必要に応じてタイムアウトを自由に調整してください。
ここで修正されたフィドル
セットアップは次のようになりました(window.onloadはフィドルでは必要ないので削除しましたが、最終ページのヘッダーにスクリプトをロードする場合はもちろん元に戻す必要があります)。変数名はもっと良いかもしれませんが、私は元の名前をいくらか残しました:
start_angle = 1.5 * Math.PI, /// common offset (north)
end_angle = 2 * Math.PI, /// ends full circle in radians
increase_end_angle = 0, /// current angle incl. offset
radius = 50,
startTime = (new Date()).getTime(), /// get current timestamp
diff; /// used for timestamp diff
また、静的設定をループの外に移動して、CPU サイクルを節約します (実際には、ストローク スタイルなどの設定は常に設定されていると効果があるため、これがより最適です)。他の場所で必要な out ループ中に多くの変数を変更していないため、 save
/を使用する必要はありません。restore
context.lineWidth = 6;
context.lineCap = "round";
主な機能は、実際の時間に基づいて円をリセットすることです:
setInterval(anim, 120); /// 120 for demo, use Ø and time to find optimal timeout
function anim() {
/// calc difference between initial and current timestamp
diff = (new Date()).getTime() - startTime;
diff = diff / 60000; /// 60000ms = 60s, now we have [0, 1] fractions
/// final angle
increase_end_angle = start_angle + end_angle * diff;
/// draw circle
context.beginPath();
context.arc(x, y, radius, start_angle, increase_end_angle);
context.stroke();
/// check diff fraction
if (diff >= 1) { /// if diff >= 1 we have passed 1 minute
/// update time and new radius
startTime = (new Date()).getTime();
radius += 10; /// add to current radius
};
}
理想的には、描画ごとに現在の円をクリアして、アンチエイリアシング ピクセルを保持し、より滑らかな外観にすることをお勧めします。これは、上に再描画すると、最終的にアルファ チャネルのためにこれが削除されるためです。
もちろん、これは、半径が大きくなったときに、現在のコンテンツを背面のキャンバスに描画して、既に描画された円を維持するなど、いくつかの追加の手順を実行する必要があることを意味します。
更新:キャンバスを「高解像度」にして、キャンバスを設定することにより、アークメソッドの粗さを減らすこともできます。
canvas.width = wantedWidth * 2;
canvas.height = wantedHeight * 2;
canvas.style.width = wantedWidth + 'px'
canvas.style.height = wantedHeight + 'px';
それに応じてすべての座標とサイズをスケーリングすることを忘れないでください(x2)。
高解像度のキャンバスで実行される更新されたフィドル
更新:追加の質問に対処するには:
1)すべてを2(つまりスケール)で乗算すると、すべてがよりシャープになります。
「高解像度モード」で何が起こるかというと、最初のサイズの 2 倍のキャンバスを使用していますが、追加のスタイリング (CSS) を適用することで、キャンバス全体を最初のサイズに縮小します。
ただし、現在、同じ領域に2倍のピクセルがあり、サブピクセル化により、これを利用してより良い「解像度」を得ることができます. しかし、キャンバスの数が 2 倍になると同時に、2 倍のサイズのキャンバスを使用する前と同じ位置に戻すために、すべてをスケーリングする必要があります。
400x400としましょうの画像のようなものです。これを 400x400 で表示すると、ピクセル比は 1:1 になります。代わりに 800x800 の画像を使用し、そのサイズを 400x400 に強制的に下げた場合、画像にはまだ 800x800 ピクセルがありますが、表示できないもの (モニターは半分のピクセルを表示できないため) が補間されてそこに表示されます。そこに半ピクセルです。
ただし、シェイプの場合は、最初にアンチエイリアス処理を行ってから周囲のものを補間することで、シェイプのより詳細なバージョンを実現できます (デモで確認できるように)。
2) setInterval(anim, 120) を理解できませんでした;//この部分..比率..これが円が 60 秒で完了する理由ですか? 繰り返しますが、setInterval.Reasonを使用するときは常に疑いがあります.Reasonは、しばらくするとジャークを提供します.この場合はそうではありません.RAFを使用するときに常に発生するアニメーションを停止したかったのですが、rafは最適化に最適です.So少し混乱がありましたが、setInterval の方法で行くと思います。
モニターの VBLANK に同期できないため、最初に問題が発生しsetInterval
ます。setTimeout
VBLANK は、内部のモニター画面をスキャンしたビームが新しいフレームを開始したときに、古いブラウン管テレビから発生します。正しく同期するには、VBLANK ギャップに同期します。これは、ビデオを含むすべての機器に当てはまります。コンピュータ。
ただし、これにはタイマーの float 分解能 (60Hz の場合は 16.7ms) が必要になるため、これらのタイマーではこれは不可能です。その間に、丸めエラーが発生し、いわばループ内でフレームがスキップされ、ぎくしゃくすることがあります。モニターのリフレッシュ レートに同期できるrAF ( requestAnimationFrame
) が付属していますが、この理由だけではありません。より低レベルで効率的であり、結果として消費電力も削減できます。
1 秒間に 60 回まで (最大で) 同期を監視する必要がない場合、不正確なタイマーを使用しても問題はありません。rAF を使用した場合、その間に何度も同じピクセルを描画して、変更を確認できない可能性があります (ただし、サブピクセル化が開始され、これらのピクセルの一部にわずかな視覚的変化が生じる可能性があります)。したがって、ここでは rAF は目的を果たしません。画面上で違いを生まない多くの不必要な描画を行うからです。
setInterval
SE ごとに悪くはありませんが、アニメーションの場合、常に更新する必要がある場合、通常は不正確です。これが行うことは、イベントを作成し、それをブラウザーのイベント キューに入れることです。可能な場合、イベントはタイムアウトになったときに実行されます (キューには、再描画、関数呼び出しなどのさまざまな種類のイベントが含まれています)。超正確ではありませんが、タイムアウトのタイミングに依存せず、アークを更新するときに実際の時間を使用するため、ここでは十分に正確です。したがって、この目的のために、微妙な不正確さを隠す非常に小さな増分で描画するので、これは機能します.
これは、他の要因がアップデートでジャークを引き起こす可能性があるという意味ではありませんが、それはrAFにも当てはまります. 何も完璧ではありません。
120msという時間に関しては、これは私が始めた数字です。滑らかさをあまり変えていない数値です。しかし、それは円を完成させるものではありません。
円を完成させるのは時間差"Now time" - "Start time"
です。この場合、ラウンドごとに 60 秒を使用するため、60,000 ミリ秒の差が生じます。したがって、有用な数値を取得するには、60000 で除算します。得られる数値は 0.0 から 1.0 の間であり、これに角度を直接掛けると、diff が 1 の場合に 100% の角度が得られます。
理想的なタイマーは、円の円周に関連して時間を使用することです。これにより、新しいピクセルごとにどれだけの時間がかかるかが決まるため、たとえば、これを 10 で再度分割して、サブピクセル化を検討できます。これは、更新の終点でオーバーラップがないため最適ですが、ループがトリガーされるたびに新しいピクセルが描画されます。
3)この質問は少し難しく、私は今のところそれに取り組んでいます.私がそれを行うことができない場合は、私はあなたのアドバイスを受けます.それはいくつかのjsonを扱い、複数のインスタンスを作成し、データが来なくなったらアニメーションを停止します. .私は明日それを試してみます.今は疲れすぎています.
このために、新しい質問を開くことをお勧めします。そして、メタ回答がメイン回答よりも大きくなったようです.. :-o :-)