插值后访问D3的y值

  • 本文关键字:D3 访问 插值 d3.js
  • 更新时间 :
  • 英文 :


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

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

除此之外,我还想在有实际数据的线上添加点。由于插值,我得出的点并不完全在线上,所以我切换到d3.curveLinear,并且我的问题消失了。

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

以这种方式,无论插值方法如何,都可以在线上提取点。

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

<!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>

最新更新