Home > Software design >  Dynamically fill area under the polyline- svg
Dynamically fill area under the polyline- svg

Time:10-20

I am getting the points dynamically, I have to fill the area under the polyline. I have tried the fill property but that would not result in what I have expected, screenshot attached.

     <svg
        viewBox={`0 0 ${points.length * 10} 100`}
        height="100%"
        width="150px"
      >
        <polyline fill="rgba(0,116,217,0.5)" stroke="#0074d9" stroke-width="5" points={points} />
     </svg>

the output is something like this

the output is something like this

and I want it to be something like this

enter image description here

I have already tried different solutions to that, like by using a path instead of using a polyline, but It is a little bit more complex if we have dynamic co-ordinates (points).

CodePudding user response:

I guess you could manually add points to your {points} list. First point in your list should be (0,0) (as i understood that should be your bottom left). And your last point should be (max_x, 0), here you could easily use your last point to get max_x.

Then fill should do what you expected.

EDIT: This made the top of the table colored instead of the bottom one, thanks to @Danny '365CSI' Engelman for pointing out you can simply invert that with (0,max_y) and (max_x,max_y)

CodePudding user response:

As suggested by @enxaneta, you can just prepend a starting and append an end point to your array.

svg vs. cartesian coordinate system

In svg the x/y origin is in the upper left.

So you might need to flip your y coordinates as well to display your values in a cartesian coordinate system.

See also: Understanding SVG Coordinate Systems and Transformations (Part 1).

So your starting point would be:

let pointStart = [0, svgHeight];

Example

let points = [
  [0, 10], 
  [10, 20], 
  [20, 50], 
  [30, 20], 
  [40, 10], 
  [50, 30]  
];

let svgHeight=100;
let svgWidth=(points.length-1)*10;
let strokeWidth= 5;
let pointStart = [0, svgHeight];
let pointEnd = [svgWidth, svgHeight];

// convert to mathematical (cartesian) coordinate system
let pointsFlipped = points.map(val=>{return [val[0], svgHeight-val[1]] });
points = pointsFlipped;

// add points:append end, prepend start
points.push(pointEnd);
points = pointStart.concat(points);

//adjust viewBox: 
svg.setAttribute('viewBox', [0, 0, svgWidth, svgHeight].join(' '));
polyline.setAttribute('points', points.join(' '));
svg{
  border:1px solid red;
  max-height:70vh;
  width: auto;
  overflow:visible;
}


polyline {
  stroke-linejoin: miter;
  stroke-miterlimit: 10;
  paint-order: stroke;
}
<svg id="svg" viewBox="0 0 50 100" >
<polyline id="polyline" paint-order="stroke" 
fill="#5cb3ff" stroke="#0074d9" stroke-width="5" />
</svg>

Besides, you might need paint-order: stroke to avoid borders around the graph.
This property will render the stroke behind the fill area. This will require to use opaque fill colors.
In the example above, I've set overflow:visible to illustrate the effect.

  • Related