基于SignalR输入连续更新d3图表中的行

时间:2014-04-30 10:52:40

标签: javascript d3.js signalr

我有一个显示三行的d3图表,其中每一行都有自己的y轴。它还有一个用于显示时间的x轴。所有轴都有缩放/平移。 enter image description here 这适用于显示历史数据,但我还需要一个按钮来开始显示实时数据。

我有一个触发SignalR的按钮,它再次为我提供了可以推送到我的数据阵列的新数据。

我的问题是,每次使用新数据值更新数组时,我如何更新这三行并在水平方向上移动它们。 我已经尝试过关注thisthis指南了,但我最后要做的就是重新绘制旧行之上的整行。 (我现在只想更新其中一个)

在显示我的图表代码之前,我应该提到我的数组每秒都会使用新值更新(这发生在一个单独的js文件中,包含在同一个视图中):

speed.push(entry.Speed);
redraw();

<script>
    /* d3 vars */
    var graph;
    var m = [];
    var w;
    var h;

    /* d3 scales */
    var x;
    var y1;
    var y2;
    var y3;

    /* d3 axes */
    var xAxis;
    var yAxisLeft;
    var yAxisLeftLeft;
    var yAxisRight;

    /* d3 lines */
    var line1;
    var line2;
    var line3;

    /* d3 zoom */
    var zoom;
    var zoomLeftLeft;
    var zoomRight;

    /* Data */
    var speed = [];
    var depth = [];
    var weight = [];
    var timestamp = [];

    var url = '@Url.Action("DataBlob", "Trend", new {id = Model.Unit.UnitId, runId = Request.Params["runId"]})';

    var data = $.getJSON(url, null, function(data) {
        var list = JSON.parse(data);
        var format = d3.time.format("%Y-%m-%dT%H:%M:%S").parse;
        if ($("#IsLiveEnabled").val() != "true") {
            list.forEach(function(d) {
                speed.push(d.Speed);
                depth.push(d.Depth);
                weight.push(d.Weight);
                var date = format(d.Time);
                d.Time = date;
                timestamp.push(d.Time);
            });
        }

        $('#processing').hide();

        m = [20, 85, 30, 140]; // margins: top, right, bottom, left
        w = ($("#trendcontainer").width() - 35) - m[1] - m[3]; // width
        h = 600 - m[0] - m[2]; // height

        x = d3.time.scale().domain(d3.extent(timestamp, function(d) {
            return d;
        })).range([0, w]);

        y1 = d3.scale.linear().domain([0, d3.max(speed)]).range([h, 0]);
        y2 = d3.scale.linear().domain([0, d3.max(depth)]).range([h, 0]);
        y3 = d3.scale.linear().domain([0, d3.max(weight)]).range([h, 0]);

        line1 = d3.svg.line()
            .interpolate("basis")
            .x(function(d, i) {
                return x(timestamp[i]);
            })
            .y(function(d) {
                return y1(d);
            });

        line2 = d3.svg.line()
            .interpolate("basis")
            .x(function(d, i) {
                return x(timestamp[i]);
            })
            .y(function(d) {
                return y2(d);
            });

        line3 = d3.svg.line()
            .interpolate("basis")
            .x(function(d, i) {
                return x(timestamp[i]);
            })
            .y(function(d) {
                return y3(d);
            });

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

        zoomLeftLeft = d3.behavior.zoom()
            .x(x)
            .y(y3)
            .scaleExtent([1, 35])
            .on("zoom", zoomed);

        zoomRight = d3.behavior.zoom()
            .x(x)
            .y(y2)
            .scaleExtent([1, 35])
            .on("zoom", zoomed);

        // Add an SVG element with the desired dimensions and margin.
        graph = d3.select(".panel-body").append("svg:svg")
            .attr("width", w + m[1] + m[3])
            .attr("height", h + m[0] + m[2])
            .call(zoom)
            .append("svg:g")
            .attr("transform", "translate(" + m[3] + "," + m[0] + ")");

        graph.append("defs").append("clipPath")
            .attr("id", "clip")
            .append("rect")
            .attr("width", w)
            .attr("height", h);

        // create xAxis
        xAxis = d3.svg.axis().scale(x).tickSize(-h).tickSubdivide(false);
        // Add the x-axis.
        graph.append("svg:g")
            .attr("class", "x axis")
            .attr("transform", "translate(0," + h + ")")
            .call(xAxis);

        // create left yAxis
        yAxisLeft = d3.svg.axis().scale(y1).ticks(10).orient("left");
        // Add the y-axis to the left
        graph.append("svg:g")
            .attr("class", "y axis axisLeft")
            .attr("transform", "translate(-25,0)")
            .call(yAxisLeft)
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 5)
            .attr("dy", ".71em")
            .style("text-anchor", "end")
            .text("Speed (m/min)");

        // create leftleft yAxis
        yAxisLeftLeft = d3.svg.axis().scale(y3).ticks(10).orient("left");
        // Add the y-axis to the left
        graph.append("svg:g")
            .attr("class", "y axis axisLeftLeft")
            .attr("transform", "translate(-85,0)")
            .call(yAxisLeftLeft)
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", 5)
            .attr("dy", ".71em")
            .style("text-anchor", "end")
            .text("Weight (kg)");

        // create right yAxis
        yAxisRight = d3.svg.axis().scale(y2).ticks(10).orient("right");
        // Add the y-axis to the right
        graph.append("svg:g")
            .attr("class", "y axis axisRight")
            .attr("transform", "translate(" + (w + 25) + ",0)")
            .call(yAxisRight)
            .append("text")
            .attr("transform", "rotate(-90)")
            .attr("y", -15)
            .attr("dy", ".71em")
            .style("text-anchor", "end")
            .text("Depth (m)");

        graph.append("svg:path").attr("d", line1(speed)).attr("class", "y1").attr("clip-path", "url(#clip)");
        graph.append("svg:path").attr("d", line2(depth)).attr("class", "y2").attr("clip-path", "url(#clip)");
        graph.append("svg:path").attr("d", line3(weight)).attr("class", "y3").attr("clip-path", "url(#clip)");
    });

    function redraw() {
        graph.append("svg:path")
            .attr("d", line1(speed))
            .attr("class", "y1")
            .attr("clip-path", "url(#clip)");
    }

    function zoomed() {
        zoomRight.scale(zoom.scale()).translate(zoom.translate());
        zoomLeftLeft.scale(zoom.scale()).translate(zoom.translate());

        graph.select(".x.axis").call(xAxis);
        graph.select(".y.axisLeft").call(yAxisLeft);
        graph.select(".y.axisLeftLeft").call(yAxisLeftLeft);
        graph.select(".y.axisRight").call(yAxisRight);
        graph.select(".x.grid")
            .call(make_x_axis()
                .tickFormat(""));
        graph.select(".y.axis")
            .call(make_y_axis()
                .tickSize(5, 0, 0));
        graph.selectAll(".y1")
            .attr("d", line1(speed));
        graph.selectAll(".y2")
            .attr("d", line2(depth));
        graph.selectAll(".y3")
            .attr("d", line3(weight));
    };

    var make_x_axis = function() {
        return d3.svg.axis()
            .scale(x)
            .orient("bottom")
            .ticks(10);
    };

    var make_y_axis = function() {
        return d3.svg.axis()
            .scale(y1)
            .orient("left")
            .ticks(10);
    };

