0

フィドル

私がやっていること:設定された間隔を使用して円弧から円を描いています。

私がやりたいこと:同じ機能を実現したいのですが、 requestAnimationFrameを使用してsetIntervalを避けて、1分(つまり、円を描くのに60秒)で円を完成させる必要があります。

RAF とは何かは知っているが、実装できない

60 秒...RAF...サークル。?? 私のコード:

    //for full code see the fiddle.

    setInterval(function () {
    context.save();
    //context.clearRect(0, 0, 500, 400);
    context.beginPath();

    increase_end_angle = increase_end_angle + 11 / 500;
    dynamic_end_angle = end_angle + increase_end_angle;
    context.arc(x, y, radius, start_angle, dynamic_end_angle, false);

    context.lineWidth = 6;
    context.lineCap = "round";
    context.stroke();
    context.restore();
    if (dynamic_end_angle > 3.5 * Math.PI) { //condition for if circle completion
        increase_end_angle = 0;
        draw(radius + 10); //draw from same origin.
    }
}, 3);

ケンのみの更新された質問:

1)すべてを2(つまりスケール)で乗算すると、すべてがよりシャープになります。

2) setInterval(anim, 120) を理解できませんでした;//この部分..比率..これが円が 60 秒で完了する理由ですか? 繰り返しになりますが、setInterval.Reasonを使用するときは常に疑問です。理由は、しばらくするとジャークが発生するためです.この場合はそうではありません.RAFを使用するときに常に発生するアニメーションを停止したかったのですが、rafは最適化に最適です.So少し混乱がありましたが、setInterval の方法で行くと思います。

3)この質問は少し難しく、私は今のところそれに取り組んでいます.私がそれを行うことができない場合は、私はあなたのアドバイスを受けます.それはいくつかのjsonを扱い、複数のインスタンスを作成し、データが来なくなったらアニメーションを停止します. .私は明日それを試してみます.今は疲れすぎています.

答えてくれてありがとうケン!!!まさに私が欲しかったもの。

4

2 に答える 2

2

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ます。setTimeoutVBLANK は、内部のモニター画面をスキャンしたビームが新しいフレームを開始したときに、古いブラウン管テレビから発生します。正しく同期するには、VBLANK ギャップに同期します。これは、ビデオを含むすべての機器に当てはまります。コンピュータ。

ただし、これにはタイマーの float 分解能 (60Hz の場合は 16.7ms) が必要になるため、これらのタイマーではこれは不可能です。その間に、丸めエラーが発生し、いわばループ内でフレームがスキップされ、ぎくしゃくすることがあります。モニターのリフレッシュ レートに同期できるrAF ( requestAnimationFrame) が付属していますが、この理由だけではありません。より低レベルで効率的であり、結果として消費電力も削減できます。

1 秒間に 60 回まで (最大で) 同期を監視する必要がない場合、不正確なタイマーを使用しても問題はありません。rAF を使用した場合、その間に何度も同じピクセルを描画して、変更を確認できない可能性があります (ただし、サブピクセル化が開始され、これらのピクセルの一部にわずかな視覚的変化が生じる可能性があります)。したがって、ここでは rAF は目的を果たしません。画面上で違いを生まない多くの不必要な描画を行うからです。

setIntervalSE ごとに悪くはありませんが、アニメーションの場合、常に更新する必要がある場合、通常は不正確です。これが行うことは、イベントを作成し、それをブラウザーのイベント キューに入れることです。可能な場合、イベントはタイムアウトになったときに実行されます (キューには、再描画、関数呼び出しなどのさまざまな種類のイベントが含まれています)。超正確ではありませんが、タイムアウトのタイミングに依存せず、アークを更新するときに実際の時間を使用するため、ここでは十分に正確です。したがって、この目的のために、微妙な不正確さを隠す非常に小さな増分で描画するので、これは機能します.

