Home > front end >  Change SVG paths to be clipped according to clipPath element
Change SVG paths to be clipped according to clipPath element

Time:12-17

I have a SVG that looks like this:

<svg id="Layer_1" data-name="Layer 1"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 576 576">
  <defs>
    <style>...</style>
    <clipPath id="clip-path">
      <rect  x="0.02" width="575.04" height="576"/>
    </clipPath>
  </defs>
  <g >
    <path  d="M137.91-147.28c-4-1.67-8.25-3.46-12.37-3.86-5.43-.53-9.26,1.73-12.55,5a18.75,18.75,0,0,0-4.69-9.42,19.23,19.23,0,0,0-6.45-...
    <path  d="M.08,502.59c-.79-5.67-6.22-4.3-5.81-.22a17.15,17.15,0,0,1,0,2.95c-.22,2.82-1.46,7.6-5,7.61-1.35,0-2.61-1-3.12...
    ...

I want to change it such that:

  • there is no grouping (this is easy)
  • change the path, rect, etc. elements such that they are clipped according to the what's in the clipPath element. The clipPath should no longer be present in the SVG (because it isn't needed anymore)

I've tried this:

inkscape --actions \
 "select-all:groups; SelectionUnGroup; ObjectUnSetClipPath; export-filename: output.svg; export-plain-svg; export-do;" \
 Decorations.svg

This removes the grouping, but the path elements are not clipped. The clipPath is still present.

CodePudding user response:

ObjectUnSetClipPath will remove the clipping attribute clip-path="url(#clip-path)" – not the <def> clipping path itself.

But you can't clip any element, if your clipPath definition is stripped.

function stripClip(el){
  let clipPaths = document.querySelector(el).querySelectorAll('clipPath');
  if(clipPaths.length){
    clipPaths.forEach(function(item, i){
      item.remove();
    }
    )
  }
}
svg{
width: 20vw;
}

.clipped{
clip-path: url(#clip-path2);
}
  <p><button onclick="stripClip('#svg1')" >remove ClipPath</button></p>
  
<svg id="svg1" data-name="Layer 1"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36">
  <defs>
    <style>...</style>
    <clipPath id="clip-path">
      <rect  x="10" width="36" height="18"/>
    </clipPath>
  </defs>
  <g >
   <path clip-path="url(#clip-path)" d="M18 2.0845
  a 15.9155 15.916 0 0 1 0 31.83
  a 15.916 15.916 0 0 1 0 -31.83z" fill="red" />
  <rect  clip-path="url(#clip-path)" x="0" y="0" width="10" height="25" />
  </g>
  </svg>
  
  
  <svg id="svg2" data-name="Layer 1"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36">
  <defs>
    <style>...</style>
    <clipPath id="clip-path2">
      <rect  x="10" width="36" height="18"/>
    </clipPath>
  </defs>
  <g >
   <path  d="M18 2.0845
  a 15.9155 15.916 0 0 1 0 31.83
  a 15.916 15.916 0 0 1 0 -31.83z" fill="red" />
  <rect  x="0" y="0" width="10" height="25" />
  </g>
  </svg>
  

**Edit:** Get svg intersection paths using paper.js

Admittedly, not the most convenient approach but it is possible:
In addition to paper.js, we also need some script/helper to convert svg shapes (circles, rects etc.) to <path> elements (I'm using 'pathThatSvg' , since paper.js can only calculate new intersection paths based on 2 path elements like so:

var intersectionPath = path1.intersect(clipPath);

Based on this answer

var svg = document.querySelector("#svgIntersect");

// set auto ids for processing
function setAutoIDs(svg) {
  var svgtEls = svg.querySelectorAll(
    "path, polygon, rect, circle, line, text, g"
  );
  svgtEls.forEach(function (el, i) {
    if (!el.getAttribute("id")) {
      el.id = el.nodeName   "-"   i;
    }
  });
}
setAutoIDs(svg);

// convert shapes to paths
function shapesToPath(svg) {  
        pathThatSvg(svg.outerHTML).then((converted) => {
          var tmp = document.createElement("div");
          tmp.innerHTML = converted;
          svg.innerHTML = tmp.querySelector("svg").innerHTML;
        });
}
shapesToPath(svg);


function intersectPath(svg, decimals=2) {
  // init paper.js and add canvas
  canvas = document.createElement('canvas');
  canvas.id = "canvasPaper";
  canvas.setAttribute('style','display:none')
  document.body.appendChild(canvas);
  paper.setup("canvasPaper");

  // process clipped elements
  var all = paper.project.importSVG(svg, function (item, i) {
    item.position = new paper.Point(
      item.bounds.width / 2,
      item.bounds.height / 2
    );
    //item.scale(0.5, new Point(0, 0) )
    var items = item.getItems();
    var ids = item._namedChildren;

    var groups = item.children;
    groups.forEach(function (gr, i) {
      var group = gr["_namedChildren"];
      if (group) {
        for (key in group) {
          //get clip path
          var clip = group["clipPath"][0];
          if (key !== "clipPath") {
            var el = group[key][0];
             //get intersection path and generate d commands
            var elClipped = el.intersect(clip);
            var elClippedD = elClipped
              .exportSVG({ precision: decimals })
              .getAttribute("d");
            // select path by id and overwrite d attribute
            var newEl = svg.querySelector("#"   key);
            newEl.setAttribute("d", elClippedD);
          }
        }
      }
    });

    // remove clip defs and attributes
    var clippedEls = svg.querySelectorAll("[clip-path]");
    clippedEls.forEach(function (clippedEl, e) {
      clippedEl.removeAttribute("clip-path");
    });
    svg.querySelector("defs").remove();
    svg.classList.add("svg-intersect");
    console.log(svg.outerHTML)
  });
}
svg{
  border: 1px solid #ccc;
  display:inline-block;
  width:200px;
}

.svg-intersect path{
  stroke:red;
  stroke-width: 0.25;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.12.0/paper-full.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/pathThatSvg.umd.min.js"></script>
<p>
  <button type="button" onclick="intersectPath(svg)">get Path Intersect</button>
</p>

<svg id="svgIntersect" viewBox="0 0 100 100">
  <defs>
    <clipPath id="clipPath">
      <circle cx="25" cy="25" r="25"/>
    </clipPath>
  </defs>
  <g id="clipGroup" clip-path="url(#clipPath)">
    <circle fill="#999" data-id="circle" cx="25" cy="25" r="25" />
    <rect fill="#555" id="rect" x="25" y="25" width="50" height="50" />
  </g>
  
  <g  clip-path="url(#clipPath)">
    <circle fill="#444" id="circle2" cx="66" cy="25" r="25" />
    <rect fill="#22" id="rect2" x="15" y="12.5" width="20" height="75" />
  </g>
</svg>

  • Related