简体   繁体   中英

Getting smooth animations in Line Chart D3

I was working on a real time line chart in d3 and finally got my chart to update the x axis and shift it left and it works fine. I was also able to shift the chart lines (paths) however for some reason the animation is not smooth.`

 const svg = d3.select('svg'); const MARGIN = {TOP: 50, BOTTOM: 50, LEFT: 50, RIGHT: 50}; const WIDTH = svg.attr('width') - MARGIN.LEFT - MARGIN.RIGHT; const HEIGHT = svg.attr('height') - MARGIN.TOP - MARGIN.BOTTOM; const limit = 60; const duration = 500; let dataList = []; let g = svg.append('g').attr('transform', `translate( ${MARGIN.LEFT}, ${MARGIN.TOP} )`); g.append('defs').append('clipPath').attr('id', 'clip2').append('rect').attr('x', 0).attr('y', 0).attr('width', WIDTH).attr('height', HEIGHT); // ParseTime const timeScale = d3.scaleTime().range([0, WIDTH]); const valueScale = d3.scaleLinear().domain([0, 10]).range([HEIGHT, 0]); const line = d3.line().curve(d3.curveBasis).x((d) => timeScale(d.time)).y((d) => valueScale(d.value)); const xAxis = d3.axisBottom(timeScale); const axisCall = g.append('g').attr('transform', `translate(0, ${HEIGHT})`); axisCall.call(xAxis); g.append('g').attr('class', 'axis axis--y').call(d3.axisLeft(valueScale)) let pathsG = g.append('g').attr('id', 'paths').attr('class', 'paths').attr('clip-path', 'url(#clip2)'); function updateChart() { let now = Date.now(); dataList.push({ time: now, value: Math.floor(Math.random() * 10) }); // Shift domain timeScale.domain([now - ((limit - 2) * duration), now - duration]); axisCall.transition().duration(duration).ease(d3.easeLinear, 2).call(xAxis); let minerG = pathsG.selectAll('.minerLine').data(dataList); let minerGEnter = minerG.enter().append('g').attr('class', 'minerLine').merge(minerG); let minerSVG = minerGEnter.selectAll('path').data(function(d) { return [d]; }); let minerSVGenter = minerSVG.enter().append('path').attr('class', 'line').style('stroke', '#D073BA').style('fill', 'none').merge(minerSVG).transition().duration(duration).ease(d3.easeLinear, 2).attr('d', line(dataList)).attr('transform', null); } setInterval(function(){ //console.log('hello'); updateChart(); }, 500);
 <.DOCTYPE html> <html> <head></head> <title>Real-time Line Chart D3</title> <link rel="stylesheet" href="styles:css"> <script src="https.//d3js.org/d3.v5.min.js"></script> <body> <svg width="960" height="500"> </svg> <script src="bundle.js"></script> </body> </html>

` I used the code from the example here: http://bl.ocks.org/Sohalt/9715be30ba57e00f2275d49247fa7118/43a24a4dfa44738a58788d05230407294ab7a348

Any ideas what is wrong?

If you look at the graph, one path seems to be thicker. If we look at the DOM we see many paths, every update brings a new path into the DOM.

This is because of how you have create the update function:

Every update you add a random value:

dataList.push({
    time: now,
  value: Math.floor(Math.random() * 10)
});

Then you use that data to enter g elements.

let minerG = pathsG.selectAll('.minerLine').data(dataList);
let minerGEnter = minerG.enter()
    .append('g')
    ...

But, the data here is the array of points. We only want one g per series, not data point. So what happens is many g elements are created right away, and one is added every update.

Then with every new g , you append a path and draw it with the points in dataList .

let minerSVG = minerGEnter.selectAll('path').data(function(d) {
  return [d];
});

let minerSVGenter = minerSVG.enter()
    .append('path')
    ...
    .attr('d', line(dataList))
    ...

The simplest fix only adds two characters:

let minerG = pathsG.selectAll('.minerLine').data([dataList]);

By using [dataList] instead of dataList we are only ever entering or updating one element, not one for every coordinate in dataList .

While I might propose a few other changes, here's your code with that one change:

 const svg = d3.select('svg'); const MARGIN = {TOP: 50, BOTTOM: 50, LEFT: 50, RIGHT: 50}; const WIDTH = svg.attr('width') - MARGIN.LEFT - MARGIN.RIGHT; const HEIGHT = svg.attr('height') - MARGIN.TOP - MARGIN.BOTTOM; const limit = 60; const duration = 500; let dataList = []; let g = svg.append('g').attr('transform', `translate( ${MARGIN.LEFT}, ${MARGIN.TOP} )`); g.append('defs').append('clipPath').attr('id', 'clip2').append('rect').attr('x', 0).attr('y', 0).attr('width', WIDTH).attr('height', HEIGHT); // ParseTime const timeScale = d3.scaleTime().range([0, WIDTH]); const valueScale = d3.scaleLinear().domain([0, 10]).range([HEIGHT, 0]); const line = d3.line().curve(d3.curveBasis).x((d) => timeScale(d.time)).y((d) => valueScale(d.value)); const xAxis = d3.axisBottom(timeScale); const axisCall = g.append('g').attr('transform', `translate(0, ${HEIGHT})`); axisCall.call(xAxis); g.append('g').attr('class', 'axis axis--y').call(d3.axisLeft(valueScale)) let pathsG = g.append('g').attr('id', 'paths').attr('class', 'paths').attr('clip-path', 'url(#clip2)'); function updateChart() { let now = Date.now(); dataList.push({ time: now, value: Math.floor(Math.random() * 10) }); // Shift domain timeScale.domain([now - ((limit - 2) * duration), now - duration]); axisCall.transition().duration(duration).ease(d3.easeLinear, 2).call(xAxis); let minerG = pathsG.selectAll('.minerLine').data([dataList]); let minerGEnter = minerG.enter().append('g').attr('class', 'minerLine').merge(minerG); let minerSVG = minerGEnter.selectAll('path').data(function(d) { return [d]; }); let minerSVGenter = minerSVG.enter().append('path').attr('class', 'line').style('stroke', '#D073BA').style('fill', 'none').merge(minerSVG).transition().duration(duration).ease(d3.easeLinear, 2).attr('d', line(dataList)).attr('transform', null); } setInterval(function(){ //console.log('hello'); updateChart(); }, 500);
 <.DOCTYPE html> <html> <head></head> <title>Real-time Line Chart D3</title> <link rel="stylesheet" href="styles:css"> <script src="https.//d3js.org/d3.v5.min.js"></script> <body> <svg width="960" height="500"> </svg> <script src="bundle.js"></script> </body> </html>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM