javascript - 根据 SignalR 输入不断更新 d3 图表中的线条

标签 javascript d3.js signalr

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

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

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

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

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>

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

最佳答案

在这里,我制作了一个非常基本的图表,其中包含一个按钮,可以满足您的需求。它有点不稳定,因为缺少一些日期。我希望它能帮到你,它基于 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>

关于javascript - 根据 SignalR 输入不断更新 d3 图表中的线条,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23386162/

相关文章:

javascript - Moment Js UTC 到本地时间

javascript - 新/更新的元素继承旧元素 D3 的事件处理程序

javascript - d3v4 为什么我的线条元素没有出现?

javascript - 如何 'unrender' MathJax方程?

c# - JavaScript OnClientClick 破坏 RadGrid 插入/更新按钮在 Web 表单上提交行为

javascript - RequireJS 模块从 API 服务器接收 JSON,返回未定义?

javascript - D3 不在折线图上绘制数据

c# - Moq 可以模拟 HubConnection 但 RhinoMocks 不能?

ios - 连接SignalR服务器的流程

azure - 与 Azure 事件中心通信时出现 EPOCH 错误