【问题标题】:Getting smooth animations in Line Chart D3在折线图 D3 中获得流畅的动画
【发布时间】:2020-05-16 03:08:28
【问题描述】:

我在 d3 中处理实时折线图,最后让我的图表更新 x 轴并将其向左移动,它工作正常。我还能够移动图表线(路径),但是由于某种原因动画不流畅。`

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>

` 我在这里使用了示例中的代码:http://bl.ocks.org/Sohalt/9715be30ba57e00f2275d49247fa7118/43a24a4dfa44738a58788d05230407294ab7a348

有什么想法吗?

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    如果您查看图表,一条路径似乎更粗。如果我们查看 DOM,我们会看到许多路径,每次更新都会为 DOM 带来一条新路径。

    这是因为您创建更新函数的方式:

    每次更新你添加一个随机值:

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

    然后您使用该数据输入g 元素。

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

    但是,这里的数据是点数组。我们只希望每个系列有一个g,而不是数据点。因此,会立即创建许多 g 元素,并且每次更新都会添加一个。

    然后,对于每个新的g,您附加一条路径并用dataList 中的点绘制它。

    let minerSVG = minerGEnter.selectAll('path').data(function(d) {
      return [d];
    });
    
    let minerSVGenter = minerSVG.enter()
        .append('path')
        ...
        .attr('d', line(dataList))
        ...
    

    最简单的修复方法只添加两个字符:

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

    通过使用[dataList] 而不是dataList,我们只会输入或更新一个元素,而不是dataList 中的每个坐标都有一个元素。

    虽然我可能会提出一些其他更改,但以下是您的代码,其中一项更改是:

    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>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多