2017-05-01 15 views
1

波のアニメーションを作成する必要があります。私は波の速度を制御する必要がありますのの可用性に依存します。波をスピードアップすることは可能ですか?私は波のためにキャンバスを使用しています。事前にwaveのrequestAnimationFrameを制御する方法

おかげで

フィドル:https://jsfiddle.net/Chaitanya_Kumar/6ztr0Lfh/

function animate() { 

      if (x > data.length - 1) { 
       return; 
      } 

      if (continueAnimation) { 
       requestAnimationFrame(animate); 
      } 

      if (x++ < panAtX) { 

       var temp = data[x]; 
       var final = constant-(temp); 
       ctx.fillRect(x, final, 1, 1); 
       ctx.lineTo(x, final); 
       ctx.stroke(); 
      } else { 

       ctx.clearRect(0, 0, canvas.width, canvas.height); 
            ctx.beginPath(); // reset the path 


       for (var xx = 0; xx < panAtX; xx++) { 
        var y = data[x - panAtX + xx]; 
        var final = constant - (y); 
        ctx.fillRect(xx, final, 1, 1); 
        ctx.lineTo(xx, final);       
       } 
       ctx.stroke(); 
      } 
     } 
+0

requestAnimationFrameコールバックは、最初の引数としてタイムスタンプを受け取ります。これを使用して、現在のコールと最後のコールの差を計算することができます。次に、2つのアニメーションフレームの間に渡された時間に基づいて計算を実行します。 –

+0

それは、フィドルの波をスピードアップすることは可能ですか? –

+0

はい、アニメーションフレーム内で現在何をやり直しているかは、2つのフレームの間の時間に比例します。 –

答えて

2

サブサンプリングデータ

以下はデータサンプリングの一例です。線形補間を使用してデータソースをサブサンプリングし、そのデータをローリンググラフ表示に表示します。

定期的な間隔データ。

質問とフィドルからのデータは、サンプルレートの間隔が一定で、そのデータの表示速度を変更したいことを示唆しています。これは私が下のデモで行ったことです。

デモ

についてグラフは、データのリアルタイム表示で、左から右にその速度を使用すると、サンプル関数を呼び出すする速度に依存しています。

displayBuffer.readFrom(dataSource, dataSpeed, samplesPerFrame) 

displayBuffer dataSourceは、データのソースであるとreadseek機能を持っており、あなたが位置をシークreadPosが先に0.01データサンプルを移動し、データを読み取るdataSource.seek(0.01);表示データを保持するオブジェクトであるdataSource.read();線形補間された値が返されます。

これにより、ソースデータからのデータストリーミングのスピードアップまたはスローダウンが可能になります。

データリーダーオブジェクト

//------------------------------------------------------------------------------ 
// data reader reads from a data source 
const dataReader = { 
    readPos : 0, 
    seek(amount){ // moves read pos forward or back 
     if(this.data.length === 0){ 
      this.readPos = 0; 
      return 0; 
     } 
     this.readPos += amount; 
     this.readPos = this.readPos < 0 ? 0 :this.readPos >= this.data.length ? this.data.length - 1 : this.readPos; 
     return this.readPos; 
    },  
    // this function reads the data at read pos. It is a linear interpolation of the 
    // data and does nor repressent what the actual data may be at fractional read positions 
    read(){ 
     var fraction = this.readPos % 1; 
     var whole = Math.floor(this.readPos); 
     var v1 = this.data[Math.min(this.data.length-1,whole)]; 
     var v2 = this.data[Math.min(this.data.length-1,whole + 1)]; 
     return (v2 - v1) * fraction + v1; 
    }, 
} 

タイムスタンプ付きデータ・ソース。

デモは、dataReaderに追加することで変更できます。

データサンプルレートが不規則な場合は、各サンプルのタイムスタンプを追加する必要があります。次に、シークアライラであるtimeSeek関数を追加して検索しますが、時間サンプル間の傾きを使用して、指定された時間の読み取り位置を計算します。現在のサンプリングされた時間から次のシーク方向(サンプリング方向)まで各サンプルをサンプリングして、不確定性を求めるために必要なCPUサイクルを必要とする。