</script>

我确实意识到它不是正确的,但我似乎无法弄清楚如何将新值附加到绘制线的末尾并移动它。

1 个答案:

答案 0 :(得分:0)

在这里,我创建了一个非常基本的图形,其中的按钮可以满足您的需求。由于缺少一些日期,这有点不稳定。我希望它可以帮到你,它基于http://bl.ocks.org/mbostock

的一个简单样本

以下是相关部分:

    //Add new post to data
data.push({
    "Date": new Date(new Date().setDate(new Date().getDate() + skip)),
        "Close": price
});

//assign the new data to the graph
svg.select(".area")
    .datum(data)
    .attr("d", area)
    .attr("transform", null);

//Make sure the X axis is updated (not updating the Y axis in the example, but same principle)
x.domain(d3.extent(data.map(function (d) {
    return d.Date;
})));
svg.select(".x.axis").transition().call(xAxis);

data.shift();

var skip = 0;
var oldPrice = 11;

var data = [{
    "Date": "2015-08-20",
        "Close": 7
}, {
    "Date": "2015-08-23",
        "Close": 8
}, {
    "Date": "2015-08-24",
        "Close": 9
}, {
    "Date": "2015-08-25",
        "Close": 6
}, {
    "Date": "2015-08-26",
        "Close": 5
}, {
    "Date": "2015-08-27",
        "Close": 7
}, {
    "Date": "2015-08-30",
        "Close": 5
}, {
    "Date": "2015-08-31",
        "Close": 9
}, {
    "Date": "2015-09-01",
        "Close": 8
}, {
    "Date": "2015-09-02",
        "Close": 10
}, {
    "Date": "2015-09-03",
        "Close": 11
}, {
    "Date": "2015-09-07",
        "Close": 12
}, {
    "Date": "2015-09-08",
        "Close": 11
}, {
    "Date": "2015-09-09",
        "Close": 12
}, {
    "Date": "2015-09-10",
        "Close": 13
}, {
    "Date": "2015-09-13",
        "Close": 14
}, {
    "Date": "2015-09-14",
        "Close": 15
}, {
    "Date": "2015-09-15",
        "Close": 13
}, {
    "Date": "2015-09-16",
        "Close": 11
}, {
    "Date": "2015-09-17",
        "Close": 7
}, {
    "Date": "2015-09-20",
        "Close": 6
}, {
    "Date": "2015-09-21",
        "Close": 5
}, {
    "Date": "2015-09-22",
        "Close": 6
}, {
    "Date": "2015-09-23",
        "Close": 7
}, {
    "Date": "2015-09-24",
        "Close": 8
}, {
    "Date": "2015-09-27",
        "Close": 10
}, {
    "Date": "2015-09-28",
        "Close": 9
}, {
    "Date": "2015-09-29",
        "Close": 10
}, {
    "Date": "2015-09-30",
        "Close": 11
}];


