Search code examples
csscss-animationscss-transforms

Cleaning up repeatable CSS codes


I got this code working from another thread here. However, I feel like as though I'm not re-using the CSS efficiently. How can I cut/reduce the CSS code further more?

It feels like I'm repeating the keyframes way too much. Also, I'm not sure why loop is used but it doesn't work properly when I remove it.

PS: In the demo, you see only ten, thirty and ninety only. I'll be adding twenty, forty, fifty etc in the actual production code.

Demo: https://jsfiddle.net/dg0L9cw7/1/

#speedometer {
  display: inline-block;
  position: relative;
}

#speedometer .barometer {
  background-image: url("https://svgshare.com/i/GAZ.svg");
  background-repeat: no-repeat;
  width: 200px;
  height: 110px;
  display: inline-block;
}

#speedometer .needle {
  background-image: url("https://svgshare.com/i/GBP.svg");
  background-repeat: no-repeat;
  z-index: 999999;
  width: 200px;
  height: 110px;
  display: inline-block;
  left: 0px;
  position: absolute;
  bottom: 5px;
  transform-origin: 50% calc(100% - 8px);
}

#speedometer .needle.ten {
  animation: changeTen 3s linear, loopTen 1s linear 3s infinite alternate;
}

@keyframes changeTen {
  0% {
    transform: rotate(-90deg);
  }
  100% {
    transform: rotate(-60deg);
  }
}

@keyframes loopTen {
  0% {
    transform: rotate(-60deg);
  }
  100% {
    transform: rotate(-60deg);
  }
}

#speedometer .needle.thirty {
  animation: changeThirty 3s linear, loopThirty 1s linear 3s infinite alternate;
}

@keyframes changeThirty {
  0% {
    transform: rotate(-90deg);
  }
  100% {
    transform: rotate(-30deg);
  }
}

@keyframes loopThirty {
  0% {
    transform: rotate(-30deg);
  }
  100% {
    transform: rotate(-30deg);
  }
}

#speedometer .needle.ninety {
  animation: changeNinety 3s linear, loopNinety 1s linear 3s infinite alternate;
}

@keyframes changeNinety {
  0% {
    transform: rotate(-90deg);
  }
  100% {
    transform: rotate(80deg);
  }
}

@keyframes loopNinety {
  0% {
    transform: rotate(80deg);
  }
  100% {
    transform: rotate(80deg);
  }
}
<div id="speedometer">
  <span class="barometer"></span>
  <span class="needle ten"></span>
</div>
<div id="speedometer">
  <span class="barometer"></span>
  <span class="needle thirty"></span>
</div>
<div id="speedometer">
  <span class="barometer"></span>
  <span class="needle ninety"></span>
</div>


Solution

  • Use CSS variables. You can also reduce the markup to only one element:

    .speedometer {
      display: inline-block;
      background-image: url("https://svgshare.com/i/GAZ.svg");
      width: 200px;
      overflow:hidden;
    }
    
    
    .speedometer:before {
      content:"";
      background-image: url("https://svgshare.com/i/GBP.svg");
      height: 110px;
      display: block;
      margin-bottom: 5px;
      transform-origin:50% calc(100% - 8px) ;
      animation: change 3s linear forwards;
      backface-visibility: hidden;
    }
    
    @keyframes change {
      0% { transform:rotate(-90deg); }
      100% { transform:rotate(var(--r,90deg)); }
    }
    <div class="speedometer" style="--r:-60deg;">
    </div>
    <div class="speedometer" style="--r:-30deg;">
    </div>
    <div class="speedometer" style="--r:80deg;">
    </div>

    You can also make the variable more friendly using some calculation. In the below I am considering percetange so values in the range [0 100]

    .speedometer {
      display: inline-block;
      background-image: url("https://svgshare.com/i/GAZ.svg");
      width: 200px;
      overflow:hidden;
    }
    
    
    .speedometer:before {
      content:"";
      background-image: url("https://svgshare.com/i/GBP.svg");
      height: 110px;
      display: block;
      margin-bottom: 5px;
      transform-origin:50% calc(100% - 8px) ;
      animation: change 3s linear forwards;
      backface-visibility: hidden;
    }
    
    @keyframes change {
      0% { transform:rotate(-90deg); }
      100% { transform:rotate(calc(1.8deg*var(--p) - 90deg)); }
    }
    <div class="speedometer" style="--p:40;">
    </div>
    <div class="speedometer" style="--p:20;">
    </div>
    <div class="speedometer" style="--p:80;">
    </div>