Make a single line be solid and a section of it da

2020-07-20 01:37发布

问题:

I would like to know if there is an alternative to applying the "stroke-dasharray" to a path. What I'm trying to do is add a dashed line to a certain range in the path. The thing is that the line is not straight. And I want to avoid creating two lines using the same data within different range. Does anyone have an idea?

回答1:

I linked this question which provides a pretty good overview of how stroke-dasharray works, but the interesting part of this question becomes, how can I dash over a specific section of the line? With that in mind, say we have a line from 0 to 10 and we want to dash it from 2.5 to 7:

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  body {
    font: 10px sans-serif;
  }
  
  .axis path,
  .axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
  }
  
  .x.axis path {
    display: none;
  }
  
  .line {
    fill: none;
    stroke: steelblue;
    stroke-width: 1.5px;
  }
</style>

<body>
  <script src="//d3js.org/d3.v4.min.js"></script>
  <script>
    var margin = {
        top: 20,
        right: 20,
        bottom: 30,
        left: 50
      },
      width = 960 - margin.left - margin.right,
      height = 500 - margin.top - margin.bottom;

    var data = d3.range(11).map(function(d, i) {
      return {
        x: i,
        y: Math.random() * 100
      };
    });

    var x = d3.scaleLinear()
      .range([0, width])
      .domain([0, 10]);

    var y = d3.scaleLinear()
      .range([height, 0])
      .domain([0, 100]);

    var xAxis = d3.axisBottom()
      .scale(x);

    var yAxis = d3.axisLeft()
      .scale(y);

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

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

    svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)

    var p = svg.append("path")
      .datum(data)
      .attr("class", "line")
      .attr("d", line);

    // draw dashed from 2.7 to 7 in the X domain
    var dashBetweenX = [2.5, 7]
        path = p.node(),
        totalLen = path.getTotalLength();
    
    // find the corresponding line lengths
    var dashBetweenL = dashBetweenX.map(function(d,i){
      
      var beginning = 0,
          end = totalLen,
          target = null,
          d = x(d);
      
      // find the line lengths the correspond to our X values
      // stolen from @duopixel from http://bl.ocks.org/duopixel/3824661
      while (true){
        target = Math.floor((beginning + end) / 2);
        pos = path.getPointAtLength(target);
        if ((target === end || target === beginning) && pos.x !== d) {
            break;
        }
        if (pos.x > d) end = target;
        else if (pos.x < d) beginning = target;
        else break; //position found
      }
      
      return target;
    })
    
    // draw the dashes
    var sd =  dashBetweenL[0],
        dp = dashBetweenL[0],
        count = 0;
    while (dp < dashBetweenL[1]){
      dp += 2;
      sd += ", 2";
      count++;
    }
    // per answer below needs odd number of dash array
    if (count % 2 == 0)
      sd += ", 2";   
    sd += ", " + (totalLen - dashBetweenL[1]);
    p.attr("stroke-dasharray", sd);    
  </script>
</body>

</html>



回答2:

@Mark has a partially working solution for me. I had to perform one additional check after adding the dashes. If the number of dashes added was even, the line after the dash would not render.

Modifying the section as follows fixed it for me:

let count = 0;
while (dp < dashBetweenL[1]) {
    count ++;
    dp += 4;
    sd += ", 4";
}

if (count % 2 == 0) {
    sd += ", 4, " + (totalLen - dashBetweenL[1]);
} else {
    sd += ", " + (totalLen - dashBetweenL[1]);
}
p.attr("stroke-dasharray", sd);

Truthfully, I'm having a hard time seeing why it was necessary but appears to be working perfectly.