Skip to: Content or Footer

Four rotating circles with play and pause button

Introduction

I wanted to create four rotating elements around a tight circumference where the text contained within remained static i.e. not rotating like its parent. Seemed easy enough, took a bit of CSS wrangling, then I wanted to add a Play/Pause button. The example below is what I came up with.

Circle 1
Circle 2
Circle 3
Circle 4
You have set a preference for reduced motion in your System Settings so no animation will be shown.

Accessibility, prefers Reduced Motion

What if the user has set a preference for Reduced Motion? In this case the animation and play button are removed. There is also some warning text indicating that the animation will not work. Give it a try, set prefers-reduced-motion in your System Settings (Accessibility > Display > Reduce Motion) and see what happens here…

Read through code or download

You can read through the various code sections inline here or skip to Download Link to play with the code locally.

Code: HTML

<div class="circles-container">
  <div class="outer-circle">
    <div class="animation1 rotate" data-animation>
      <div class="counter-rotate1" data-animation>
        <div class="inner">Circle 1</div>
      </div>
    </div>
    <div class="animation2 rotate" data-animation>
      <div class="counter-rotate2" data-animation>
        <div class="inner">Circle 2</div>
      </div>
    </div>
    <div class="animation3 rotate" data-animation>
      <div class="counter-rotate3" data-animation>
        <div class="inner">Circle 3</div>
      </div>
    </div>
    <div class="animation4 rotate" data-animation>
      <div class="counter-rotate4" data-animation>
        <div class="inner">Circle 4</div>
      </div>
    </div>
  </div>
  <div id="buttons">
    <button id="pause-toggle" class="">Pause</button>
  </div>
  <div id="prefers-reduced-motion">
    You have set a preference for reduced motion in your System Settings so no animation will be shown.
  </div>
</div>

Code: Cascading Style Sheets (CSS)

body {
  background-color: #eaeaea;
}

#buttons {
  position: absolute;
  top: 240px;
  left: 50%;
  transform: translate(-50%, 0);
}

#pause-toggle:hover {
  cursor: pointer;
}

#pause-toggle {
  background-color: #333;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #fff;
  font-size: 1.25rem;
  width: 150px;
  padding: 10px 20px;
  border: 0;
  border-radius: 6px;
}

#prefers-reduced-motion {
  display: none;
}

.circles-container {
  position: relative;
  top: 40px;
  margin: 0 auto;
}

.outer-circle {
  background-color: #add8e6;
  position: absolute;
  left: 50%;
  transform: translate(-50%, 0);
  width: 200px;
  height: 200px;
  border-radius: 50%;
}

.rotate {
  width: 100%;
  height: 100%;
  position: absolute;
}

.counter-rotate1,
.counter-rotate2,
.counter-rotate3,
.counter-rotate4 {
  width: 100px;
  height: 100px;
}

.inner {
  background-color: #ed7883;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #000;
  width: 100px;
  height: 100px;
  border-radius: 50%;
}
.animation1 {
  animation-name: circle1;
}
.animation1 .counter-rotate1 {
  animation-name: counter-rotate-circle1;
}
.animation2 {
  animation-name: circle2;
}
.animation2 .counter-rotate2 {
  animation-name: counter-rotate-circle2;
}
.animation3 {
  animation-name: circle3;
}
.animation3 .counter-rotate3 {
  animation-name: counter-rotate-circle3;
}
.animation4 {
  animation-name: circle4;
}
.animation4 .counter-rotate4 {
  animation-name: counter-rotate-circle4;
}

.animation1,
.animation1 .counter-rotate1,
.animation2,
.animation2 .counter-rotate2,
.animation3,
.animation3 .counter-rotate3,
.animation4,
.animation4 .counter-rotate4 {
  animation-duration: 45s;
  animation-iteration-count: infinite;
  animation-timing-function: linear;
}

@keyframes circle1 {
  from {
    transform: rotateZ(0deg);
  }
  to {
    transform: rotateZ(360deg);
  }
}
@keyframes counter-rotate-circle1 {
  from {
    transform: rotateZ(0deg);
  }
  to {
    transform: rotateZ(-360deg);
  }
}
@keyframes circle2 {
  from {
    transform: rotateZ(90deg);
  }
  to {
    transform: rotateZ(450deg);
  }
}
@keyframes counter-rotate-circle2 {
  from {
    transform: rotateZ(-90deg);
  }
  to {
    transform: rotateZ(-450deg);
  }
}
@keyframes circle3 {
  from {
    transform: rotateZ(180deg);
  }
  to {
    transform: rotateZ(540deg);
  }
}
@keyframes counter-rotate-circle3 {
  from {
    transform: rotateZ(-180deg);
  }
  to {
    transform: rotateZ(-540deg);
  }
}
@keyframes circle4 {
  from {
    transform: rotateZ(270deg);
  }
  to {
    transform: rotateZ(630deg);
  }
}
@keyframes counter-rotate-circle4 {
  from {
    transform: rotateZ(-270deg);
  }
  to {
    transform: rotateZ(-630deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  #buttons {
    display: none;
  }

  #prefers-reduced-motion {
    display: block;
    position: absolute;
    top: 240px;
    left: 50%;
    text-align: center;
    max-width: 280px;
    transform: translate(-50%, 0);
  }

  .animation1,
  .animation1 .counter-rotate1,
  .animation2,
  .animation2 .counter-rotate2,
  .animation3,
  .animation3 .counter-rotate3,
  .animation4,
  .animation4 .counter-rotate4 {
    position: relative;
    animation: none;
  }

  .animation1 {
    left: 50px;
    top: -20px;
  }
  .animation2 {
    left: 120px;
    top: -150px;
  }
  .animation3 {
    top: -280px;
    left: 50px;
  }
  .animation4 {
    top: -550px;
    left: -20px;
  }
}

Code: JavaScript

const pauseToggle = document.getElementById('pause-toggle');
pauseToggle.addEventListener('click', () => {
	const animations = document.querySelectorAll('[data-animation]');
	animations.forEach(animation => {
		const running = animation.style.animationPlayState || 'running';
		animation.style.animationPlayState = running === 'running' ? 'paused' : 'running';
		pauseToggle.innerHTML = running === 'running' ? 'Play' : 'Pause';
	});
});

Download sample code

I’ve assembled all the parts so that you can play with them. The standard – software is provided “as is”, without warranty of any kind – applies here.

Download File
Zip Files Info
index.html
1.8KB
script.js
468 bytes
style.css
4.5KB
readme.txt
460 bytes
File: barrd-4-rotating-circles.zip
Details: 4 files @ 5.8KB

That’s all folks

Have fun playing with the code, if you improve upon it or have any other ideas please get in touch.

// End of Project

More Information

Dave Barr

Bristol based Scottish Expat who has 20+ years experience of Web Development and is continually on the look out to improve his skill sets. Learning new and innovative solutions for current requirements in the world of IT, WebDev and eCommerce.

About Dave Barr

Image for Dave Barr
Bristol based Scottish Expat who has 20+ years experience of Web Development and is continually on the look out to improve his skill sets. Learning new and innovative solutions for current requirements in the world of IT, WebDev and eCommerce.

Read more about Dave

Back to Top

Click to Copy