以下は、timeShift引数で前方にシフトされた時間のためにreadPos(上記のdataReaderオブジェクトから)を検出する例であるseekTimeです。オブジェクトのreadTimereadPosのプロパティが更新され、次のread()コールではdataSource.readTimeのデータが返されます。

readTime : 0, // current seeked time 
    seekTime(timeShift){ // Example is forward seek only 
     if(this.timeStamps.length === 0){ 
      this.readPos = 0; 
      return 0; 
     } 
     this.readTime += timeShift; // set new seeked time 
     var readPos = Math.floor(this.readPos); 
     // move read pos forward until at correct sample 
     while(this.timeStamps[readPos] > this.readTime && 
       readPos++ < this.timeStamps.length); 

     // Warning you could be past end of buffer 
     // you will need to check and set seek time to the last 
     // timestamp value and exit. Code below the following line 
     // will crash if you dont vet here. 
     //if(readPos === this.timeStamps.length) 


     // now readPos points to the first timeStamp less than the needed 
     // time position. The next read position should be a time ahead of the 
     // needed time 
     var t1 = this.timeStamps[readPos]; // time befor seekTime 
     var t2 = this.timeStamps[readPos+1]; // time after seekTime 
     // warning divide by zero if data bad 
     var fraction = (this.readTime-t1)/(t2-t1)); // get the sub sample fractional location for required time. 
     this.readPos = readPos + fraction; 
     return this.readPos; 
    },  

警告私は、すべての安全性チェックを省略しています。バッファの終わり、悪いタイムシフト値をチェックする必要があります。タイムスタンプデータに不良サンプルがある場合、ゼロで除算すると、dataReaderはそのポイントからNaNだけを返し、読み込みにはスローします。安全のために獣医師。

上記のタイムスタンプ機能を使用するには、各データサンプルに対応するtimeStampがあることを確認する必要があります。各サンプルのタイムスタンプが1対1で一致しない場合、上記のコードは機能しません。

dataDisplayへの変更は簡単です。関数のシーク呼び出しを変更するだけで dataDisplay.readFrom(dataSource,speed,samples)dataSource.seekTime(speed/samples)speedはサンプルではなく時間を表します。 (または、seekTime()の場合は関数を上書きします)、これにより、dataDisplayオブジェクトは、timeStampedデータと通常の間隔データの両方をそのまま扱うことができます。

デモ

実施例試料ランダムデータと可変速度及びサンプリングレートで表示します。左に表示速度を設定します。フレームレートは60fpsですが、速度変数をフレーム間の時間に合わせて調整することができます。

var ctx = canvas.getContext("2d"); 
 
window.focus(); 
 
//============================================================================== 
 
// the current data read speed 
 
var dataSpeed = 1; 
 
var samplesPerFrame = 1; 
 
requestAnimationFrame(mainLoop); // start animation when code has been parsed and executed 
 

 
//------------------------------------------------------------------------------ 
 
// data reader reads from a data source 
 
const dataReader = { 
 
    readPos : 0, 
 
    seek(amount){ // moves read pos forward or back 
 
     if(this.data.length === 0){ 
 
      this.readPos = 0; 
 
      return 0; 
 
     } 
 
     this.readPos += amount; 
 
     this.readPos = this.readPos < 0 ? 0 :this.readPos >= this.data.length ? this.data.length - 1 : this.readPos; 
 
     return this.readPos; 
 
    },  
 
    // this function reads the data at read pos. It is a linear interpolation of the 
 
    // data and does nor repressent what the actual data may be at fractional read positions 
 
    read(){ 
 
     var fraction = this.readPos % 1; 
 
     var whole = Math.floor(this.readPos); 
 
     var v1 = this.data[Math.min(this.data.length-1,whole)]; 
 
     var v2 = this.data[Math.min(this.data.length-1,whole + 1)]; 
 
     return (v2 - v1) * fraction + v1; 
 
    }, 
 
} 
 

 
//------------------------------------------------------------------------------ 
 
// Create a data source and add a dataReader to it 
 
const dataSource = Object.assign({ 
 
     data : [], 
 
    },dataReader 
 
); 
 
// fill the data source with random data 
 
