2014-05-24 170 views
7

FIDDLE < < < <這比在問題更是最新的代碼。D3.JS時間序列折線圖與實時數據,平移和縮放

我試圖在d3中創建一個實時(實時更新)的時間序列圖表,它也可以平移(X)和縮放。理想情況下,我想要的功能是,如果用戶可以看到該行的最右側部分,那麼當新數據添加到圖形時,它會自動橫向移動以包含新數據(不更改軸比例)。

我d3.json()請求應該返回看起來像這樣的JSON數組:

[{"timestamp":1399325270,"value":-0.0029460209892230222598710528},{"timestamp":1399325271,"value":-0.0029460209892230222598710528},{"timestamp":1399325279,"value":-0.0029460209892230222598710528},....] 

當第一次加載頁面,我提出請求,並得到所有可用的日期到現在爲止,並繪製圖表 - 容易。以下代碼執行此操作,它還允許平移(在X中)和縮放。

var globalData; 
var lastUpdateTime = "0"; 
var dataIntervals = 1; 

var margin = { top: 20, right: 20, bottom: 30, left: 50 }, 
    width = document.getElementById("chartArea").offsetWidth - margin.left - margin.right, 
    height = document.getElementById("chartArea").offsetHeight - margin.top - margin.bottom; 

var x = d3.time.scale() 
    .range([0, width]); 

var y = d3.scale.linear() 
    .range([height, 0]); 

var xAxis = d3.svg.axis() 
    .scale(x) 
    .orient("bottom") 
    .ticks(10) 
    .tickFormat(d3.time.format('%X')) 
    .tickSize(1); 
    //.tickPadding(8); 

var yAxis = d3.svg.axis() 
    .scale(y) 
    .orient("left"); 

var valueline = d3.svg.line() 
    .x(function (d) { return x(d.timestamp); }) 
    .y(function (d) { return y(d.value); }); 

var zoom = d3.behavior.zoom() 
    .x(x) 
    .y(y) 
    .scaleExtent([1, 4]) 
    .on("zoom", zoomed); 

var svg = d3.select("#chartArea") 
    .append("svg") 
    .attr("width", width + margin.left + margin.right) 
    .attr("height", height + margin.top + margin.bottom) 
    .append("g") 
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")") 
    .call(zoom); 

svg.append("rect") 
    .attr("width", width) 
    .attr("height", height) 
    .attr("class", "plot"); // ???? 

var clip = svg.append("clipPath") 
    .attr("id", "clip") 
    .append("rect") 
    .attr("x", 0) 
    .attr("y", 0) 
    .attr("width", width) 
    .attr("height", height); 

var chartBody = svg.append("g") 
    .attr("clip-path", "url(#clip)"); 

svg.append("g")   // Add the X Axis 
    .attr("class", "x axis") 
    .attr("transform", "translate(0," + height + ")") 
    .call(xAxis); 

svg.append("g")   // Add the Y Axis 
    .attr("class", "y axis") 
    .call(yAxis); 

svg.append("text") 
    .attr("transform", "rotate(-90)") 
    .attr("y", 0 - margin.left) 
    .attr("x", (0 - (height/2))) 
    .attr("dy", "1em") 
    .style("text-anchor", "middle") 
    .text("Return (%)"); 

// plot the original data by retrieving everything from time 0 
d3.json("/performance/benchmark/date/0/interval/" + dataIntervals, function (error, data) { 
    data.forEach(function (d) { 

     lastUpdateTime = String(d.timestamp); // this will be called until the last element and so will have the value of the last element 
     d.timestamp = new Date(d.timestamp); 
     d.value = d.value * 100; 

    }); 

    globalData = data; 

    x.domain(d3.extent(globalData, function (d) { return d.timestamp; })); 
    y.domain(d3.extent(globalData, function (d) { return d.value; })); 

    chartBody.append("path")  // Add the valueline path 
     .datum(globalData) 
     .attr("class", "line") 
     .attr("d", valueline); 

    var inter = setInterval(function() { 
     updateData(); 
    }, 5000); 

}); 

var panMeasure = 0; 
var oldScale = 1; 
function zoomed() { 
    //onsole.log(d3.event); 
    d3.event.translate[1] = 0; 
    svg.select(".x.axis").call(xAxis); 

    if (Math.abs(oldScale - d3.event.scale) > 1e-5) { 
     oldScale = d3.event.scale; 
     svg.select(".y.axis").call(yAxis); 
    } 

    svg.select("path.line").attr("transform", "translate(" + d3.event.translate[0] + ",0)scale(" + d3.event.scale + ", 1)"); 
    panMeasure = d3.event.translate[0]; 

} 

