2016-04-18 11 views
1

2つの配列を取るコードを記述しました。どちらも4隅の形状(実際には開始フレームと終了フレーム)の座標、キャンバスID、時間値。次に、各コーナーのdXとdYを計算し、window.performance.now()を使用してタイムスタンプを作成します。次に、requestAnimationFrame()ごとに、dX、dY、古いタイムスタンプ、新しいタイムスタンプ、および関数呼び出しからの時間値を使用して、座標が何であるべきかを計算します。複数のアニメーションを一度に実行するときに問題が発生するHTML5キャンバス

function doAnim(cv, startFrame, endFrame, animTime) 
{ 
    this.canvas = document.getElementById(cv); 
    this.ctx = this.canvas.getContext('2d'); 

    if(startFrame.length != endFrame.length) 
    { 
     return('Error: Keyframe arrays do not match in length'); 
    }; 


    this.animChange = new Array(); 

    for(i=1;i<=startFrame.length;i++) 
    { 
     var a = startFrame[i]; 
     var b = endFrame[i] 
     var c = b - a; 
     this.animChange[i] = c; 
    } 
    this.timerStart = window.performance.now(); 

    function draw() 
    {  
     this.requestAnimationFrame(draw, cv); 
     this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height); 
     this.currentFrame = new Array(); 
     for(i=1;i<=startFrame.length;i++) 
     { 
      this.currentFrame[i] = startFrame[i]+(this.animChange[i]*((window.performance.now()-this.timerStart)/animTime)); 
     } 
     if((window.performance.now()-this.timerStart)>=animTime) 
     { 
      this.ctx.beginPath() 
      this.ctx.moveTo(endFrame[1], endFrame[2]); 
      this.ctx.lineTo(endFrame[3], endFrame[4]); 
      this.ctx.lineTo(endFrame[5], endFrame[6]); 
      this.ctx.lineTo(endFrame[7], endFrame[8]); 
      this.ctx.fill(); 
      return; 
     } 
     else 
     { 
      this.ctx.beginPath() 
      this.ctx.moveTo(this.currentFrame[1], this.currentFrame[2]); 
      this.ctx.lineTo(this.currentFrame[3], this.currentFrame[4]); 
      this.ctx.lineTo(this.currentFrame[5], this.currentFrame[6]); 
      this.ctx.lineTo(this.currentFrame[7], this.currentFrame[8]); 
      this.ctx.fill(); 
     } 
    } 
    draw(); 
} 

目的は、複数のオブジェクトのアニメーションを同時に発生させることです。地平線から来ているかのようにオブジェクトを表示し、偽の3Dパースペクティブエフェクトを作成する(すべてのオブジェクトの開始フレームはキャンバスの中心にある単一のポイントになる)ために、私は全体のコーディネートアプローチをとった。私はオブジェクトのテクスチャを歪ませたくありません。

1つのアニメーションにはうまくいきますが、最初のアニメーションが実行されている間に完全に異なるキャンバスで新しいアニメーションを開始しようとすると、最初のアニメーションはそのトラックで停止します。

thisの無償使用で私のJSから見ることができました(私は完全に理解していません。thisがどのように機能し、私が読んだすべての説明が私をさらに混乱させました)。それは働いていません。私はまた、すべての関数の独自の変数を1つのグローバル配列に格納する恐ろしいアプローチを試みました(関数が最初に実行され、すべての変数が1から30のエントリに入れられ、2回目は31から60などに格納されます)。 )。意外にも、それはどちらもうまくいかなかった。

Here is a JSFiddle so you can see this scenario for yourself and play with my code.私は公式にアイデアがありません。どんな助けでも大歓迎です。

+0

この要素はどこですか?document.getElementById(cv); –

+0

cvは、doAnim関数の最初の引数で定義されたキャンバスのIDです。私のテスト環境で実行する関数は、ID「canvas1」のキャンバスで1秒のアニメーションを行うためにdoAnim( 'canvas1'、anim_frame1、anim_frame2,1000)です。 – Clifforus

+0