var margin = {
    top: 10,
    right: 10,
    bottom: 100,
    left: 40
};
var width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;


var parseDate = d3.time.format("%Y-%m-%d").parse;

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

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

var xAxis = d3.svg.axis().scale(x).orient("bottom"),
    yAxis = d3.svg.axis().scale(y).orient("left");

var area = d3.svg.area()
    .interpolate("monotone")
    .x(function (d) {
    return x(d.Date);
})
    .y0(height)
    .y1(function (d) {
    return y(d.Close);
});


var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom);

svg.append("defs").append("clipPath")
    .attr("id", "clip")
    .append("rect")
    .attr("width", width)
    .attr("height", height);

var focus = svg.append("g")
    .attr("class", "focus")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


data.forEach(function (d) {
    d.Date = parseDate(d.Date);
    d.Close = +d.Close;

});

x.domain(d3.extent(data.map(function (d) {
    return d.Date;
})));
y.domain([0, 200]);

focus.append("path")
    .datum(data)
    .attr("class", "area")
    .attr("d", area);

focus.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis);

focus.append("g")
    .attr("class", "y axis")
    .call(yAxis);

function update() {

    //Generate a new price
    var price = Math.floor(Math.random() * 5) + oldPrice

    //Add new post to data
    data.push({
        "Date": new Date(new Date().setDate(new Date().getDate() + skip)),
            "Close": price
    });

    //assign the new data to the graph
    svg.select(".area")
        .datum(data)
        .attr("d", area)
        .attr("transform", null);

    //Make sure the X axis is updated (not updating the Y axis in the example, but same principle)
    x.domain(d3.extent(data.map(function (d) {
        return d.Date;
    })));
    svg.select(".x.axis").transition().call(xAxis);

    data.shift();

    skip++;
    oldPrice = price;
}


$(document).ready(function () {

    $('#update').click(function () {
        update();

    });

});
svg {
    font: 10px sans-serif;
}
.area {
    fill: steelblue;
    clip-path: url(#clip);
}
.axis path, .axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}
.brush .extent {
    stroke: #fff;
    fill-opacity: .125;
    shape-rendering: crispEdges;
}
<html>
<head>  
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
</head>
<body>
<input type="button" id="update" value="update">
</body>
</html>