Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Beginning and pausing SVG animations on hover

I would like to animate the gears on the following SVG when the user hovers over it. That is, when the mouse enters, both gears begin rotating where they left off. When the mouse leaves, the gears stop in whatever position they're in. If possible I would like the animation to begin and end using an ease-in/out function. How can this be done using SVG animations?

Codepen

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="256" height="256" viewBox="0 0 256 256">

    <path d="M249.363 80.921l-6.402-15.451c-1.769-4.267-6.659-6.292-10.927-4.528l-9.032 3.7 c-8.043-12.801-18.893-23.646-31.692-31.689l3.744-9.032c1.768-4.266-0.258-9.162-4.528-10.926l-15.45-6.402 c-4.264-1.764-9.16 0.261-10.923 4.523l-3.744 9.032c-14.458-3.293-29.542-3.478-44.817 0l-3.74-9.028 c-1.768-4.267-6.659-6.292-10.926-4.528l-15.451 6.402c-4.267 1.768-6.296 6.656-4.528 10.926l3.744 9 C71.893 41 61 51.9 53 64.687l-9.032-3.744c-4.263-1.764-9.158 0.261-10.927 4.528l-6.398 15.5 c-1.768 4.3 0.3 9.2 4.5 10.926l9.028 3.74c-3.349 14.666-3.435 29.8 0 44.816l-9.028 3.7 c-4.271 1.768-6.296 6.664-4.528 10.926l6.398 15.451c1.772 4.3 6.7 6.3 10.9 4.524l9.032-3.741 c8.044 12.8 18.9 23.6 31.7 31.693l-3.74 9.032c-1.768 4.3 0.3 9.2 4.5 10.927l15.451 6.4 c4.267 1.8 9.158-0.257 10.926-4.528l3.74-9.028c14.613 3.3 29.7 3.4 44.8 0l3.739 9 c1.768 4.3 6.7 6.3 10.9 4.528l15.45-6.402c4.267-1.768 6.292-6.663 4.524-10.927l-3.744-9.032 c12.8-8.048 23.649-18.896 31.692-31.693l9.032 3.741c4.268 1.8 9.158-0.254 10.927-4.524l6.398-15.451 c1.768-4.262-0.257-9.162-4.524-10.922l-9.032-3.739c3.328-14.583 3.458-29.682 0-44.825l9.032-3.74 C249.103 90.1 251.1 85.2 249.4 80.921z M138 176.536c-32.278 0-58.537-26.259-58.537-58.536 c0-32.281 26.259-58.536 58.537-58.536c32.276 0 58.5 26.3 58.5 58.536C196.535 150.3 170.3 176.5 138 176.536z">
        <animateTransform
            attributeName="transform"
            repeatCount="indefinite"
            type="rotate"
            from="00 138 118"
            to="+360 138 118"
            begin="0s" dur="30s"/>
    </path>

    <path d="M57.552 217.745l-2.16 0.895c0.811 3.6 0.8 7.2 0 10.721l2.16 0.895c1.021 0.4 1.5 1.6 1.1 2.612l-1.53 3.7 c-0.423 1.021-1.593 1.506-2.613 1.082l-2.16-0.895c-1.924 3.061-4.519 5.655-7.58 7.58l0.895 2.2 c0.423 1.02-0.062 2.19-1.082 2.613l-3.695 1.531c-1.021 0.422-2.191-0.063-2.614-1.083l-0.895-2.16 c-3.508 0.801-7.12 0.821-10.719 0l-0.895 2.159c-0.423 1.021-1.593 1.506-2.613 1.083l-3.695-1.53 c-1.021-0.423-1.505-1.594-1.082-2.613l0.895-2.16c-3.062-1.924-5.656-4.519-7.581-7.58l-2.16 0.9 c-1.021 0.424-2.189-0.061-2.613-1.082l-1.53-3.695c-0.423-1.02 0.062-2.19 1.083-2.613l2.159-0.895 c-0.801-3.507-0.821-7.121 0-10.719l-2.159-0.895c-1.021-0.424-1.506-1.594-1.083-2.613l1.53-3.696 c0.423-1.021 1.594-1.505 2.613-1.083l2.161 0.896c1.924-3.062 4.519-5.655 7.58-7.579l-0.896-2.16 c-0.423-1.021 0.063-2.19 1.083-2.613l3.695-1.531c1.021-0.422 2.2 0.1 2.6 1.083l0.895 2.159c3.427-0.78 7.035-0.839 10.7 0 l0.895-2.16c0.422-1.02 1.593-1.504 2.612-1.082l3.695 1.531c1.021 0.4 1.5 1.6 1.1 2.613l-0.895 2.2 c3.061 1.9 5.7 4.5 7.6 7.579l2.161-0.896c1.021-0.422 2.2 0.1 2.6 1.083l1.531 3.7 C59.057 216.2 58.6 217.3 57.6 217.745z M46 224c0-7.721-6.28-14-14-14s-14 6.279-14 14c0 7.7 6.3 14 14 14 S46 231.7 46 224z">
        <animateTransform
            attributeName="transform"
            repeatCount="indefinite"
            type="rotate"
            from="00 32 224"
            to="-360 32 224"
            begin="0s" dur="20s"/>
    </path>