これは、他の要因がアップデートでジャークを引き起こす可能性があるという意味ではありませんが、それはrAFにも当てはまります. 何も完璧ではありません。

120msという時間に関しては、これは私が始めた数字です。滑らかさをあまり変えていない数値です。しかし、それは円を完成させるものではありません。

円を完成させるのは時間差"Now time" - "Start time"です。この場合、ラウンドごとに 60 秒を使用するため、60,000 ミリ秒の差が生じます。したがって、有用な数値を取得するには、60000 で除算します。得られる数値は 0.0 から 1.0 の間であり、これに角度を直接掛けると、diff が 1 の場合に 100% の角度が得られます。

理想的なタイマーは、円の円周に関連して時間を使用することです。これにより、新しいピクセルごとにどれだけの時間がかかるかが決まるため、たとえば、これを 10 で再度分割して、サブピクセル化を検討できます。これは、更新の終点でオーバーラップがないため最適ですが、ループがトリガーされるたびに新しいピクセルが描画されます。

3)この質問は少し難しく、私は今のところそれに取り組んでいます.私がそれを行うことができない場合は、私はあなたのアドバイスを受けます.それはいくつかのjsonを扱い、複数のインスタンスを作成し、データが来なくなったらアニメーションを停止します. .私は明日それを試してみます.今は疲れすぎています.

このために、新しい質問を開くことをお勧めします。そして、メタ回答がメイン回答よりも大きくなったようです.. :-o :-)

于 2013-08-04T16:44:40.820 に答える
2

setInterval関数を繰り返し呼び出しながら、requestAnimationFrame一度だけ呼び出されるようにスケジュールします。関数を繰り返し呼び出す場合は、関数の最後でrequestAnimationFrameを再度呼び出すことができます。

以下は、コードに基づく例です。requestAnimationFrameが 2 回呼び出されることに注意してください。

  1. 一番下で、これは初めてdrawFrameを呼び出します。
  2. drawFrame関数の最後。drawFrameが処理を完了すると、requestAnimationFrameを呼び出して、次のアニメーション フレームで自分自身を再度呼び出します。

JSFミドルリンク

// set up the geometry
var start_angle = -0.5 * Math.PI;
var end_angle = 1.5 * Math.PI;
var arc_end_angle = start_angle;
var angle_step = 2 * Math.PI / 60;
var radius = 30;

// set up the canvas
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.lineWidth = 6;
context.lineCap = "round";

// the drawFrame function is called up to 60 times per second
function drawFrame() {

    // draw an arc
    context.beginPath();
    context.arc(
        canvas.width / 2,
        canvas.height / 2,
        radius,
        start_angle,
        arc_end_angle,
        false);
    context.stroke();

    // update the geometry, for use in the next call to
    // drawFrame
    arc_end_angle += angle_step;
    if (arc_end_angle > end_angle) {
        arc_end_angle = start_angle;
        radius += 10;
    }

    // request that drawFrame be called again, for the
    // next animation frame
    requestAnimationFrame(drawFrame);
}

// request that drawFrame be called once, in the next
// animation frame
requestAnimationFrame(drawFrame);

requestAnimationFrameは、関数が 1 秒あたり正確に 60 回呼び出されることを保証しないことに注意してください。MDNのドキュメントから(強調鉱山):

画面上のアニメーションを更新する準備ができたら、いつでもこのメソッドを呼び出す必要があります。これにより、ブラウザーが次の再描画を実行する前に、アニメーション関数を呼び出すことが要求されます。この再描画は、フォアグラウンド タブでは 1 秒あたり最大60 回発生する可能性があります (正確な速度はブラウザーが決定します) が、バックグラウンド タブではより低い速度に減少する可能性があります。

正確に 1 秒以内に弧を描く必要がある場合は、 drawFrameの呼び出し回数ではなく、経過時間に基づいて弧の終了角度を計算してください。

于 2013-08-03T23:29:34.530 に答える