admin管理员组

文章数量:1401947

I'm building a Gauge chart in a presentational ponent in React.

I just need to pass it a percentage and let the ponent do the rest. I can't use any animations because I'm taking a screenshot of the ponent to place the image in a Powerpoint presentation.

Here's a screenshot of what I'm trying to do:

As you can see in my code snippet, the circle <marker> is being positioned at the end of the grey <path> instead of at the end of the green <path>. How could I position the circle so it sits at the stroke-linecap of the green <path> as in the image above?

Here's the HTML code I have so far:

<div style="width:400px">
  <svg viewBox="0 0 100 100" xmlns="">
    <defs>
      <marker
        id="dot"
        viewBox="0 0 10 10"
        refX="4"
        refY="4"
        markerWidth="4"
        markerHeight="4"
      >
        <circle
          cx="4"
          cy="4"
          r="2"
          fill="#FFFFFF"
          stroke="#008000"
          stroke-width="2"
        />
      </marker>
    </defs>
    <path d="M20,60a35,35 0 1,1 60,0" stroke="#D3D7DB" stroke-width="4" fill="none" stroke-linecap="round"></path>
    <path d="M20,60a35,35 0 1,1 60,0" stroke="#008000" stroke-width="6" pathLength="100" fill="none" stroke-linecap="round" stroke-dasharray="75 35" marker-end="url(#dot)"></path>
  </svg>
</div>

I'm building a Gauge chart in a presentational ponent in React.

I just need to pass it a percentage and let the ponent do the rest. I can't use any animations because I'm taking a screenshot of the ponent to place the image in a Powerpoint presentation.

Here's a screenshot of what I'm trying to do:

As you can see in my code snippet, the circle <marker> is being positioned at the end of the grey <path> instead of at the end of the green <path>. How could I position the circle so it sits at the stroke-linecap of the green <path> as in the image above?

Here's the HTML code I have so far:

<div style="width:400px">
  <svg viewBox="0 0 100 100" xmlns="http://www.w3/2000/svg">
    <defs>
      <marker
        id="dot"
        viewBox="0 0 10 10"
        refX="4"
        refY="4"
        markerWidth="4"
        markerHeight="4"
      >
        <circle
          cx="4"
          cy="4"
          r="2"
          fill="#FFFFFF"
          stroke="#008000"
          stroke-width="2"
        />
      </marker>
    </defs>
    <path d="M20,60a35,35 0 1,1 60,0" stroke="#D3D7DB" stroke-width="4" fill="none" stroke-linecap="round"></path>
    <path d="M20,60a35,35 0 1,1 60,0" stroke="#008000" stroke-width="6" pathLength="100" fill="none" stroke-linecap="round" stroke-dasharray="75 35" marker-end="url(#dot)"></path>
  </svg>
</div>

Share Improve this question edited Jun 22, 2021 at 0:47 Ezra Free asked Jun 18, 2021 at 21:15 Ezra FreeEzra Free 75412 silver badges21 bronze badges 2
  • 2 I'm on my phone, so just quick hint. If you put the small circle in <g> node and set the translate right, you can then wrap it in another which sets the rotation correctly. Still, you need some script to alter the rotation depending on the percentage. – Tomáš Zato Commented Jun 18, 2021 at 23:43
  • 2 A better way to do this would be to use a path with an arc mand and a marker. Markers auto-orient by default, so if the marker is not a symmetric shape, you don't have to calculate your own rotation transform. – Michael Mullany Commented Jun 19, 2021 at 14:57
Add a ment  | 

2 Answers 2

Reset to default 4

You can do it all in SVG by setting pathLength and using animationMotion to position the circle.

Some JavaScript and a W3C standard Web Component (JSWC supported in all modern Browsers) help in putting multiple gauges on screen and making them dynamic.

customElements.define("svg-gauge", class extends HTMLElement {
  connectedCallback() {
    let speed = 0.5; // set to 0.0001 for instant display!
    let arc = "M20,60a35,35 0 1,1 60,0";
    let col = this.getAttribute("color") || "green";
    this.innerHTML = 
    `<input type="range" min="0" max="100" step="5" value="0"`+ // delete 2 lines
    ` oninput="this.parentNode.percent=this.value" /><br>`+ // just for demo
    `<svg viewbox="0 0 100 100">
       <path d="${arc}" stroke="grey" stroke-width="6" fill="none" stroke-linecap="round"></path>
       <path id="P" d="${arc}" stroke="${col}" stroke-width="8" pathLength="100" fill="none" stroke-linecap="round" stroke-dasharray="75 35"/>
       <circle stroke="${col}" cx="0" cy="0" fill="#fff" r="4" stroke-width="3">
         <animateMotion dur="${speed}s" path="${arc}" keyPoints="0;0.75" fill="freeze" keyTimes="0;1" calcMode="linear"/>
       </circle>
       <text x="50%" y="40%" text-anchor="middle">XXX</text>
     </svg>`;
    this.style.display='inline-block';
    this.percent = this.getAttribute("value") || "50";
  }
  set percent(val = 0) {
    this.setAttribute("value", val);
    let dash = val + " " + (105 - val);
    this.querySelector("#P").setAttribute('stroke-dasharray', dash);
    this.querySelector("animateMotion").setAttribute('keyPoints', '0;'+val/100);
    this.querySelector("text").innerHTML =`${val} %`;
    this.querySelector("animateMotion").beginElement();
    this.querySelector("input").value = val;
  }
})
drag sliders
<svg-gauge value="35" color="red"   ></svg-gauge>
<svg-gauge value="50" color="orange"></svg-gauge>
<svg-gauge value="75" color="green" ></svg-gauge>

I went ahead and accepted Danny '365CSI' Engelman's answer above, but just in case anyone wants to do this without the animations here is how I ended up implementing it:

<div style="width:400px">
  <svg viewBox="0 -10 100 100" xmlns="http://www.w3/2000/svg">
    <path d="M20,60a35,35 0 1,1 60,0" stroke="#D3D7DB" stroke-width="4" fill="none" stroke-linecap="round"></path>
    <path d="M20,60a35,35 0 1,1 60,0" stroke="#008000" stroke-width="6" pathLength="100" fill="none" stroke-linecap="round" stroke-dasharray="50 85"></path>
    <circle
          cx="0"
          cy="0"
          r="6"
          stroke-width="6"
          fill="#FFFFFF"
          stroke="#008000"
        >
          <animateMotion
            begin="0s"
            dur="infinite"
            repeatCount="infinite"
            keyPoints="0.5;0.5"
            fill="freeze"
            keyTimes="0;1"
            calcMode="linear"
            path="M20,60a35,35 0 1,1 60,0"
          ></animateMotion>
        </circle>
  </svg>
</div>

本文标签: javascriptHow to position an SVG circle along another circle39s pathStack Overflow