Beginning and pausing SVG animations on hover

Dave picture Dave · Aug 6, 2014 · Viewed 15.6k times · Source

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>

Answer

Erik Dahlstr&#246;m picture Erik Dahlström · Aug 6, 2014

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.