Position dynamic shapes at same position

324 Views Asked by At

When I click on button each time it gives a different shape.All the shapes are dynamic. It can be polygon or circle (hundreds of shapes).

The shape is formed by group of lines.

The issue here is each shape gets positioned at different place and get scaled smaller then the other.I want them to be normalized. All the shapes should have proper scaling effect and positioned at same x position. Some of the shapes come in center , some go a bit towards top left.

Code with same transform matrix and scale is shown below.Shapes get positioned differently.May be the problem is coordinates of line.In the first code snippet it starts at (0,0) and last shapes line start at (15,5)

Can I give the group g positioned at same position for all shapes.Should I place relative to something?

var draw = SVG('drawing').viewbox(0, 0, 400, 400).attr("preserveAspectRatio", "xMidYMid meet");
var group = draw.group().translate(90, 90).scale(3)
var obj = {
    "type": "Polygon",
    "coords": [
        [
            [0, 0],
            [30, 0],
            [30, 20],
            [60, 20],
            [60, 40],
            [0, 40],
            [0, 0]
        ],
        [
            [0, 0],
            [10, 50],
            [50, 10],
            [0, 0]
        ],
        [
            [0, 0],
            [60, 0],
            [80, 40],
            [0, 40],
            [0, 0]
        ],
        [
            [0, 0],
            [50, 0],
            [50, 20],
            [0, 20],
            [0, 0]
        ],
        [
            [50, 10],
            [40, 40],
            [20, 40],
            [10, 20],
            [50, 10]
        ],
        [
            [15, 5],
            [40, 10],
            [10, 20],
            [5, 10],
            [15, 5]
        ],
        [
            [20, 35],
            [10, 30],
            [10, 10],
            [30, 5],
            [45, 20],
            [20, 35]
        ]
    ]
};

shapehandler()
function shapehandler() {
    if (obj.coords.length) {
        group.clear();
        drawShape(obj.coords[0]);
        obj.coords.shift();

    }
}


function drawShape(coords) {
    var lineCoords = [];
    var pointListString = coords.toString();
    var pointListArray = JSON.parse("[" + pointListString + "]");
    for (let i = 0; i < pointListArray.length - 2; i += 2) {
        let [x1, y1, x2, y2] = pointListArray.slice(i, i + 4)
        lineCoords.push({
            x1,
            y1,
            x2,
            y2
        });
    }

    lineCoords.forEach(function (lin, i) {
        let colorShade = [
            '#FFDC0B',
            '#002438',
            '#9B56BB',
            '#c6c7e2',
            '#318700',
            '#fe854f',
            '#FF8400',
            '#31d0c6',
            '#7c68fc',
            '#61549C',
            '#6a6c8a',
            '#4b4a67',
            '#3c4260',
            '#33334e',
            '#222138'
        ];
        group.line(lin.x1, lin.y1, lin.x2, lin.y2).stroke({ color: colorShade[i], width: 4, linecap: 'square' });
    });

}
html, body {
    margin: 0;
    padding: 0;
    font-family: Arial;
}
svg {
    width: 100%;
    height: 100%;
  }
#drawing{
    margin: 20px;
    display: inline-block;
    position: relative;
    border: 1px solid darkgrey;
    overflow:hidden;
    box-sizing: border-box;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.6/svg.js"></script>
 <div id="toolbar">
    <button type="button" id="btn-show-shape" onclick="shapehandler()">Show Polygon Shapes</button>
  </div>
  <div id="drawing">
  </div>

1

There are 1 best solutions below

0
enxaneta On

I'm keeping your HTML.

The CSS is yours but I've added a rule for lines. It sets the stroke-width. For a nicer effect the stroke-linecap is round.

The most important: I'm using vector-effect:non-scaling-stroke. Since I don't know how much I will scale the group, I need vector-effect:non-scaling-stroke that causes an object's stroke-width to be unaffected by transformations and zooming.

In the javaScript I'm keeping all your data (points and colors), but I've rewrite the rest.

The most important thing; I'm calculating the scale which depends on the group's bounding box width. I'm translating the group in the center of the SVG canvas and scaling it.

I hope this is what you need.

const SVG_NS = 'http://www.w3.org/2000/svg';
const W = 400,cx = W/2;
const H = 400,cy = H/2;

