我有一个显示三行的d3图表,其中每一行都有自己的y轴。它还有一个用于显示时间的x轴。所有轴都有缩放/平移。 这适用于显示历史数据,但我还需要一个按钮来开始显示实时数据。
我有一个触发SignalR的按钮,它再次为我提供了可以推送到我的数据阵列的新数据。
我的问题是,每次使用新数据值更新数组时,我如何更新这三行并在水平方向上移动它们。 我已经尝试过关注this和this指南了,但我最后要做的就是重新绘制旧行之上的整行。 (我现在只想更新其中一个)
在显示我的图表代码之前,我应该提到我的数组每秒都会使用新值更新(这发生在一个单独的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>
我确实意识到它不是正确的,但我似乎无法弄清楚如何将新值附加到绘制线的末尾并移动它。
答案 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>