Home > Blockchain >  How to make border of stroke SVG?
How to make border of stroke SVG?

Time:08-24

I am making gauge component using SVG. Two strokes are used to display two states at once. My current result is like this. enter image description here

enter image description here

And want to make like this. To block the end of the hatched part. enter image description here I don't know how to close this end... Can <filter> and :fill be used together?

 .circle-front {
      stroke-dasharray: 120;
      stroke: #333;
      stroke-width: 5.5px;
      transform: translate(2px, 5px) rotate(148deg);
      transform-origin: center;
    }

    .circle-back {
      stroke-dasharray: 120;
      stroke-dashoffset: 39;
      stroke: rgba(255, 255, 255, 0.2);
      transform: translate(2px, 5px) rotate(148deg);
      stroke-width: 5;
      transform-origin: center;   
    }

    .circle-oblique {
      stroke-dasharray: 120;
      /* stroke: #333; */
      stroke-linejoin: round;

      stroke-width: 4.5px;
      transform: translate(2px, 5px) rotate(148deg);
      transform-origin: center;
    }
<!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <svg
          width="110"
          height="110"
          viewBox="0 0 60 54"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <g filter="url(#filter0_d_711_531)">
            <circle cx="29.7727" cy="30.2273" r="19" className="circle-back" />
          </g>
          <circle
            cx="29.7727"
            cy="30.2273"
            r="19"
            stroke="url(#vertical-stripe-2)"
            className="circle-oblique"
            strokeDashoffset="90"
          />
          <defs>
            {" "}
            <pattern
              id="vertical-stripe-2"
              patternUnits="userSpaceOnUse"
              width="2"
              height="2"
            >
              {" "}
              <image
                xlinkHref="data:image/svg xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSd3aGl0ZScgLz4KICA8cmVjdCB4PScwJyB5PScwJyB3aWR0aD0nMicgaGVpZ2h0PScxMCcgZmlsbD0nYmxhY2snIC8 Cjwvc3ZnPg=="
                x="0"
                y="0"
                width="2"
                height="2"
              >
                {" "}
              </image>{" "}
            </pattern>{" "}
          </defs>
          <circle
            cx="29.7727"
            cy="30.2273"
            r="19"
            className="circle-front"
            strokeDashoffset="43"
          />
          <defs>
            <filter id="filter0_d_711_531">
              <feFlood floodOpacity="0" result="BackgroundImageFix" />
              <feColorMatrix
                in="SourceAlpha"
                type="matrix"
                values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 0"
              />
              <feOffset />
              <feGaussianBlur stdDeviation="0.3" />
              <feComposite in2="hardAlpha" operator="out" />
              <feColorMatrix
                type="matrix"
                values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0"
              />
            </filter>
          </defs>
        </svg>
    </body>
    </html>

CodePudding user response:

I don't like the use of filters if they are not really needed for a pixel-oriented operations: they are hard to understand and performance-heavy. All they seem to do in your case is draw a border, which is a vector operation. Here is a solution that draws paths.

For the same reason, this uses vector-based <pattern> content instead of an image.

(Your code example is neither executable nor self-explaining, so I am ignoring it.) From the example pictures you provided it seems the border is to surround the total area of the gauge, no matter what the displayed values are. That means it can be drawn once without any reference to dynamic data. Then the lines dividing the areas representing your values are just short perpendicular lines bisecting the gauge area. To get them dynamically in the correct position is matter of rotating them. The areas themselves are drawn separately, below the borders.

I have provided the dynamic data as CSS variables here, but it should be easy to insert them via some JS scripting, just replace the CSS properties that reference them with an appropriate way to insert values, probably in presentation attributes.

svg {
  height: 100vh;
  --val1: 0.4;
  --val2: 0.6;
}
symbol {
  overflow: visible;
}
.area {
  fill:none;
  stroke-width: 15;
}
.stroke {
  fill:none;
  stroke: #333;
  stroke-width: 2;
  stroke-linejoin: round;
}
#area-1 {
  stroke: #333;
  stroke-dasharray: calc(var(--val1) * 100px) 100px;
}
#limit-1 {
  transform: rotate(calc(45deg   var(--val1) * 270deg));
}
#area-2 {
  stroke: url(#hatch);
  stroke-dasharray: calc(var(--val2) * 100px) 100px;
}
#limit-2 {
  transform: rotate(calc(45deg   var(--val2) * 270deg));
}
<svg viewBox="-60 -60 120 120">
  <symbol id="limit">
    <path d="M 0,41 V 54" />
  </symbol>
  <pattern id="hatch" patternUnits="userSpaceOnUse" width="16" height="8">
    <path  d="M -2,-6 18,4 M -2,2 18,12 M -2,10 18,20" />
  </pattern>
  <!-- data areas in reverse order -->
  <path id="area-2"  d="M -33.588,33.587 A 47.5,47.5 0 1 1 33.588,33.588" pathLength="100" />
  <path id="area-1"  d="M -33.588,33.587 A 47.5,47.5 0 1 1 33.588,33.588" pathLength="100" />
  <g >
    <!-- static outer border -->
    <path d="M -38.184,38.184 A 54,54 0 1 1 38.184,38.184 L 28.991,28.991 A 41,41 0 1 0 -28.991,28.991 Z" />
    <!-- bisecting data lines, cited from a template -->
    <use id="limit-1" href="#limit" />
    <use id="limit-2" href="#limit" />
  </g>
</svg>

  • Related