It's interesting comparing how it came out in each of the browsers:
- Safari felt like it was really struggling to handle it,
- Chrome was drawing the circles a bit choppy due to the floating point positions,
- Firefox was really nice and smooth, and
- Opera was good but not quite as good as Firefox.
Demo
Markup
<div id="working-container"> <div class="working"> <div class="ball"></div> <div class="ball"></div> <div class="ball"></div> <div class="ball"></div> <div class="ball"></div> </div> </div>
The
.working
needs to be added after the .ball
elements are loaded otherwise the timing will be wrong.CSS
I took out all the vendor-prefixes for simplicity, the SASS below has them included.
#working-container { position:relative; background-color:#2d2d2d; width:300px; height:200px; margin:20px auto; } .working { position:absolute; top:50%; left:50%; } .working .ball { position: absolute; background-color: #0D76BD; width: 5px; height: 5px; border-radius: 2.5px; opacity: 0; animation-name: working; animation-duration: 4.6s; animation-iteration-count: infinite; animation-timing-function: linear; } .working .ball:nth-child(1) { animation-delay: 0s; } .working .ball:nth-child(2) { animation-delay: 0.4s; } .working .ball:nth-child(3) { animation-delay: 0.8s; } .working .ball:nth-child(4) { -moz-animation-delay: 1.2s; } .working .ball:nth-child(5) { animation-delay: 1.6s; } @keyframes working { 0% { left: -85px; opacity: 0; } 3.261% { left: -55px; } 6.522% { left: -35px; opacity: 1; } 9.783% { left: -20px; } 32.609% { left: 0px; } 55.435% { left: 20px; } 58.696% { left: 35px; opacity: 1; } 61.957% { left: 55px; } 65.217%, 100% { left: 85px; opacity: 0; } }
SASS
$number-of-balls:5; $ball-delay:0.4; $duration:3s; $gap:($number-of-balls - 1) * $ball-delay; $duration-percentage:$duration / ($duration + $gap); #working-container { position:relative; background-color:#2d2d2d; width:300px; height:200px; margin:20px auto; } .working { position:absolute; top:50%; left:50%; } .working .ball { position:absolute; background-color:#0D76BD; width:5px; height:5px; border-radius:2.5px; opacity:0; -moz-animation-name: working; -moz-animation-duration: $duration + $gap; -moz-animation-iteration-count: infinite; -moz-animation-timing-function: linear; -o-animation-name: working; -o-animation-duration: $duration + $gap; -o-animation-iteration-count: infinite; -o-animation-timing-function: linear; -webkit-animation-name: working; -webkit-animation-duration: $duration + $gap; -webkit-animation-iteration-count: infinite; -webkit-animation-timing-function: linear; } @mixin initial-delay($n) { $delay:unquote(($n - 1) * $ball-delay + 's'); -moz-animation-delay:$delay; -o-animation-delay:$delay; -webkit-animation-delay:$delay; } .working .ball:nth-child(1) { @include initial-delay(1); } .working .ball:nth-child(2) { @include initial-delay(2); } .working .ball:nth-child(3) { @include initial-delay(3); } .working .ball:nth-child(4) { @include initial-delay(4); } .working .ball:nth-child(5) { @include initial-delay(5); } @-moz-keyframes working { 0% { left:-85px; opacity:0; } #{5 * $duration-percentage + '%'} { left:-55px; } #{10 * $duration-percentage + '%'} { left:-35px; opacity:1; } #{15 * $duration-percentage + '%'} { left:-20px; } #{50 * $duration-percentage + '%'} { left:0px; } #{85 * $duration-percentage + '%'} { left:20px; } #{90 * $duration-percentage + '%'} { left:35px; opacity:1; } #{95 * $duration-percentage + '%'} { left:55px; } #{100 * $duration-percentage + '%'}, 100% { left:85px; opacity:0; } } @-o-keyframes working { 0% { left:-85px; opacity:0; } #{5 * $duration-percentage + '%'} { left:-55px; } #{10 * $duration-percentage + '%'} { left:-35px; opacity:1; } #{15 * $duration-percentage + '%'} { left:-20px; } #{50 * $duration-percentage + '%'} { left:0px; } #{85 * $duration-percentage + '%'} { left:20px; } #{90 * $duration-percentage + '%'} { left:35px; opacity:1; } #{95 * $duration-percentage + '%'} { left:55px; } #{100 * $duration-percentage + '%'}, 100% { left:85px; opacity:0; } } @-webkit-keyframes working { 0% { left:-85px; opacity:0; } #{5 * $duration-percentage + '%'} { left:-55px; } #{10 * $duration-percentage + '%'} { left:-35px; opacity:1; } #{15 * $duration-percentage + '%'} { left:-20px; } #{50 * $duration-percentage + '%'} { left:0px; } #{85 * $duration-percentage + '%'} { left:20px; } #{90 * $duration-percentage + '%'} { left:35px; opacity:1; } #{95 * $duration-percentage + '%'} { left:55px; } #{100 * $duration-percentage + '%'}, 100% { left:85px; opacity:0; } }