Search code examples
cssdynamicspinnerloading

Dyanmic resize a CSS3 loading spinner by pixels


I've been asked to take a pure CSS3 loading spinner and make it dynamically resizable by pixels to use in different places in a program.

My current code is: (Which apparently doesn't run well in SO's snippets)

.loader {
  animation:spin 1s infinite linear;
  border:solid 2vmin transparent;
  border-radius:50%;
  border-right-color:#71c491;
  border-top-color:#f7941d;
  box-sizing:border-box;
  height:20vmin;
  left:calc(50% - 10vmin);
  position:fixed;
  top:calc(50% - 10vmin);
  width:20vmin;
  z-index:1;
  &:before {
    animation:spin 2s infinite linear;
    border:solid 2vmin transparent;
    border-radius:50%;
    border-right-color:#21409a;
    border-top-color:#92278f;
    box-sizing:border-box;
    content:"";
    height:16vmin;
    left:0;
    position:absolute;
    top:0;
    width:16vmin;
  }
  &:after {
    animation:spin 3s infinite linear;
    border:solid 2vmin transparent;
    border-radius:50%;
    border-right-color:#13b0e6;
    border-top-color:#18244c;
    box-sizing:border-box;
    content:"";
    height:12vmin;
    left:2vmin;
    position:absolute;
    top:2vmin;
    width:12vmin;
  }
}

@keyframes spin {
  100% {
    transform:rotate(360deg);
  }
}
<div class="loader"></div>

I googled and tried transform:scale() but as far as I can tell that only takes specific input and increases/decreases the size by percentage. (2 = 200% size)

I'm thinking I need some sort of wrapper, but I'm not too familiar with advanced CSS to get the effect. When I tried to create my own, only the top border of the spinner would be resized into a weird shape and not the inner borders. I'm just stumped. If you could point me in the right direction, I'd be appreciative. Thank you.


Solution

  • You could try a mix of CSS var() / calc() / clamp() / grid ... and relative/absolute positionning to lay the loader over the parent where you need it , if that inspire you :

    demo with a few loader within a div sized and the possibility to set an average size to start from, % size based on the width of the parent.

    value to reset in the demo is --size ; you may also tune the other --MyVarCss values to your needs.

    * {
      box-sizing: border-box;
    }
    
    :root { /* init for the var() values */
      --size: 20;/* value used to set the loader's width and adjust border's width */
      --width: calc(var(--size) * 1%);
      --widthBorder: calc( clamp(20px, 6vw, 80px) * var(--size) * 0.005);
    }
    
    .a,/* for the demo , just a bunch of containers */
    .b,
    .c,
    .d,
    .d,
    .e {
      position: relative;
      /* what the parent loader needs to be (absolute/fixed/sticky works too, static not) */
      float: left;
      border: solid;
      margin: 1em;
    }
    
    div.a {
      --size: 50; /* reset the value used to set the loader's width */
      width: 50%;
      padding-top: 50%;
    }
    
    .b {
      --size: 10;/* reset the value used to set the loader's width */
      width: 600px;
      height: 200px;
    }
    
    .c {
      --size: 15;/* reset the value used to set the loader's width */
      width: 25%;
      padding-top: 20%;
    }
    
    .d {
      --size: 30;/* reset the value used to set the loader's width */
      width: 800px;
      height: 400px;
    }
    
    .e {
      --size: 14;/* reset the value used to set the loader's width */
      width: 90%;
      min-height: 20vh;
    }
    
    div {
      width: 20%;
      padding-top: 20%;
    }
    /* loader styles */
    .loader {
      position: absolute;
      top: 0;
      left: 0;
      display: flex;
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
    }
    
    .loader b {
      display: grid;
      animation: rotate 3s -1s infinite linear;
      border: solid var(--widthBorder) transparent;
      padding: calc(var(--widthBorder) / 2);
      border-radius: 50%;
      border-right-color: #71c491;
      border-top-color: #f7941d;
      grid-row: 1;
      grid-column: 1;
      margin: 0;
    }
    
    .loader>b {
      margin: auto;
      width: var(--width);
    }
    
    .loader>b:before {
      content: "";
      padding-top: 100%;
      grid-row: 1;
      grid-column: 1;
    }
    
    .loader b b {
      border-right-color: #21409a;
      border-top-color: #92278f;
    }
    
    .loader b b b {
      border-right-color: #13b0e6;
      border-top-color: #18244c;
      padding: 0;
    }
    
    @keyframes rotate {
      100% {
        transform: rotate(360deg);
      }
    }
    <div class=a>
      <div class="loader"><b><b><b></b></b>
        </b>
      </div>
    </div>
    <div class=b>
      <div class="loader"><b><b><b></b></b>
        </b>
      </div>
    </div>
    <div class=c>
      <div class="loader"><b><b><b></b></b>
        </b>
      </div>
    </div>
    <div class=d>
      <div class="loader"><b><b><b></b></b>
        </b>
      </div>
    </div>
    <div class=e>
      <div class="loader"><b><b><b></b></b>
        </b>
      </div>
    </div>