Search code examples
javascriptsvgformula

Formula for <rect>dynamic spacing in svg


How to make dynamic spacing base on the no. of returned data? I want that if only one data is returned, it is positioned in middle. If two data returned it will adjust, and so on.

   var prev = {
      "Prev": [{
          "type": "Prev1",
        },
        {
          "type": "Prev2",
        },
        {
          "type": "Prev3",
        },
       
      ]
    };
    
    var draw = document.getElementById('drawing');
    
    prev.Prev.forEach((p, i, arr) => {
      let x = 55;
      let y = 250/arr.length*i+50;
      draw.innerHTML += `<g transform="translate(${x} ${y})">
            <rect x="-50" y="-20" width="100" height="40" rx="10" />
            <text dominant-baseline="middle" text-anchor="middle" width="100">${p.type}</text>
            <line x1="50" y1="0" x2="100" y2="${250/arr.length-y+50}" /> 
            </g>`;
    });
    
        draw.innerHTML += `<g>
            <rect x="150" y="110" width="100" height="40" rx="10" />
            <text  x="200" y="130" dominant-baseline="middle" text-anchor="middle" width="100">BASE</text>
            </g>`;
        line, rect {stroke-width: 1; stroke: navy; fill: none;}
        text {fill: navy;}
      
 <svg id="drawing" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">


    </svg>

 


Solution

  • It's pretty simple. You just have to calculate the top of the first rect and then add on i times the distance from the top of one item to the top of the next.

    var prev = {
      "Prev": [{
          "type": "Prev1",
        },
        {
          "type": "Prev2",
        },
        {
          "type": "Prev3",
        }
       
      ]
    };
    
    var draw = document.getElementById('drawing');
    var RECT_HEIGHT = 40;
    var RECT_SPACING = 50;
    var SVG_HEIGHT = 300;
    
    prev.Prev.forEach((p, i, arr) => {
      // number of rects/graph items
      let num = arr.length;
      // total height of all <num> 
      let totalHeight = num * RECT_HEIGHT + (num - 1) * RECT_SPACING;
      let x = 55;
      // final position = position of first rect + i * distance_between_rects
      let y = (SVG_HEIGHT - totalHeight) / 2   // top of first rect
              + i * (RECT_HEIGHT + RECT_SPACING);  // i * distance_between_rects
    
      draw.innerHTML += `<g transform="translate(${x} ${y})">
            <rect x="-50" y="0" width="100" height="40" rx="10" />
            <text y="20" dominant-baseline="middle" text-anchor="middle"
                  width="100">${p.type}</text>
            <line x1="50" y1="20" x2="100" y2="${SVG_HEIGHT / 2 - y}" /> 
            </g>`;
    });
    
    /*
        draw.innerHTML += `<g>
            <rect x="150" y="110" width="100" height="40" rx="10" />
            <text  x="200" y="130" dominant-baseline="middle" text-anchor="middle" width="100">BASE</text>
            </g>`;
    */
    svg {
      background: linen;
    }
    <svg id="drawing" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
      <style>
        line, rect {stroke-width: 1; stroke: navy; fill: none;}
        text {fill: navy;}
      </style>
    </svg>