Water Ripple Animation with CSS
As part of my work volunteering at 48in48, I got to play around with CSS animations recently. For the Pebble Tossers website, I wanted to build an engaging animation that both reflected the spirit of the organization and enticed users to sign up as volunteers–so I added a water-ripple effect behind the call-to-action buttons.
Check out the effect below. When you hover over the animation, it pauses.
Building the animation
I created a SASS mixin so that the animation can be attached to any element. It accepts 3 parameters:
- A CSS selector to which the animation is attached
- The background color of the animation container
- The size of the ripple animation
The containing element for the animation can have children elements–the animation will also be attached to the first child.
Sample markup
<div class="ripple-container"> <a href="#" class="btn btn-default ripple-wrapper"> <span>Hover over me!</span> </a> </div>
Including the SASS mixin
@include ripples(".ripple-wrapper", $primaryBgColor, 75);
SASS mixin
@mixin ripples($rippleContainer, $rippleContainerBgColor, $rippleSize) { #{$rippleContainer} { position: relative; z-index: 0; &:before, &:after, > *:first-child:before, > *:first-child:after { content: ''; display: block; position: absolute; top: 25%; left: 50%; width: 2px; height: 2px; border-radius: 2px; animation: ripple 4s infinite; backface-visibility: hidden; } &:before { z-index: -4; margin-left: 5px; animation-delay: 0; } &:after { z-index: -2; margin-left: 8px; animation-delay:.5s; } > *:first-child:before { z-index: -3; margin-left: -3px; animation-delay:.3s; } > *:first-child:after { z-index: -1; margin-left: -8px; animation-delay: .8s; } &:hover:before, &:hover:after, > *:first-child:hover:before, > *:first-child:hover:after { animation-play-state: paused; } @keyframes ripple { 0% { box-shadow:0 0 0 0 transparent, 0 0 0 0 transparent, 0 0 0 0 transparent, 0 0 0 0 transparent; } 5% { box-shadow:0 0 0 0 $rippleContainerBgColor, 0 0 0 0 rgba(255,255,255,0.5), 0 0 0 0 $rippleContainerBgColor, 0 0 0 0 rgba(0,0,0,0.2); } 75% { box-shadow:0 0 ($rippleSize/2)+px $rippleSize+px $rippleContainerBgColor, 0 0 ($rippleSize/($rippleSize/10))+px ($rippleSize+($rippleSize/20))+px transparent, 0 0 ($rippleSize/3)+px ($rippleSize + ($rippleSize/20))+px $rippleContainerBgColor, 0 0 ($rippleSize/($rippleSize/($rippleSize/20)))+px ($rippleSize + ($rippleSize/8))+px transparent; } } } }
CSS styles
In case you’re not using SASS, here’s the CSS output from the mixin:
.ripple-wrapper { position: relative; z-index: 0; } .ripple-wrapper:before, .ripple-wrapper:after, .ripple-wrapper > *:first-child:before, .ripple-wrapper > *:first-child:after { content: ''; display: block; position: absolute; top: 25%; left: 50%; width: 2px; height: 2px; border-radius: 2px; -webkit-animation: ripple 4s infinite; animation: ripple 4s infinite; -webkit-backface-visibility: hidden; backface-visibility: hidden; } .ripple-wrapper:before { z-index: -4; margin-left: 5px; -webkit-animation-delay: 0; animation-delay: 0; } .ripple-wrapper:after { z-index: -2; margin-left: 8px; -webkit-animation-delay: .5s; animation-delay: .5s; } .ripple-wrapper > *:first-child:before { z-index: -3; margin-left: -3px; -webkit-animation-delay: .3s; animation-delay: .3s; } .ripple-wrapper > *:first-child:after { z-index: -1; margin-left: -8px; -webkit-animation-delay: .8s; animation-delay: .8s; } .ripple-wrapper:hover:before, .ripple-wrapper:hover:after, .ripple-wrapper > *:first-child:hover:before, .ripple-wrapper > *:first-child:hover:after { -webkit-animation-play-state: paused; animation-play-state: paused; } @-webkit-keyframes ripple { 0% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, 0 0 0 0 transparent, 0 0 0 0 transparent; } 5% { box-shadow: 0 0 0 0 #009cff, 0 0 0 0 rgba(255, 255, 255, 0.5), 0 0 0 0 #009cff, 0 0 0 0 rgba(0, 0, 0, 0.2); } 75% { box-shadow: 0 0 37.5px 75px #009cff, 0 0 10px 78.75px transparent, 0 0 25px 78.75px #009cff, 0 0 3.75px 84.375px transparent; } } @keyframes ripple { 0% { box-shadow: 0 0 0 0 transparent, 0 0 0 0 transparent, 0 0 0 0 transparent, 0 0 0 0 transparent; } 5% { box-shadow: 0 0 0 0 #009cff, 0 0 0 0 rgba(255, 255, 255, 0.5), 0 0 0 0 #009cff, 0 0 0 0 rgba(0, 0, 0, 0.2); } 75% { box-shadow: 0 0 37.5px 75px #009cff, 0 0 10px 78.75px transparent, 0 0 25px 78.75px #009cff, 0 0 3.75px 84.375px transparent; } }