Search code examples
cssperformancecss-animationscss-transforms

CSS, switch left/right to transform/translate for better performance


I found this good CSS animation for a progress bar.

But I want to transform: translate instead of left/right.

How can I switch to transform it? I tried but it doesn't work:

https://codepen.io/shalimano/pen/wBmNGJ

body{
  background:#ffffff;
  margin:50px 300px;
}

.slider{
  position:absolute;
  width:1000px;
  height:5px;
  overflow-x: hidden;
}

.line{
  position:absolute;
  opacity: 0.4;
  background:#4a8df8;
  width:150%;
  height:5px;
}

.subline{
  position:absolute;
  background:#4a8df8;
  height:5px; 
}
.inc{
animation: increase 2s infinite;
}
.dec{
animation: decrease 2s 0.5s infinite;
}

@keyframes increase {
 from { left: -5%; width: 5%; }
 to { left: 130%; width: 100%;}
}
@keyframes decrease {
 from { left: -80%; width: 80%; }
 to { left: 110%; width: 10%;}
}
<div class="slider">
	  <div class="line"></div>
  <div class="subline inc"></div>
  <div class="subline dec"></div>
</div>

First attempt:

@keyframes increase {
 from { transform: translateX(-5%); width: 5%; }
 to { transform: translateX(130%); width: 100%;}
}
@keyframes decrease {
 from { transform: translateX(-100%); width: 80%; }
 to { transform: translateX(1100%); width: 10%;}
}

But it's not the same.


Solution

  • You should consider the fact that percentage value with translate are related to the element size and not parent size like top/left and in order to have good performance you need to also replace the width by scale so that you only use transform and you will not trigger layout changes.

    Here is an approximation of your code using only transform:

    body {
      background: #ffffff;
      margin: 50px 10px;
      position:relative;
    }
    
    .slider {
      position: absolute;
      left:0;
      right:0;
      height: 5px;
      overflow-x: hidden;
      background: rgba(74, 141, 248, 0.4);
    }
    
    .slider:before,
    .slider:after {
      content: "";
      position: absolute;
      background: #4a8df8;
      height: 5px;
      width:100%;
      transform:scaleX(0);
      animation: increase 2s infinite linear;
    }
    
    .slider:after {
      animation-delay:1s;
    }
    
    @keyframes increase {
      from {
        transform:translateX(0%) scaleX(0);
        transform-origin:left;
      }
      50% {
        transform-origin:left;
      }
      60% {
        transform:translateX(0%) scaleX(0.5);
        transform-origin:right;
      }
      80% {
        transform:translateX(20%) scaleX(0.3);
        transform-origin:right;
      }
      100% {
        transform:translateX(0%) scaleX(0);
        transform-origin:right;
      }
    }
    <div class="slider">
    </div>