如何修复更改浏览器选项卡时不同步的可视化?
How to fix viz that is getting out of sync when I change browser tabs?
我正在使用 this jsFiddle 作为模板,我注意到当我更改浏览器选项卡并返回时,图表会不同步(彩色线条不会移动,但 x -轴偏移)。
我可以在 d3 中添加什么,以便在我更改浏览器选项卡然后更改回可视化项选项卡时同时移动可视化项的线条和时间轴?完整代码如下:
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<title>Live Pressure Data</title>
<style>
svg {
font: 12px sans-serif;
}
.line {
fill: none;
stroke: black;
stroke-width: 1.5px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.y.axis path {
display: none;
}
.y.axis line {
stroke: #777;
stroke-dasharray: 2px,2px;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
</style>
<script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
<!--<script src="/socket.io/socket.io.js"></script>-->
</head>
<body>
<script>
var n = 120;
var nSensors = 4;
var duration = 1000;
var now = new Date(Date.now() - duration);
// 1 x nSensors array of zeros
var count = d3.range(nSensors).map(function () {
return 0;
});
// nSensors x n array of zeros
var data = count.map(function () {
return d3.range(n).map(function () {
return 0;
});
});
var margin = { top: 20, right: 10, bottom: 20, left: 20 };
var width = 800 - margin.right - margin.left;
var height = 300 - margin.top - margin.bottom;
var x = d3.time.scale()
.domain([now - (n - 2) * duration, now - duration])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, 100])
.range([height, 0]);
var line = d3.svg.line()
.interpolate("basis")
.x(function (d, i) { return x(now - (n - 1 - i) * duration); })
.y(function (d, i) { return y(d); });
var color = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("class", "lineChart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var yAxis = d3.svg.axis()
.scale(y)
.tickSize(width)
.orient("right")
var gy = svg.append("g")
.attr("class", "y axis")
.call(yAxis)
gy.selectAll("text")
.attr("text-anchor", "end")
.attr("x", 4)
.attr("dy", -4)
var xAxis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(x.axis = d3.svg.axis().scale(x).orient("bottom"));
var clipPath = svg.append("g")
.attr("clip-path", "url(#clip)");
var paths = clipPath.append("g")
for (var series in data) {
paths.append("path")
.attr("class", "line")
.data([data[series]])
.style("stroke", color(series))
}
// Live bar graph
var barW = 300 - margin.left - margin.right;
var barH = 190 - margin.top - margin.bottom;
var rectH = 28;
var barX = d3.scale.linear()
.domain([0, 100])
.range([0, barW]);
var barSvg = d3.select("body").append("svg")
.attr("class", "barChart")
.attr("width", barW + margin.right + margin.left)
.attr("height", barH + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
barSvg.selectAll("rect")
.data(count)
.enter().append("rect")
.attr("y", function (d, i) { return i * 38; })
.attr("width", barX)
.style("fill", function (d, i) { return color(i); })
.attr("height", rectH);
barSvg.selectAll("text")
.data(count)
.enter().append("text")
.attr("x", 50)
.attr("y", function (d, i) { return (i * 38) + 14 })
.attr("dy", "0.35em")
.attr("text-anchor", "end")
.text(function (d) { return d + " psi" })
barSvg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + barH + ")")
.call(d3.svg.axis().scale(barX).orient("bottom"))
// Live data
//var socket = io.connect('');
//socket.on('news', function (pressure) {
// oldCount = count;
// count = pressure.values;
//})
setInterval(function () {
for (var series in count) {
count[series] += (Math.random() - 0.5) * 5;
count[series] = Math.min(Math.max(count[series], 0), 100);
}
}, 900);
// Animate
tick();
function tick() {
// update the domains
now = new Date();
x.domain([now - (n - 2) * duration, now - duration]);
for (var series in data) {
data[series].push(count[series]);
}
// slide the x-axis left
xAxis.transition()
.duration(duration)
.ease("linear")
.call(x.axis);
// redraw the line
svg.selectAll(".line")
.attr("d", line)
// slide the line left
paths.attr("transform", null)
.transition()
.duration(duration)
.ease("linear")
.attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
.each("end", tick);
// pop the old data point off the front
for (var series in data) {
data[series].shift();
}
// bar animation
barSvg.selectAll("rect")
.data(count)
.transition()
.duration(duration)
.attr("width", barX);
barSvg.selectAll("text")
.data(count)
.text(function (d) { return Math.floor(d) + " psi" })
}
</script>
</body>
</html>
您的转换效果很差。
如果您查看示例 here 的源代码,您会注意到 tick 函数中的这条奇怪的行:
function tick() {
transition = transition.each(function() {
...
根据docs:
If type is not specified, behaves similarly to selection.each:
immediately invokes the specified function for each element in the
current transition, passing in the current datum d and index i, with
the this context of the current DOM element. Any transitions created
within the scope of transition.each will inherit transition parameters
from the parent transition, including id, delay, duration and easing.
Thus, transitions created within a transition.each will not interrupt
the parent transition, similar to subtransitions.
The transition.each method can be used to chain transitions and apply
shared timing across a set of transitions.
[加粗我的]
将此应用于您的 example,似乎可以解决您的问题。
请注意,我必须更改您的 paths
变量。在我结束过渡后,当包含在外部 g
元素中时,它不再是平滑过渡。
我正在使用 this jsFiddle 作为模板,我注意到当我更改浏览器选项卡并返回时,图表会不同步(彩色线条不会移动,但 x -轴偏移)。
我可以在 d3 中添加什么,以便在我更改浏览器选项卡然后更改回可视化项选项卡时同时移动可视化项的线条和时间轴?完整代码如下:
<!DOCTYPE html>
<meta charset="utf-8">
<html>
<head>
<title>Live Pressure Data</title>
<style>
svg {
font: 12px sans-serif;
}
.line {
fill: none;
stroke: black;
stroke-width: 1.5px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.y.axis path {
display: none;
}
.y.axis line {
stroke: #777;
stroke-dasharray: 2px,2px;
}
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
</style>
<script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
<!--<script src="/socket.io/socket.io.js"></script>-->
</head>
<body>
<script>
var n = 120;
var nSensors = 4;
var duration = 1000;
var now = new Date(Date.now() - duration);
// 1 x nSensors array of zeros
var count = d3.range(nSensors).map(function () {
return 0;
});
// nSensors x n array of zeros
var data = count.map(function () {
return d3.range(n).map(function () {
return 0;
});
});
var margin = { top: 20, right: 10, bottom: 20, left: 20 };
var width = 800 - margin.right - margin.left;
var height = 300 - margin.top - margin.bottom;
var x = d3.time.scale()
.domain([now - (n - 2) * duration, now - duration])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, 100])
.range([height, 0]);
var line = d3.svg.line()
.interpolate("basis")
.x(function (d, i) { return x(now - (n - 1 - i) * duration); })
.y(function (d, i) { return y(d); });
var color = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("class", "lineChart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var yAxis = d3.svg.axis()
.scale(y)
.tickSize(width)
.orient("right")
var gy = svg.append("g")
.attr("class", "y axis")
.call(yAxis)
gy.selectAll("text")
.attr("text-anchor", "end")
.attr("x", 4)
.attr("dy", -4)
var xAxis = svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(x.axis = d3.svg.axis().scale(x).orient("bottom"));
var clipPath = svg.append("g")
.attr("clip-path", "url(#clip)");
var paths = clipPath.append("g")
for (var series in data) {
paths.append("path")
.attr("class", "line")
.data([data[series]])
.style("stroke", color(series))
}
// Live bar graph
var barW = 300 - margin.left - margin.right;
var barH = 190 - margin.top - margin.bottom;
var rectH = 28;
var barX = d3.scale.linear()
.domain([0, 100])
.range([0, barW]);
var barSvg = d3.select("body").append("svg")
.attr("class", "barChart")
.attr("width", barW + margin.right + margin.left)
.attr("height", barH + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
barSvg.selectAll("rect")
.data(count)
.enter().append("rect")
.attr("y", function (d, i) { return i * 38; })
.attr("width", barX)
.style("fill", function (d, i) { return color(i); })
.attr("height", rectH);
barSvg.selectAll("text")
.data(count)
.enter().append("text")
.attr("x", 50)
.attr("y", function (d, i) { return (i * 38) + 14 })
.attr("dy", "0.35em")
.attr("text-anchor", "end")
.text(function (d) { return d + " psi" })
barSvg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + barH + ")")
.call(d3.svg.axis().scale(barX).orient("bottom"))
// Live data
//var socket = io.connect('');
//socket.on('news', function (pressure) {
// oldCount = count;
// count = pressure.values;
//})
setInterval(function () {
for (var series in count) {
count[series] += (Math.random() - 0.5) * 5;
count[series] = Math.min(Math.max(count[series], 0), 100);
}
}, 900);
// Animate
tick();
function tick() {
// update the domains
now = new Date();
x.domain([now - (n - 2) * duration, now - duration]);
for (var series in data) {
data[series].push(count[series]);
}
// slide the x-axis left
xAxis.transition()
.duration(duration)
.ease("linear")
.call(x.axis);
// redraw the line
svg.selectAll(".line")
.attr("d", line)
// slide the line left
paths.attr("transform", null)
.transition()
.duration(duration)
.ease("linear")
.attr("transform", "translate(" + x(now - (n - 1) * duration) + ")")
.each("end", tick);
// pop the old data point off the front
for (var series in data) {
data[series].shift();
}
// bar animation
barSvg.selectAll("rect")
.data(count)
.transition()
.duration(duration)
.attr("width", barX);
barSvg.selectAll("text")
.data(count)
.text(function (d) { return Math.floor(d) + " psi" })
}
</script>
</body>
</html>
您的转换效果很差。
如果您查看示例 here 的源代码,您会注意到 tick 函数中的这条奇怪的行:
function tick() {
transition = transition.each(function() {
...
根据docs:
If type is not specified, behaves similarly to selection.each: immediately invokes the specified function for each element in the current transition, passing in the current datum d and index i, with the this context of the current DOM element. Any transitions created within the scope of transition.each will inherit transition parameters from the parent transition, including id, delay, duration and easing. Thus, transitions created within a transition.each will not interrupt the parent transition, similar to subtransitions.
The transition.each method can be used to chain transitions and apply shared timing across a set of transitions.
[加粗我的]
将此应用于您的 example,似乎可以解决您的问题。
请注意,我必须更改您的 paths
变量。在我结束过渡后,当包含在外部 g
元素中时,它不再是平滑过渡。