let obj = {
    type: "polygon",
    coords: [
        [
            [0, 0],
            [30, 0],
            [30, 20],
            [60, 20],
            [60, 40],
            [0, 40],
            [0, 0]
        ],
        [
            [0, 0],
            [10, 50],
            [50, 10],
            [0, 0]
        ],
        [
            [0, 0],
            [60, 0],
            [80, 40],
            [0, 40],
            [0, 0]
        ],
        [
            [0, 0],
            [50, 0],
            [50, 20],
            [0, 20],
            [0, 0]
        ],
        [
            [50, 10],
            [40, 40],
            [20, 40],
            [10, 20],
            [50, 10]
        ],
        [
            [15, 5],
            [40, 10],
            [10, 20],
            [5, 10],
            [15, 5]
        ],
        [
            [20, 35],
            [10, 30],
            [10, 10],
            [30, 5],
            [45, 20],
            [20, 35]
        ]
    ]
};

let colorShade = [
            '#FFDC0B',
            '#002438',
            '#9B56BB',
            '#c6c7e2',
            '#318700',
            '#fe854f',
            '#FF8400',
            '#31d0c6',
            '#7c68fc',
            '#61549C',
            '#6a6c8a',
            '#4b4a67',
            '#3c4260',
            '#33334e',
            '#222138'
        ];


// create a new SVG element
let svg = drawSVGElmt(
"svg",
{
viewbox:`0 0 ${W} ${H}`,
preserveAspectRatio:"xMidYMid meet",
}, 
drawing);

// create a group element
let group = drawSVGElmt(
"g",
{/*transform:"translate(90,90) scale(3)"*/},
svg
)


// draw a red dot in the middle of the canvas
drawSVGElmt("circle",{cx:cx,cy:cy,r:5,fill:"red"},svg)


let n = 0;



function drawAndScale(n){
  //first remove all the lines from the group
  while (group.firstChild) {
    group.removeChild(group.firstChild);
  }
  
  // for all the points in obj.coords[n] draw a line
  for(let i = 0, l = obj.coords[n].length -1; i < l; i++){
    
  let p0 = obj.coords[n][i];
  let p1 =  obj.coords[n][i + 1];
  let x1 = p0[0],y1 = p0[1];
  let x2 = p1[0],y2 = p1[1];
  let stroke = colorShade[i];
    
  drawSVGElmt("line", {x1:x1,y1:y1,x2:x2,y2:y2,stroke:stroke}, group)
}

//get the size of the bounding box of the group
let BB = group.getBBox();

// set the scate using the width of the bounding box
let scale = (W * .75) / BB.width;
  
// scale & translate the group in the crnter of the SVG element
group.setAttributeNS(null,"transform",
`translate(${ scale * (-BB.x -BB.width/2)}, ${ scale * (-BB.y -BB.height/2)}) 
scale(${scale})
translate(${cx/scale}, ${cy/scale})`); 
}



drawAndScale(n);

function shapehandler(){
  if(n < obj.coords.length - 2){ n++; }else{n = 0};
  drawAndScale(n);
}
       


// a function to draw an SVG element
function drawSVGElmt(type, o, _parent) {
  let elmt = document.createElementNS(SVG_NS, type);
  for (var name in o) {
    if (o.hasOwnProperty(name)) {
      elmt.setAttributeNS(null, name, o[name]);
    }
  }
  _parent.appendChild(elmt);
  return elmt;
}
html, body {
    margin: 0;
    padding: 0;
    font-family: Arial;
}
svg {
    width: 100%;
    height: 100%;
  }
#drawing{
    margin: 20px;
    display: inline-block;
    position: relative;
    border: 1px solid;
    overflow:hidden;
    box-sizing: border-box;
    width:400px;
    height:400px;
}


line{
  stroke-width:50px;
  stroke-linecap: round;
  /* vector-effect:non-scaling-stroke causes an object's stroke-width to be unaffected by transformations and zooming */
  vector-effect:non-scaling-stroke;}
<div id="toolbar">
<button type="button" id="btn_show_shape" onclick="shapehandler()">Show Polygon Shapes</button>
</div>
<div id="drawing">
</div>