DNAScanner
DNAScanner

Reputation: 74

Centering a SVG path

is there a way to center my <path> in an SVG-File?

This is my SVG: xml

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72">
    <rect width="100%" height="100%" fill="#444444" />
    <path d="M5.7 0L1.4 10.985V55.88h15.284V64h8.597l8.12-8.12h12.418l16.716-16.716V0H5.7zm51.104 36.3L47.25 45.85H31.967l-8.12 8.12v-8.12H10.952V5.73h45.85V36.3zM47.25 16.716v16.716h-5.73V16.716h5.73zm-15.284 0v16.716h-5.73V16.716h5.73z" fill="#6441A4" />
</svg>

Upvotes: 0

Views: 290

Answers (2)

herrstrietzel
herrstrietzel

Reputation: 17354

Another quick fix could be setting negative viewBox x/y values like so:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="-3.95  -3.95  72 72" >
 <rect x="-3.95" y="-3.95" width="100%" height="100%" fill="#444444" />        
 <path fill="#6441A4" 
          d="M5.7 0L1.4 10.985V55.88h15.284V64h8.597l8.12-8.12h12.418l16.716-16.716V0H5.7zm51.104 36.3L47.25 45.85H31.967l-8.12 8.12v-8.12H10.952V5.73h45.85V36.3zM47.25 16.716v16.716h-5.73V16.716h5.73zm-15.284 0v16.716h-5.73V16.716h5.73z"/>
</svg>

Slightly hacky, especially if you need a background element like a <rect>.

Centering and scaling by recalculating path coordinates

Most likely, a transform works perfectly fine and should be your first choice.
If you need your transformations to be "hard coded" – it's not too complicated:

let svg = document.querySelector(".svg");
let path = svg.querySelector("path");
let bBox = svg.getBBox();
let vBox = svg.getAttribute("viewBox");
let vBoxArr = vBox ? vBox.split(" ") : [0, 0, bBox.width, bBox.height];


scalePathProportional(path, 0.75);
centerPath(path, true, true);

function centerPath(path, centerX= true, centerY=true, precision=3, render=true){
  let svg = path.closest('svg');
  let viewBox = svg.getAttribute('viewBox');
  let bBox = svg.getBBox();
  viewBox = viewBox ? viewBox.split(' ') : ([bBox.x, bBox.y, bBox.width, bBox.height]);  
  let offXnorm = viewBox[0] * (-1);
  let offYnorm = viewBox[1] * (-1);
  
  // convert to relative to move only M
  let dRel = snapPathToRelative(path, 1);
  let pathBB = path.getBBox();
  let pX = pathBB["x"];
  let pY = pathBB["y"];
  // get x/y offsets to center path
  let shiftX = (viewBox[2] - pathBB.width) / 2  - pX ;
  let shiftY = (viewBox[3] - pathBB.height) / 2 - pY;
  // save them to pathData
  dRel[0][1] = (dRel[0][1] + shiftX).toFixed(precision) * 1;
  dRel[0][2] = (dRel[0][2] + shiftY).toFixed(precision) * 1;
  // apply change
  if(render){
      path.setAttribute('d',dRel.toString());
  }
  return dRel;
}

//scale to path to width and height by units or percentages
function scalePathProportional(path, scale=1) {
  let svg = path.closest("svg");
  let pathData = path.getPathData();
  pathData.forEach(function (command, p) {
    let coords = command.values;
    //scale coordinates if viewBox < 1000 units
    if (scale!==1) {
      coords.forEach(function (el, i) {
        coords[i] = coords[i] * scale;
      });
    }
  });
  path.setPathData(pathData);
  return pathData;
}


function snapPathToRelative(path, precicion=null, render=false){
  let d = path.getAttribute('d');
  let pathRel = Snap.path.toRelative(d);
  if(precicion){
    roundCoords(pathRel)
  }
  if(render){
      path.setAttribute('d', pathRel.toString());
  }
  return pathRel;
}

function roundCoords(commands, decimals=1){
commands.forEach(function (coordinates, i) {
        let coordRelative = [];
        coordinates.forEach(function (coordinate, v) {
            if (typeof coordinate === 'number') {
                coordinate = (coordinate).toFixed(decimals) * 1
            }
            coordRelative.push(coordinate);
        });
        commands[i] = coordRelative;
    });
    //console.log(commands)
    return commands;
}
svg {
  display: inline-block;
  height: 5em;
  font-size: 1em;
  border: 1px solid #ccc;
  background-color:#444;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/path-data-polyfill.min.js"></script>
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72">
    <rect width="100%" height="100%" fill="#444444" />
    <path d="M5.7 0L1.4 10.985V55.88h15.284V64h8.597l8.12-8.12h12.418l16.716-16.716V0H5.7zm51.104 36.3L47.25 45.85H31.967l-8.12 8.12v-8.12H10.952V5.73h45.85V36.3zM47.25 16.716v16.716h-5.73V16.716h5.73zm-15.284 0v16.716h-5.73V16.716h5.73z" fill="#6441A4" />
</svg>

Scaling d coordinates proportionally
Requires to loop through all path commands and multiply all coordinates by a scaling factor (like 0.75). To get the path data, I use Jarek Foksa's pathData polyfill

Centering paths
Involves some comparisons between the parent svg's viewBox and the actual path's boundaries (retrieved via path.getBBox()).
To actually shift the path horizontally and vertically by x/y offsets we can simplify the task by converting the path commands to relative coordinates (using snap.svg's toRelative(d).
Now we only need to change the M command's x/y coordinates (see also Lea Verou's post Convert SVG path to all-relative or all-absolute commands)

**Disclaimer: not intended for live usage **
This approach should be used for custom svg pre-optimizing. So run the script - re-save your svg asset.

Upvotes: 0

Michael Mullany
Michael Mullany

Reputation: 31805

Add a suitable transform. This seems fairly close:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72">
    <rect width="100%" height="100%" fill="#444444" />
    <path fill="#6441A4" 
          transform="translate(3.95 3.95)" 
          d="M5.7 0L1.4 10.985V55.88h15.284V64h8.597l8.12-8.12h12.418l16.716-16.716V0H5.7zm51.104 36.3L47.25 45.85H31.967l-8.12 8.12v-8.12H10.952V5.73h45.85V36.3zM47.25 16.716v16.716h-5.73V16.716h5.73zm-15.284 0v16.716h-5.73V16.716h5.73z"/>
</svg>

Upvotes: 1

Related Questions