実行方法を示したこの[Q&A](http://stackoverflow.com/questions/27521142/web-browser-paint-rate-with-multiple-animations-on-the-page/27534151#27534151)をご覧ください。 1つのrequestAnimationFrameループの下に複数のアニメーションがあります。 – markE

答えて

1

markEも同様にリンクされているため、requestAnimationFrameを複数回呼び出すと機能しません。 代わりに、複数のオブジェクトを作成し、各フレームで何らかの関数を呼び出すことができます。 私はあなたのコードを使用して例を作成しました: https://jsfiddle.net/samcarlin/2bxn1r79/7/

var anim0frame1 = new Array(); 
anim0frame1[1] = 0; 
anim0frame1[2] = 0; 
anim0frame1[3] = 50; 
anim0frame1[4] = 0; 
anim0frame1[5] = 50; 
anim0frame1[6] = 150; 
anim0frame1[7] = 0; 
anim0frame1[8] = 150; 

var anim0frame2 = new Array(); 
anim0frame2[1] = 200; 
anim0frame2[2] = 200; 
anim0frame2[3] = 300; 
anim0frame2[4] = 250; 
anim0frame2[5] = 300; 
anim0frame2[6] = 300; 
anim0frame2[7] = 200; 
anim0frame2[8] = 250; 


//Call global 
animations = []; 
requestAnimationFrame(GlobalStep); 

function GlobalStep(delta){ 
    //Functions called by request animation frame have the new time as an argument 
    //so delta should be approximately the same as window.performance.now() 
    //especially in realtime applications, which this is 

    //Check if we have any animation objects 
    if(animations.length > 0){ 
    //Iterate through and call draw on all animations 
    for(var i=0; i<animations.length; i++){ 
     if(animations[i].draw(delta)){ 
     //Basically we have it so if the draw function returns true we stop animating the object 
     //And remove it from the array, so have the draw function return true when animation is complete 
     animations[i].splice(i, 0); 
     //We removed an object from the array, so we decrement i 
     i--; 
     } 
    } 
    } 

    //And of course call requestAnimationFrame 
    requestAnimationFrame(GlobalStep); 
} 

function AnimationObject(cv, startFrame, endFrame, animTime){ 

    //Add this object to the objects arrays 
    animations.push(this); 

    //We need to store start and end frame 
    this.startFrame = startFrame; 
    this.endFrame = endFrame; 
    this.animTime = animTime; 

    //Your code 
    this.canvas = document.getElementById(cv); 
    this.ctx = this.canvas.getContext('2d'); 

    if (startFrame.length != endFrame.length) { 
    return ('Error: Keyframe arrays do not match in length'); 
    }; 

    this.animChange = new Array(); 

    for (i = 1; i <= startFrame.length; i++) { 
    var a = startFrame[i]; 
    var b = endFrame[i] 
    var c = b - a; 
    this.animChange[i] = c; 
    } 

    this.timerStart = window.performance.now(); 
} 

//This adds a function to an object, but in such a way that every object shares the same function 
//Imagine a kitchen, each object is a person, and this function is a spoon 
//by defining this function in this manner Object.prototype.function_name = function(arguments){} 
//We make it so one function definition is needed, essentially allowing all the people to share one spoon, 
//the 'this' variable still refers to whichever object we call this method, and we save memory etc. 
AnimationObject.prototype.draw = function(newTime){ 
    //I added this to start frame so we get what we stored earlier 
    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); 
    this.currentFrame = new Array(); 

    for (i = 1; i <= this.startFrame.length; i++) { 
     this.currentFrame[i] = this.startFrame[i] + (this.animChange[i] * ((newTime - this.timerStart)/this.animTime)); 
    } 
    if ((newTime - this.timerStart) >= this.animTime) { 
     this.ctx.beginPath() 
     this.ctx.moveTo(this.endFrame[1], this.endFrame[2]); 
     this.ctx.lineTo(this.endFrame[3], this.endFrame[4]); 
     this.ctx.lineTo(this.endFrame[5], this.endFrame[6]); 
     this.ctx.lineTo(this.endFrame[7], this.endFrame[8]); 
     this.ctx.fill(); 
     return; 
    } else { 
     this.ctx.beginPath() 
     this.ctx.moveTo(this.currentFrame[1], this.currentFrame[2]); 
     this.ctx.lineTo(this.currentFrame[3], this.currentFrame[4]); 
     this.ctx.lineTo(this.currentFrame[5], this.currentFrame[6]); 
     this.ctx.lineTo(this.currentFrame[7], this.currentFrame[8]); 
     this.ctx.fill(); 
    } 
} 

注:それはチェックするよう 毎回新しいオブジェクトが追加されたボタンを押すと、単純にフレームごとに前のものを上書きし、あなたがあなたのプログラムを実装する必要があります特定のアニメーションが既に開始されている場合、あなたはまた、完全なアニメーションを停止する組み込みのメカニズムを使用することができます(コード内のコメントを読んで)

また、ボタンクリックコード

<button onclick="new AnimationObject('canvas1', anim0frame1, anim0frame2, 3000);"> 
に変更する必要があります

最後にご質問がある場合は、私に連絡してください。

+0

うわー、どのような簡潔かつ有用な返信、ありがとう!私はあなたのコメントが特に好きです。私はまだあなたのコードをうまく使っていますが、これらのアニメーションで達成しようとしているのは、本質的に特定の方法で開始および停止し、ユーザーが中断することのできない大きなアニメーションですそれで、すべての個々のアニメーションが適切に組み合わされると、オンザフライで新しいアニメーションを追加する必要がないので、あなたのソリューションを実装するのはかなり簡単です。そのことを念頭に置いて、コードをスリムにするためにできることは何もないと思いますか? – Clifforus

関連する問題