for(let i = 0; i < 100000; i++){ 
 
    // because random data looks the same if sampled every 1000 or 1 unit I have added 
 
    // two waves to the data that will show up when sampling at high rates 
 
    var wave = Math.sin(i/10000) * 0.5; 
 
    wave += Math.sin(i/1000) * 0.5; 
 
    // high frequency data shift 
 
    var smallWave = Math.sin(i/100) * (canvas.height/5); 
 
    // get a gaussian distributed random value 
 
    dataSource.data[i] = Math.floor(smallWave + ((wave + Math.random()+Math.random()+Math.random()+Math.random()+Math.random())/5) * canvas.height); 
 
} 
 

 
//------------------------------------------------------------------------------ 
 
// Data displayer used to display a data source 
 
const dataDisplay = { 
 
    writePos : 0, 
 
    width : 0, 
 
    color : "black", 
 
    lineWidth : 1, 
 
    // this function sets the display width which limits the data buffer 
 
    // when it is called all buffers are reset 
 
    setDisplayWidth(width){ 
 
     this.data.length = 0; 
 
     this.width = width; 
 
     this.writePos = 0; 
 
     if(this.lastRead === undefined){ 
 
      this.lastRead = {}; 
 
     } 
 
     this.lastRead.mean = 0; 
 
     this.lastRead.max = 0; 
 
     this.lastRead.min = 0; 
 
    }, 
 
    // this draws the buffered data scrolling from left to right 
 
    draw(){ 
 
     var data = this.data; // to save my self from writing this a zillion times 
 
     const ch = canvas.height/2; 
 
     if(data.length > 0){ // only if there is something to draw 
 
      ctx.beginPath(); 
 
      ctx.lineWidth = this.lineWidth; 
 
      ctx.strokeStyle = this.color; 
 
      ctx.lineJoin = "round"; 
 
      if(data.length < this.width){ // when buffer is first filling draw from start 
 
       ctx.moveTo(0, data[0]) 
 
       for(var i = 1; i < data.length; i++){ 
 
        ctx.lineTo(i, data[i]) 
 
       } 
 
      }else{ // buffer is full and write position is chasing the tail end 
 
       ctx.moveTo(0, data[this.writePos]) 
 
       for(var i = 1; i < data.length; i++){ 
 
        ctx.lineTo(i, data[(this.writePos + i) % data.length]); 
 
       } 
 
      } 
 
      ctx.stroke(); 
 
     } 
 
    }, 
 
    // this reads data from a data source (that has dataReader functionality) 
 
    // Speed is in data units, 
 
    // samples is number of samples per buffer write. 
 
    //   samples is only usefull if speed > 1 and lets you see the 
 
    //   mean, min, and max of the data over the speed unit 
 
    //   If speed < 1 and sample > 1 the data is just a linear interpolation 
 
    //   so the lastRead statistics are meaningless (sort of) 
 
    readFrom(dataSource,speed,samples){ // samples must be a whole positive number 
 
     samples = Math.floor(samples); 
 
     var value = 0; 
 
     var dataRead; 
 
     var min; 
 
     var max; 
 
     for(var i = 0; i < samples; i ++){ // read samples 
 
      dataSource.seek(speed/samples); // seek to next sample 
 
      dataRead = dataSource.read();  // read the sample 
 
      if(i === 0){ 
 
       min = dataRead; 
 
       max = dataRead; 
 
      }else{ 
 
       min = Math.min(dataRead,min); 
 
       max = Math.min(dataRead,max); 
 
      } 
 
      value += dataRead; 
 
     } 
 
     // write the samples data and statistics. 
 
     this.lastRead.min = min; 
 
     this.lastRead.max = max; 
 
     this.lastRead.delta = value/samples - this.lastRead.mean; 
 
     this.lastRead.mean = value/samples; 
 
     this.data[this.writePos] = value/samples; 
 
     this.writePos += 1; 
 
     this.writePos %= this.width; 
 
    } 
 
} 
 
// display data buffer 
 
var displayBuffer = Object.assign({ // this data is displayed at 1 pixel per frame 
 
     data : [],     // but data is written into it at a variable speed 
 
    }, 
 
    dataDisplay // add display functionality 
 
); 
 

 
//------------------------------------------------------------------------------ 
 