</svg>
like image 916
Dave Avatar asked Dec 05 '22 05:12

Dave


2 Answers

Assuming the cogs have id big and little, here's a way to do this using css animations:

#big {
    transform-origin: 138px 118px;
    animation-duration: 30s;
    animation-name: rotateBig;
    animation-fill-mode: forwards;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    animation-play-state: paused;
}
#little {
    transform-origin: 32px 224px;
    animation-duration: 20s;
    animation-name: rotateLittle;
    animation-fill-mode: forwards;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    animation-play-state: paused;
}
#big:hover, #little:hover {
    animation-play-state: running;
}
@keyframes rotateBig {
    to {
        transform: rotate(360deg);
    }
}
@keyframes rotateLittle {
    to {
        transform: rotate(-360deg);
    }
}

See live example. This animates each of the gears individually when hovered.

For something that animates both gears when the whole svg is hovered:

#big {
    transform-origin: 138px 118px;
    animation-duration: 30s;
    animation-name: rotateBig;
    animation-fill-mode: forwards;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}
#little {
    transform-origin: 32px 224px;
    animation-duration: 20s;
    animation-name: rotateLittle;
    animation-fill-mode: forwards;
    animation-timing-function: linear;
    animation-iteration-count: infinite;        
}
svg > * { 
    animation-play-state: paused; 
}
svg:hover > * {
    animation-play-state: running;
}
@keyframes rotateBig {
    to {
        transform: rotate(360deg);
    }
}
@keyframes rotateLittle {
    to {
        transform: rotate(-360deg);
    }
}

See live example. The only difference is what selectors set animation-play-state.

You may have to resort to using css vendor prefixes to get this to be more compatible, but I've provided it in the cleanest form I could. The fiddle works fine in Opera 25, Chrome 38 and Firefox Nightly 34 at least.

A slight downside with the solution here is that the begin and end isn't using ease-in-out, this is because that makes the repetitions visible. If anyone has a clever idea for how to fix that without using script I'm all ears.

like image 85
Erik Dahlström Avatar answered Dec 30 '22 06:12

Erik Dahlström


You are almost there. Add this tweaks:

  • Add id="gear1" to first <path...>
  • Add id="gear2" to second <path...>
  • End both <path... with this onmouseover=runGears(); onmouseout=stopGears(); >
  • remove <animate .....> from both <path....>

On the JS magic to rotate the gears, use this code:

var animateGears ; // event handler
var angle = 0; // rotation of angle

var gear1 = document.getElementById("gear1");
var gear2 = document.getElementById("gear2");

function runGears() {
animateGears = setInterval( function () {
        angle += 5;
        if (angle == 360 ) { angle = 0 } ; // 360 == 0 degrees

        gear1.setAttribute("transform", "rotate(" + angle + " 138 118)");
        gear2.setAttribute("transform", "rotate(" + angle + " 32 224)");
    }, 100);
}

function stopGears() {
    clearInterval(animateGears);
}

Note: play around with the Timeout of 100ms and Angle of +5 to make it look smoother.

Update: The other JS coding that may work for you (without removing the <animate....):-

var svgDoc =  document.getElementsByTagName('svg');

svgdoc[0].pauseAnimations(); // pause animation at load time

function runGears() {
 svgDoc[0].unpauseAnimations(); // resumes ALL animations
}
function stopGears() {
 svgDoc[0].pauseAnimations(); // pause ALL animations
}
like image 45
Alvin K. Avatar answered Dec 30 '22 07:12

Alvin K.