Javascript 具有实时数据、平移和缩放功能的D3.JS时间序列折线图

Javascript 具有实时数据、平移和缩放功能的D3.JS时间序列折线图,javascript,d3.js,real-time,linechart,linegraph,Javascript,D3.js,Real Time,Linechart,Linegraph,您可以保留一个包含所有值的全局数组,而不是绘制整个数据并剪裁不必要的部分,您只需在每次需要更新图形时切片即可。 然后,您可以更轻松地重新计算x轴和值。不利的一面是你不能轻易地进行转换 因此,有两个变量: globalOffset将在每次填充新值时更新,而panOffset将在每次平移时更新。 然后,只需在打印前对数据进行切片即可: 有关完整的解决方案,请参阅。这是一个相当复杂的问题。如果没有小提琴,我想不可能有人能帮助你。顺便问一下,你看过NVD3吗?也许它有一些现成的东西给你…谢谢,伙计,

您可以保留一个包含所有值的全局数组,而不是绘制整个数据并剪裁不必要的部分,您只需在每次需要更新图形时切片即可。 然后,您可以更轻松地重新计算x轴和值。不利的一面是你不能轻易地进行转换

因此,有两个变量:

globalOffset
将在每次填充新值时更新,而
panOffset
将在每次平移时更新。 然后,只需在打印前对数据进行切片即可:


有关完整的解决方案,请参阅。

这是一个相当复杂的问题。如果没有小提琴,我想不可能有人能帮助你。顺便问一下,你看过NVD3吗?也许它有一些现成的东西给你…谢谢,伙计,是的,我有,但我想能够定制更多的东西,并学习使用D3。我希望至少能得到部分解决方案;例如,如何将新数据添加到绘图或线条(无论外观如何),等等。我可以理解你,我也处于相同的情况。D3非常棒,但很难调试。最好使用版本控制(Git),在前进时提交所有更改和小步骤,在出现问题时回滚。小步,小步。。。理解所有复制和粘贴的代码非常重要。每个形象都有自己的面孔。你认为我每次都应该重画整行吗/从理论上讲,如果相同的数据具有密钥,则不需要重新绘制。缩放、平移时。。。它只是改变了大小和位置,可能被剪裁了。使用Firefox和Chrome、桌面进行了尝试,并将图形向左或向右拖动,没有问题,当然,只要有数据。这对我来说在Chrome上真的很奇怪,线的右端固定在右边缘,图形线向左延伸而不是平移:/
[{"timestamp":1399325270,"value":-0.0029460209892230222598710528},{"timestamp":1399325271,"value":-0.0029460209892230222598710528},{"timestamp":1399325279,"value":-0.0029460209892230222598710528},....]
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];

}
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);

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