// for control 
 
const keys = { 
 
    ArrowLeft : false, 
 
    ArrowRight : false, 
 
    ArrowUp : false, 
 
    ArrowDown : false, 
 
} 
 
function keyEvent(event){ 
 
    if(keys[event.code] !== undefined){ 
 
     event.preventDefault(); 
 
     keys[event.code] = true; 
 
    } 
 
} 
 
addEventListener("keydown",keyEvent); 
 

 
//------------------------------------------------------------------------------ 
 
function mainLoop(time){ 
 
    ctx.clearRect(0,0,canvas.width,canvas.height); 
 
    if(canvas.width !== displayBuffer.width){ 
 
     displayBuffer.setDisplayWidth(canvas.width); 
 
    } 
 
    displayBuffer.readFrom(dataSource,dataSpeed,samplesPerFrame); 
 
    displayBuffer.draw(); 
 
    
 
    
 
    //----------------------------------------------------------------------------- 
 
    // rest is display UI and stuff like that 
 
    ctx.font = "16px verdana"; 
 
    ctx.fillStyle = "black"; 
 
    //var dataValue =displayBuffer.lastRead.mean.toFixed(2); 
 
    //var delta = displayBuffer.lastRead.delta.toFixed(4); 
 
    var readPos = dataSource.readPos.toFixed(4); 
 
    //if(displayBuffer.lastRead.delta > 0){ delta = "+" + delta } 
 
    // ctx.fillText("Data : " + dataValue + " (" +delta +")" ,4,18); 
 
    ctx.setTransform(0.9,0,0,0.89,4,18); 
 
    ctx.fillText("Speed : " + dataSpeed.toFixed(3) + ", Sample rate :" +samplesPerFrame + ", Read @ "+readPos ,0,0); 
 
    ctx.setTransform(0.7,0,0,0.7,4,32); 
 
    if(samplesPerFrame === 1){ 
 
     ctx.fillText("Keyboard speed -left, +right Sample rate +up",0,0); 
 
    }else{ 
 
     ctx.fillText("Keyboard speed -left, +right Sample rate -down, +up",0,0); 
 
    } 
 
    ctx.setTransform(1,0,0,1,0,0); 
 
    if(keys.ArrowLeft){ 
 
     keys.ArrowLeft = false; 
 
     if(dataSpeed > 1){ 
 
      dataSpeed -= 1; 
 
     }else{ 
 
      dataSpeed *= 1/1.2; 
 
     } 
 
    } 
 
    if(keys.ArrowRight){ 
 
     keys.ArrowRight = false; 
 
     if(dataSpeed >= 1){ 
 
      dataSpeed += 1; 
 
     }else{ 
 
      dataSpeed *= 1.2; 
 
      if(dataSpeed > 1){ dataSpeed = 1 } 
 
     } 
 
    } 
 
    if(keys.ArrowUp){ 
 
     keys.ArrowUp = false; 
 
     samplesPerFrame += 1; 
 
    } 
 
    if(keys.ArrowDown){ 
 
     keys.ArrowDown = false; 
 
     samplesPerFrame -= 1; 
 
     samplesPerFrame = samplesPerFrame < 1 ? 1 : samplesPerFrame; 
 
    } 
 

 
    requestAnimationFrame(mainLoop); 
 
}
canvas { 
 
    border : 2px black solid; 
 
}
<canvas id=canvas width=512 height=200></canvas>

の読み取りとデータを表示するこの方法は、迅速かつ簡単です。グリッドマーキングとデータ処理をデータソースと表示データに追加するのは簡単です。デモ(定期的な間隔のデータ)は、大きなデータソースの表示やデータの拡大/縮小を簡単に処理できます。 timeStampedデータの場合、上記のseekTime関数は大きなデータセットには適していないことに注意してください。より効果的なシーク時間のためには、そのようなデータのインデックスを作成する必要があります。

+0

私はそれを試しました....しかしdatasource.data配列の値を削除すると、私は矢印キーを押して.....データソースではなく、独自の値をプロットすると問題があります。データ.....助けをありがとう –

関連する問題