Search code examples
javascriptcssdomcss-transformscss-variables

How to rotate 4 children within a circle 90 degrees every 3 seconds using CSS custom properties and transform?


I'm trying to rotate 4 children (.feat) elements within a circle 90 degrees every 3 seconds using a CSS custom property called --angle, but it doesn't seem to work as expected:

function rotation() {
  let inner = document.querySelector('[inn]');
  let rota = inner.style.transform;
}

setInterval(rotation, 3000);
.feat-cont {
  width: 60vmax;
  height: 60vmax;
}

.feat-cont p {
  display: inline-block;
}

.inner {
  --angle: 0;
  
  position: relative;
  background-color: yellow;
  transform: rotate(var(--angle) * 1deg);
  width: 100%;
  height: 100%;
  border-radius: 50%;
}

.feat {
  position: absolute;
}

.feat1 {
  left: 25vmax;
}

.feat2 {
  left: 50vmax;
  top: 25vmax;
}

.feat3 {
  left: 25vmax;
  top: 50vmax;
}

.feat4 {
  top: 25vmax;
}
<div class='feat-cont'>
  <div class='inner' inn>
    <div class='feat feat1' f1>
      <img src="https://img.icons8.com/nolan/64/fast-forward.png"/>
      <p>Fast Performance</p>
    </div>

    <div class='feat feat2' f2>
      <img src="https://img.icons8.com/color/48/000000/memory-slot.png"/>
      <p>64 GBs of memory</p>
    </div>

    <div class='feat feat3' f3>
      <img src="https://img.icons8.com/nolan/64/camera.png"/>
      <p>3K MP camera</p>
    </div>

    <div class='feat feat4' f4>
      <img src="https://img.icons8.com/dusk/64/000000/branding.png"/>
      <p>Trusted brand</p>
    </div>
  </div>
</div>


Solution

  • You can change the angle CSS custom property using CSSStyleDeclaration.setProperty():

    circle.style.setProperty('--angle', `${ angle }deg`);
    

    Also, note you use it as transform: rotate(var(--angle)), not as rotate(var(--angle) * 1deg), so the unit should already be included in the CSS property.

    If you don't want to use CSS properties, you can change the style attribute directly:

    circle.style.transform = `rotate(${ angle }deg)`;
    

    However, I guess you need to rotate the circle in one direction while rotating all the children (.feat) in the opposite direction to keep them straight, so using CSS properties is probably more convenient, as otherwise, you would have to change the style attribute in all 4 children:

    const circle = document.querySelector('.circle');
    const prev = document.querySelector('.prev');
    const next = document.querySelector('.next');
    const step = 90;
    
    let angle = 0;
    let intervalID = null;
    
    function updateRotation() {
      circle.style.setProperty('--angle', `${ angle }deg`);
      circle.style.setProperty('--angle-inverse', `${ -angle }deg`);
    }
    
    function autoRotate() {
      intervalID = setInterval(() => {
        angle += step;  
        updateRotation();
      }, 3000);
    }
    
    prev.onclick = () => {
      clearInterval(intervalID);
      angle -= step;
      updateRotation();
      autoRotate();
    };
    
    next.onclick = () => {
      clearInterval(intervalID);
      angle += step;
      updateRotation();
      autoRotate();
    };
    
    autoRotate();
    body {
      font-family: monospace;
    }
    
    .container {
      width: 60vmax;
      height: 60vmax;
      margin: 0 auto;
      overflow: hidden;
    }
    
    .circle {
      --angle: 0;
      --angle-inverse: 0;
      
      position: relative;
      background-color: yellow;
      width: 100%;
      height: 100%;
      border-radius: 50%;
      transition: transform linear 1s;
      transform: rotate(var(--angle));
    }
    
    .feat {
      position: absolute;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      width: 20vmax;
      height: 20vmax;
      text-align: center;
      transition: transform linear 1s;
    }
    
    .feat > img {
      width: 10vmax;
      height: 10vmax;
    }
    
    .feat > p {
      margin: 8px 0 0;
    }
    
    .feat1 {
      top: 0;
      left: 50%;
      transform: translate(-50%, 0) rotate(var(--angle-inverse));
    }
    
    .feat2 {
      right: 0;
      top: 50%;
      transform: translate(0, -50%) rotate(var(--angle-inverse));
    }
    
    .feat3 {
      bottom: 0;
      left: 50%;
      transform: translate(-50%, 0) rotate(var(--angle-inverse));
    }
    
    .feat4 {
      left: 0;
      top: 50%;
      transform: translate(0, -50%) rotate(var(--angle-inverse));
    }
    
    .button {
      position: fixed;
      top: 0;
      bottom: 0;
      background: transparent;
      border: 0;
      padding: 32px;
      outline: none;
      font-family: monospace;
      font-size: 32px;
    }
    
    .button:hover {
      background: rgba(0, 0, 0, .0625);
    }
    
    .prev {
      left: 0;
    }
    
    .next {
      right: 0;
    }
    <div class="container">
      <button class="button prev">‹</button>
      
      <div class="circle">
        <div class="feat feat1">
          <img src="https://img.icons8.com/nolan/64/fast-forward.png"/>
          <p>Fast Performance</p>
        </div>
    
        <div class="feat feat2">
          <img src="https://img.icons8.com/color/48/000000/memory-slot.png"/>
          <p>64 GBs of memory</p>
        </div>
    
        <div class="feat feat3">
          <img src="https://img.icons8.com/nolan/64/camera.png"/>
          <p>3K MP camera</p>
        </div>
    
        <div class="feat feat4">
          <img src="https://img.icons8.com/dusk/64/000000/branding.png"/>
          <p>Trusted brand</p>
        </div>
      </div>
      
      <button class="button next">›</button>
    </div>