I have created a scatter chart in NVD3.js with dynamic data, but I cannot manage to prevent points from moving across the screen when the data changes (see 'demo snippet' at the end of this question). I would like these points to appear and disappear instantaneously, without any movement at all. Is this possible?
What I have tried
- Played around with chart's settings:
- chart.duration(0): no effect (from How to remove NVD3 chart resize/update delay)
- chart.duration(-1): idem
- chart.transitionDuration(0): shown in examples on nvd3.org but found out that function no longer exists (see: transitionDuration function does not exist in nvd3.js).
- chartElement.transition().duration(0): no effect
- Simply reinitialize chart everytime the data changes (not elegant, I know). This did not work because the chart also animates when it initializes.
- Even uglier: disable all D3 animations as explained in Disabling all D3 animations (for testing), but also no success.
I have found out that someone else had a similar problem with NVD3's pie chart (https://github.com/novus/nvd3/issues/1474). It turned out that duration is not even used anywhere in the pie chart model. This does not seem to apply to the scatter chart (see https://github.com/novus/nvd3/blob/master/src/models/scatterChart.js#L96-L101, line 96 until 101):
chart.update = function() {
if (duration === 0)
container.call(chart);
else
container.transition().duration(duration).call(chart);
};
More details
- I am using:
- D3.js v3.4.11
- NVD3.js v1.8.2
- In case that this turns out the be a bug then I will try to write and commit a bug fix. Nevertheless, it would be nice to have a quick workaround.
Demo snippet
Run the snippet and move the slider to see the problem.
var chart;
var dataset; // All data points
var subset; // Datapoints that are close to
function randomPoint() {
return {
x: Math.random(),
y: Math.random(),
time: Math.random()
}
}
// Calculate the subset
function updateSubset(time) {
var upperBound = time + 0.1;
var lowerBound = time - 0.1;
subset[0].values = dataset[0].values.filter(function(d) {
return lowerBound < d.time && d.time < upperBound;
});
}
subset = [{
key: "Foobar",
values: []
}];
for (var i = 0; i < 100; i++) {
subset[0].values.push(randomPoint());
}
// Make a deep copy of the full dataset
dataset = jQuery.extend(true, {}, subset);
chart = nv.models.scatterChart()
.forceX([0, 1])
.forceY([0, 1])
.pointRange([150, 150])
.duration(0); // This doesn't seem to work?
updateSubset(0.5);
d3.select("#chart svg")
.datum(subset)
.call(chart);
// Recalculate subset and update chart when the slider is changed
$("#time").on("input", function() {
var time = Number($("#time").val());
updateSubset(time);
chart.update();
});
#time {
width: 100%;
}
#chart {
width: 100%;
height: 400px;
}
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/nvd3/1.8.2/nv.d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
Time control:
</div>
<div>
<input id="time" type="range" min="0" max="1" value="0.5" step="0.01" />
</div>
<div>
Points around that time:
</div>
<div id="chart">
<svg></svg>
</div>