在下面的代碼塊中,我發出一個http請求來獲取所有新數據並將其添加到圖表中。這工作正常。現在我只需要理清新數據泛邏輯 - 我想象會去在這裏:

var dx = 0; 
function updateData() { 

    var newData = []; 

     d3.json("/performance/benchmark/date/" + lastUpdateTime + "/interval/" + dataIntervals, function (error, data) { 
      data.forEach(function (d) { 

       lastUpdateTime = String(d.timestamp); // must be called before its converted to Date() 
       d.timestamp = new Date(d.timestamp); 
       d.value = d.value * 100; 

       globalData.push(d); 
       newData.push(d); 

      }); 

      // panMeasure would be some measure of how much the user has panned (ie if the right-most part of the graph is still visible to the user. 
      if (panMeasure <= 0) { // add the new data and pan 

       x1 = newData[0].timestamp; 
       x2 = newData[newData.length - 1].timestamp; 
       dx = dx + (x(x1) - x(x2)); // dx needs to be cummulative 

       d3.select("path") 
        .datum(globalData) 
        .attr("class", "line") 
        .attr("d", valueline(globalData)) 
       .transition() 
        .ease("linear") 
        .attr("transform", "translate(" + String(dx) + ")"); 

      } 

      else { // otherwise - just add the new data 
       d3.select("path") 
        .datum(globalData) 
        .attr("class", "line") 
        .attr("d", valueline(globalData)); 
      } 

      svg.select(".x.axis").call(xAxis); 

     }); 
} 

我想要做的(我想這就是我應該做的)是獲得新數據的時間值的範圍(即newData []數組的第一個值和最後一個值之間的差值,將其轉換爲像素,然後使用該數字平移該行。工作,但只在第一次更新。另一個問題是,如果我在數據嘗試更新時使用鼠標進行平移/縮放,則該行會消失,並且不一定會在下一次更新時再次顯示。一些關於潛在錯誤的反饋意見在代碼中和/或應該如何完成。謝謝。

更新1:

好了,所以我想通了什麼與自動平移的問題了。我意識到翻譯矢量需要來自某個來源的累積值,所以一旦我將dx累加爲(dx = dx + (x(x2) - x(x1));,那麼橫向平移就開始工作,以便添加新數據。

更新2:

我現在已經包括了fiddle是接近我期望能夠檢索和數據繪製的實際方式。看來工作在一定程度上,我希望它只是方式:

  1. X軸刻度線不與新的數據平移
  2. 當我做手動平移,行爲變得有點奇怪上第一盤(跳回位)
  3. 如果我搖攝/當新的數據正試圖加入縮放 - 這條線消失:
+0

這是一個相當複雜的問題。沒有小提琴,我想這不太可能有人能幫助你。順便說一句,你看過NVD3嗎?也許它有一些開箱即用的東西... – hgoebl

+0

感謝隊友,是的,我有但我想能夠定製更多的東西,也學會使用D3。我希望至少得到部分解決方案;例如,如何將新數據添加到繪圖或線條(無論外觀如何)等。 – rex

+1

我可以理解你,我處於相同的情況。 D3很棒,但很難調試。最好是使用版本控制(Git),並在事件中斷時提前並回滾所有更改和小步驟。小小的步驟......瞭解所有複製粘貼的代碼非常重要。每個可視化都有自己的面孔。 – hgoebl

回答

-1

相反的(「多線程」的問題S')繪製整個數據並剪切不必要的部分,您可以在每次需要t時保留一個全局數組的值o更新圖表。 然後您可以更輕鬆地重新計算您的x軸&值。缺點是你不能輕易過渡。

所以,你將有兩個變量:

var globalOffset = 0; 
var panOffset = 0; 

globalOffset將在每次填充每次平移時新的價值觀和panOffset時間更新。 然後你只需繪製前切片您的數據:

var offset = Math.max(0, globalOffset + panOffset); 
var graphData = globalData.slice(offset, offset + 100); 

爲完整的解決方案見fiddle

+0

hey mate,謝謝你的回答,但圖表不能在X中平移? – rex

+0

嘗試使用Firefox和Chrome,桌面,並拖動圖形沒有問題的左側或右側平板,只要有數據當然。 – Fradenger

+0

在Chrome上,對我來說這真的很奇怪,這條線的右端被固定在右邊緣上,圖形線向左延伸而不是平移:/ – rex