1

I have the SVG circle chart which has a gradient stroke. My problem is that I can not figure out how to do the gradient invisible from the start with opacity 0 and make it opacity 1 at the end. What do I need to get is at the image bellow.

enter image description here

What do I have until now is in my snippet bellow

/*graf animacie*/
.circle-chart__circle {
  animation: circle-chart-fill 2s reverse; /* 1 */ 
  transform: rotate(-90deg); /* 2, 3 */
  transform-origin: center; /* 4 */
}
.circle-chart__circle--negative {
  transform: rotate(-90deg) scale(1,-1); /* 1, 2, 3 */
}

.circle-chart__info {
  animation: circle-chart-appear 2s forwards;
  opacity: 0;
  transform: translateY(0.3em);
}

@keyframes circle-chart-fill {
  to { stroke-dasharray: 0 100; }
}

@keyframes circle-chart-appear {
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.grid {
  display: grid;
  grid-column-gap: 1em;
  grid-row-gap: 1em;
  grid-template-columns: repeat(1, 1fr);
}

@media (min-width: 31em) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
<div style="margin: auto; display: contents; text-align: center;">
<section>
    <svg class="circle-chart" viewbox="0 0 33.83098862 33.83098862" width="150" height="200" xmlns="http://www.w3.org/2000/svg">
      <circle class="circle-chart__background" stroke="#efefef" stroke-width="2" fill="none" cx="16.91549431" cy="16.91549431" r="15.91549431" />
      <circle class="circle-chart__background" stroke="white" stroke-width="2" fill="none" cx="16.91549431" cy="16.91549431" r="15.91549431" />
      <circle class="circle-chart__circle circle-chart__circle--negative" stroke="url(#gradient)" stroke-width="2" stroke-dasharray="90,100" stroke-linecap="" fill="none" cx="16.91549431" cy="16.91549431" r="15.91549431" />
      <g class="circle-chart__info">
        <text class="circle-chart__percent" x="16.91549431" y="15.5" alignment-baseline="central" text-anchor="middle" font-size="8">DEMO</text>
      </g>
        <defs>
           <linearGradient id="gradient" x1="1" y1="1" x2="0" y2="0">
       <stop offset="0.01" stop-color="#fdfafa"></stop>
        <stop  offset="0.20" style="stop-color:#e5b4b6"/>
        <stop  offset="0.80" style="stop-color:#c2545a"/>
       <stop offset="1" stop-color="#AE1515"></stop>
    </linearGradient>
   
        </defs>
    </svg>
  </section>
</div>

Thanks in advance for your help.

Lubo Masura
  • 1,034
  • 6
  • 20

1 Answers1

2

What you need here is a "conic gradient". Unfortunately SVG doesn't support conic gradients natively. They are supported in CSS, but you can't yet apply CSS gradients to SVG elements.

Option 1 (Not really an option :)

You could apply a conic gradient to an HTML element, and then mask it with an SVG mask to produce the final effect. Unfortunately masking HTML elements with SVG masks, currently only works in Firefox.

DEMO (Firefox only)

.chart-container {
  /* positioned parent is required in order for us to stack the children on top of each other */
  position: relative;
}

.chart-container .conic {
  width: 200px;
  height: 200px;
  background: conic-gradient(rgba(176, 37, 44, 1) 34deg, rgba(176, 37, 44, 0) 326deg);
  mask: url(#div-mask);
}

.chart-container svg {
  width: 200px;
  height: 200px;
  position: absolute;
  top: 0;
  left: 0;
}

.div-mask-path {
  stroke-dasharray: 100 100;
  animation: circle-chart-fill 2s forwards;
}

@keyframes circle-chart-fill {
  from { stroke-dashoffset: 100; }
  to { stroke-dashoffset: 0; }
}
<div class="chart-container">
  <div class="conic"/>
  
  <svg viewBox="0 0 100 100">
    <mask id="div-mask" maskContentUnits="objectBoundingBox">
      <path class="div-mask-path"
            d="M 0.2706,0.1723
               A 0.40,0.40 0 0 0 0.5,0.90
               A 0.40,0.40 0 0 0 0.7294,0.1723"
              fill="none" stroke="white" stroke-width="0.12"
              pathLength="100"/>
    </mask>
  </svg>
</div>

Option 2

If you know what your page background colour is going to be. You can use a similar technique as above, but instead of masking the HTML element, you cover it with an SVG element with a hole cut in it.

Demo (should work in all browsers)

.chart-container {
  /* positioned parent is required in order for us to stack the children on top of each other */
  position: relative;
}

.chart-container .conic {
  width: 200px;
  height: 200px;
  background: conic-gradient(rgba(176, 37, 44, 1) 34deg, rgba(176, 37, 44, 0) 326deg);
}

.chart-container svg {
  width: 200px;
  height: 200px;
  position: absolute;
  top: 0;
  left: 0;
}

.div-mask-path {
  stroke-dasharray: 2.025 2.025;
  animation: circle-chart-fill 2s forwards;
}

@keyframes circle-chart-fill {
  from { stroke-dashoffset: 2.025; }
  to { stroke-dashoffset: 0; }
}
<div class="chart-container">
  <div class="conic"/>
  
  <svg viewBox="0 0 100 100">
    <defs>
      <mask id="div-mask" maskContentUnits="objectBoundingBox">
        <!-- mask is mostly solid (white parts) -->
        <rect width="1" height="1" fill="white"/>
        <!-- but has a hole cut in it (black path) -->
        <path class="div-mask-path"
              d="M 0.2706,0.1723
                 A 0.40,0.40 0 0 0 0.5,0.90
                 A 0.40,0.40 0 0 0 0.7294,0.1723"
                fill="none" stroke="black" stroke-width="0.12"/>
      </mask>
    </defs>

    <!-- use a white rectangle (matches page background)
         to cover the <div> with the conic gradient -->
    <rect width="100" height="100" fill="white" mask="url(#div-mask)"/>
  </svg>
</div>

Option 3

Instead of using an <div> element with a conic gradient. Create a bitmap image that contains the conic gradient; include it in the SVG; mask that image instead.

Demo (should work in all browsers)

Note that I've messed up here and created a solid white-to-red PNG here, rather than a transparent-to-red one as you wanted. That's just a mistake on my part. I can't be bothered re-creating it now. I'll leave that to you :)

svg {
  width: 200px;
}

.div-mask-path {
  stroke-dasharray: 2.025 2.025;
  animation: circle-chart-fill 2s forwards;
}

@keyframes circle-chart-fill {
  from { stroke-dashoffset: 2.025; }
  to { stroke-dashoffset: 0; }
}
<svg viewBox="0 0 100 100">
  <defs>
    <mask id="div-mask" maskContentUnits="objectBoundingBox">
      <path class="div-mask-path"
            d="M 0.2706,0.1723
               A 0.40,0.40 0 0 0 0.5,0.90
               A 0.40,0.40 0 0 0 0.7294,0.1723"
              fill="none" stroke="white" stroke-width="0.12"/>
    </mask>
  </defs>

  <image xlink:href="https://i.imgur.com/cqhjI45.png"
         width="100" height="100"
         mask="url(#div-mask)"/>
</svg>

Option 4

Create the gradient by aligning a sequence of SVG elements in a circle. Start with a transparent one and interpolate the colours around to the end point. Then mask that group of elements using the same technique as Option 3. This will work, but will result in a larger SVG because you'll be adding up to 256 extra elements to create that gradient sequence.

(Not demoed here)

Paul LeBeau
  • 97,474
  • 9
  • 154
  • 181
  • 1
    Wow amazing demonstrations! Thank you very much for the explain and everything! I used the option n. 2 and it was exactly what I needed! Thank you again Paul. – Lubo Masura Jul 30 '21 at 16:19