首页 > 解决方案 > 移动折线图上的圆圈

问题描述

想在我的实时折线图上绘制圆圈。该图表每次迭代都会生成随机 y 值并将其绘制在图表上,从而移动域。这是我的代码-

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);
                  
   let circles = minerGEnter.selectAll('circle')/*.data(function(d) {
            return [d];
        });*/
  circles.data(dataList)
          .enter()
          .append('circle')
          .merge(circles)
          .transition()
          .duration(duration)
          .ease(d3.easeLinear, 2)
          .attr('r', 5)
          .attr('cx', (d, i) => timeScale(d.time))
          .attr('cy', d => valueScale(d.value));

  circles.exit().remove();
  
  }

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>

如您所见,图表有效,但圆圈无效。他们是两个问题。图表显示圆圈进入图表的动画,圆圈似乎有错误的 y 值。我很确定我添加圆圈的方式不正确。尝试从在线代码中获取示例,但没有任何效果。

任何想法如何解决这两个问题?

标签: javascriptd3.js

解决方案


圆圈的位置没有错,它们是正确的。它们可能看起来是错误的,因为您d3.curveBasis对路径使用了插值器 ( ),它会修改它:删除插值器可以清楚地说明这一点,如下面的演示所示。此外,关于转换,解决方案可能是在输入选择中设置圆的位置,并且仅x在更新选择中转换位置。

这是您的代码,仅包含这 2 个更改:

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()
  .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);

  let circles = minerGEnter.selectAll('circle').data(function(d) {
    return d;
  });

  circles.exit().remove();

  circles = circles.enter()
    .append('circle')
    .attr('r', 5)
    .attr('cx', (d, i) => timeScale(d.time))
    .attr('cy', d => valueScale(d.value))
    .merge(circles);

  circles.transition()
    .duration(duration)
    .ease(d3.easeLinear, 2)
    .attr('cx', (d, i) => timeScale(d.time));

}

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>


推荐阅读