插值后访问d3.line的y值

时间:2019-07-05 15:35:42

标签: d3.js

我正在使用curveMonotoneX插值绘制一条简单的线:

  const line = d3
    .line()
    .x((_, i) => xScale(i))
    .y(d => yScale(d))
    .curve(d3.curveMonotoneX);

除此之外,我想在有实际数据的线上添加点。由于进行了插值,我绘制的点并不完全在直线上,所以我切换到d3.curveLinear,问题就解决了。

但是,我想知道是否有一种现成的方法可以使用y值访问行的x值?

这样,无论插值方法如何,都可以在线上绘制点。

1 个答案:

答案 0 :(得分:0)

这是一个简单的示例,将代码here包装到可重用的函数中。它将一堆点放置在拟合曲线上。

<!DOCTYPE html>
<meta charset="utf-8">

<style type="text/css">
  .line {
    fill: none;
    stroke: orange;
    stroke-width: 2;
  }
  
  .overlay {
    fill: none;
    pointer-events: all;
  }
  
  .dot {
    fill: steelblue;
    stroke: #fff;
  }
  
</style>


<body>

<!-- Load in the d3 library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
  var margin = {
      top: 50,
      right: 50,
      bottom: 50,
      left: 50
    },
    width = window.innerWidth - margin.left - margin.right,
    height = window.innerHeight - margin.top - margin.bottom;

  var xScale = d3.scaleLinear()
    .domain([0, 9])
    .range([0, width]);

  var yScale = d3.scaleLinear()
    .domain([0, 10])
    .range([height, 0]);


  var line = d3.line()
    .x(function(d, i) {
      return xScale(i);
    })
    .y(function(d) {
      return yScale(d);
    })
    .curve(d3.curveBasis);

  var dataset = d3.range(10).map(function(d) {
    return d3.randomUniform(1)() * 10;
  })

  var svg = d3.select("body").append("svg")
    .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("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(d3.axisBottom(xScale));

  svg.append("g")
    .attr("class", "y axis")
    .call(d3.axisLeft(yScale));
    
  var path = svg.append("path")
    .datum(dataset)
    .attr("class", "line")
    .attr("d", line);
    
  svg.selectAll(".dot")
    .data(d3.range(0, 9.5, 0.5))
    .enter()
    .append("circle")
    .attr("cx", (d) => xScale(d))
    .attr("cy", (d) => yValueForX(d))
    .attr("r", 5)
    .attr("class", "dot")
    
  function yValueForX(xCor){
    var x = xScale(xCor),
        pathEl = path.node(),
        pathLength = pathEl.getTotalLength(); 
        
    var beginning = x, end = pathLength, target;
    while (true) {
      target = Math.floor((beginning + end) / 2);
      pos = pathEl.getPointAtLength(target);
      if ((target === end || target === beginning) && pos.x !== x) {
          break;
      }
      if (pos.x > x)      end = target;
      else if (pos.x < x) beginning = target;
      else                break; //position found
    }
    
    return pos.y;
  }
    
    
</script>

</body>