Home > Blockchain >  Fill Shapes/Icon In SVG Path (js)
Fill Shapes/Icon In SVG Path (js)

Time:01-22

I have a SVG with path

<svg width="100" height="100" stroke="black" fill="transparent">
  <path d="M 10 10 H 90 V 90 H 10 L 10 10"/>
</svg>

SVG Image

and I have to fill some svg shapes / icons in the path with a gap. For example I have filled the svg path with the circle shape with a gap=2px

SVG Edited Image

is there any javascript function or can we do it with just SVGs and the gap between the circle should be customizable and it should work for all the svg with paths. (thanks in advance)

CodePudding user response:

Instead of filling you will need a special stroke. The stroke-dasharray is similar to the border-style in css. However it will create only dashed lines. In the following example I'm using stroke-dasharray="0.1, 9.9" meaning lines of 0.1 units and gaps of 9.9

To make them look like dotted I'm using stroke-linecap="round" and a thick line: stroke-width="6"

Please observe that line gap = 0.1 9.9 = 10 and the square side length is 80 (a multiple of 10) ensuring that you have a dot in every corner.

<svg width="100" height="100" stroke="black" fill="transparent">
  <path d="M 10 10 H 90 V 90 H 10 L 10 10" stroke-dasharray="0.1, 9.9" stroke-width="6" stroke-linecap="round" ></line>/>
</svg>

CodePudding user response:

You can use getTotalLength() to find the length of the path and getPointAtLength() to find points along the path where you place the elements.

In this example I append <use> elements to a <g>.

const p1 = document.getElementById('p1');
const p2 = document.getElementById('p2');
const c1 = document.getElementById('c1');
const c2 = document.getElementById('c2');

setIcons(p1, 'r1', c1, 10);
setIcons(p2, 'p3', c2, 10);

function setIcons(path, id, container, numb){
  let total = parseInt(path.getTotalLength());
  let gap = total / numb;
  
  [...Array(numb).keys()].map(i => {
    let point = path.getPointAtLength(i*gap);
    let use = document.createElementNS('http://www.w3.org/2000/svg','use');
    use.setAttribute('href', `#${id}`);
    use.setAttribute('transform', `translate(${point.x} ${point.y})`);
    container.appendChild(use);
  });
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 100" height="100" stroke="black" fill="transparent">
  <defs>
    <rect id="r1" x="-5" y="-5" width="10" height="10" fill="red" />
    <path id="p3" d="M -10 -12.5 A 5 5 90 0 0 10 -12.5 V 12.5 A 5 5 90 0 0 -10 12.5 Z" fill="red" />
  </defs>
  <path id="p1" d="M 10 10 H 90 V 90 H 10 L 10 10" />
  <g id="c1"></g>
  <g transform="translate(120 0)">
    <path id="p2" d="m 0 90 V 50 A 1 1 1 0 1 90 50 V 90" />
    <g id="c2"></g>
  </g>
</svg>

CodePudding user response:

Not using:

  • <marker>
  • getTotalLength / getPointAtLength

Using:

  • <defs> to define any (compound) SVG group "markN"
    global IDs in whole DOM page!
  • a native JS Web Component <svg-path-markers> to proces <path>
    customElements.define script can be executed anytime you want!
  • <path marker="mark1" markers="4"> attributes indicating which markN to use
  • a unique ID for each <path> to link it to an <mpath> (motionpath)
  • <animateMotion> to animate a "marker"
  • keyPoints="0,${dist}" to position the "marker"

Output:

With:

<svg-path-markers>
  <svg viewBox="0 0 100 100" style="background:pink;max-height:180px">
    <defs>
      <g id="mark1"><circle fill="red"    cx="0" cy="0" r="8" /></g>
      <g id="mark2"><rect   fill="green"  x="-4" y="-4" width="8" height="8" /></g>
      <g id="mark3"><circle fill="yellow" cx="0" cy="0" r="2" /></g>
    </defs>
    <style>path{ stroke:green ; fill:none }</style>
    <path marker="mark1" markers="4"  d="M 10 10 H 90 V 90 H 10 L 10 10" />
    <path marker="mark2" markers="8"  d="M 10 10 H 90 V 90 H 10 L 10 10" />
    <path marker="mark3" markers="16" d="M 10 10 H 90 V 90 H 10 L 10 10" />
  </svg>
</svg-path-markers>
<script>
  customElements.define("svg-path-markers", class extends HTMLElement {
    connectedCallback() {
      setTimeout(() => { // wait till innerHTML is parsed
        this.querySelectorAll("[marker]").forEach( path => {
          let duration = "2"; // set to 0.00001 for instant display
          let count = ~~path.getAttribute("markers") || 4;
          let id = path.id || (path.id = this.localName   Math.random()*1e9);
          let marker = dist => `<use href="#${path.getAttribute("marker")}">
                                <animateMotion dur="${duration}" fill="freeze"calcMode="linear"
                                               keyPoints="0;${dist}" keyTimes="0;1" >
                                <mpath href="#${id}"/></animateMotion></use>`;
          path.insertAdjacentHTML("afterend", Array(count).fill(0)
                                               .map((_,i) => marker(i*(1/count))).join(""));
        } )
      })}})
</script>

  • Related