Search code examples
csssvgsvg-animate

Shine/Sheen effect on hover for SVG?


I'm new to SVG animation and I was hoping someone could help me achieve an effect.

I have two questions actually: 1.) As you can see in this codepen. I have an CSS keyframe animation that draws out a shape, fill a flat color then ends in a SVG gradient. However it doesn't properly transition to that gradient (it just kinda 'pops' to that state at the end)

CSS Animation

@-webkit-keyframes dash {
   0% {stroke-dashoffset: 900; fill:#FFF;}
   85% {stroke-dashoffset: 0; fill:#FFF;}
  90% {fill:#fce669;}
  100% {fill:url(#glow);}
}

SVG '#Glow' gradient

  <defs>
        <linearGradient id = "glow" x1 = "0%" y1 = "0%" x2 = "100%" y2 = "100%">
            <stop stop-color = "#fce669" offset = "0%"/>
            <stop stop-color = "#fff" offset = "100%"/>
        </linearGradient>
  </defs>

Is it possible to transition between these states?

Okay that aside, here's my real question:

2.) Ideally, after the drawing animation is complete, I'd like there to be a sheen effect that is also triggered on hover. Like this codepen. I tried animating the color stops on the gradient to achieve this effect and it's pretty terrible. http://codepen.io/StuffieStephie/pen/eJdWeJ

I really have no idea how to proceed with this. Can someone point me in the right direction?


Solution

  • You seem to came in some hard place here...

    I am not sure about the specs recommendations for this, but from tests, it seems that svg's linearGradient can't be animated through CSS. I guess that it's because it is seen as an image, just like CSS gradients are seen as background-image.

    Note that Firefox won't even execute the animation if you place a <funcIRI> into it (url(#yourElement)). Once again, I couldn't find the specs about this, so can't tell for sure if that's a bug, but since Chrome is deprecating SMIL, that will probably become an issue quite soon.

    But for today, the only workaround I can give you, is to use SMIL.

    You can use <animate> elements to animate your linearGradient. In the example above, I do animate only one stop-color of the #glow gradient.

    I also used an other animated linear-gradient to the shine effect. This linear-gradient is then applied as the fill attribute of a mask, making the black part of the gradient the only masking of the <mask> element.
    Note that chrome only do render offset's animation if you do use absolute values...

    svg {
      max-width: 100%;
      max-height: 100%;
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      margin: auto;
    }
    .st0 {
      stroke-dasharray: 900;
      stroke-dashoffset: 0;
      fill: url(#glow);
      animation: dash 4s linear alternate;
      -webkit-animation: dash 4s linear alternate;
    }
    @keyframes dash {
      0% {
        stroke-dashoffset: 900;
      }
      85% {
        stroke-dashoffset: 0;
      }
    }
    @-webkit-keyframes dash {
      0% {
        stroke-dashoffset: 900;
      }
      85% {
        stroke-dashoffset: 0;
      }
    }
    .st2 {
      fill: #FFF;
    }
    <svg version="1.1" id="starmoon" class="icon" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 254.7 220.8" style="enable-background:new 0 0 254.7 220.8;" xml:space="preserve">
      <style type="text/css">
        .st0 {
          stroke: #EFA441;
          stroke-miterlimit: 10;
        }
      </style>
      <defs>
        <linearGradient id="glow" x1="0%" y1="0%" x2="100%" y2="100%">
          <stop stop-color="#FFF" offset="0%">
            <animate id="color" attributeName="stop-color" from="#fff" to="#fce669" dur="0.8s" begin="3.2s" fill="freeze" />
          </stop>
          <stop stop-color="#fff" offset="100%" />
        </linearGradient>
    
        <!-- the gradient used by the mask -->
        <linearGradient id="shine" x1="0%" y1="0%" x2="100%" y2="100%">
          <stop stop-color="white" offset="-10%">
            <animate attributeName="offset" values="-.05;.95" dur="2s" begin="color.end" />
          </stop>
          <stop stop-color="black" offset="-5%">
            <animate attributeName="offset" values="0;1" dur="2s" begin="color.end" />
          </stop>
          <stop stop-color="white" offset="0%">
            <animate attributeName="offset" values=".05;1.05" dur="2s" begin="color.end" />
          </stop>
        </linearGradient>
    
    
        <mask id="myShiningMask">
          <rect x="0" y="0" width="100%" height="100%" fill="url(#shine)" />
        </mask>
    
      </defs>
      <path fill="url(#glow)" class="st0 icon" d="M169,69.1l5.4-5.4c1.6-1.6,0.8-4.3-1.5-4.7l-42.6-7.6c-0.9-0.2-1.6-0.7-2-1.5l-19.7-38.4c-1-2-3.9-2-5,0
    	L83.5,49.4c-0.4,0.8-1.2,1.3-2,1.4l-42.7,6.5c-2.2,0.3-3.2,3.1-1.6,4.7L67.3,93c0.6,0.6,0.9,1.5,0.8,2.4l-6.7,42.3
    	c-0.4,2.2,1.9,3.9,4,3l38.9-18.7c0.8-0.4,1.7-0.4,2.5,0l38.6,19.7c2,1,4.4-0.6,4-2.9l-6.1-42.5c-0.1-0.9,0.2-1.8,0.8-2.4l15.2-15
    	c10,9.6,16.2,23.1,16.2,38.1c0,29.3-23.7,53-53,53c-10,0-19.3-2.8-27.3-7.6c10.8,21.4,33,36.1,58.6,36.1c36.2,0,65.6-29.4,65.6-65.6
    	C219.1,102,197.7,76.1,169,69.1z" mask="url(#myShiningMask)" />
    
      <circle class="st2" cx="87.9" cy="67.3" r="12.4" fill="white" />
    
    </svg>