Important note:

This version is deprecated and will not be supported from 1 May 2018. A new version is available at https://stas-melnikov.ru/books/hctasks/tasks/the-responsive-loader-with-em-units/

The responsive loader with em units

Demo: https://codepen.io/melnik909/pen/MrXjwX

Task description

Developers very often need to create one component with several modifications. So I need to create preloaders having differentt sizes according to my current task.

Firstly I have described common component styles:

.preloader{
  box-sizing: border-box;
  position: relative;
  color: #fff;
  border: 2px solid currentColor;
}

.preloader:before{
  content: "";
  background-color: currentColor;
  position: absolute;
  top: 50%;
  left: 50%;
  animation: 3s ease-out infinite alternate backwards;
}

Next I wanted to set different sizes for the component. In the beginning I set sizes for the main variant. I defined the outline size equals 60px for the .preloader class and moving square sizes for the .preloader:before.

After that I should make the second variant of my component with 120px size for the outline and 30px for the square. I created the class .preloader_lg for setting these sizes.

.preloader{
  width: 60px;
  height: 60px;
}

.preloader:before{
  width: 15px;
  height: 15px;
}

.preloader_lg{
  width: 120px;
  height: 120px;
}

.preloader_lg:before{
  width: 30px;
  height: 30px;
}

Next I wrote component css animation for the main variant:

.preloader:before{
  width: 15px;
  height: 15px;
  animation-name: preloader;  
}

@keyframes preloader{

  0%, 10%, 90%, 100%{
    transform: translate3d(-7.5px, -7.5px, 0) scale(0);
  }

  20%, 70%{
   transform: translate3d(-7.5px, -7.5px, 0) scale(1);
  }

  30%{
   transform: translate3d(-30px, -30px, 0) scale(1);
  }

  40%{
   transform: translate3d(15px, -30px, 0) scale(1);
  }

  50%{
   transform: translate3d(15px, 15px, 0) scale(1);
  }

  60%{
   transform: translate3d(-30px, 15px, 0) scale(1);
  }
}

Then I did the same for the second type of the component:

.preloader_lg:before{
  width: 30px;
  height: 30px;
  animation-name: preloader-lg;
}

@keyframes preloader-lg{

  0%, 10%, 90%, 100%{
   transform: translate3d(-15px, -15px, 0) scale(0);
  }

  20%, 70%{
   transform: translate3d(-15px, -15px, 0) scale(1);
  }

  30%{
   transform: translate3d(-60px, -60px, 0) scale(1);
  }

  40%{
   transform: translate3d(30px, -60px, 0) scale(1);
  }

  50%{
   transform: translate3d(30px, 30px, 0) scale(1);
  }

  60%{
   transform: translate3d(-60px, 30px, 0) scale(1);
  }
}

I have solved this task as but I think my solution isn’t an optimal one because the code duplicated. My team-lead hinted that I can avoid code repeating with em units. And I would like to ask you do it and show me how it can be possible.

You must define preloaders sizes using font-size as a result. For example:

.preloader{
  /* width and height in em's here */
  font-size: 15px;
}

.preloader_lg{
  font-size: 30px;
}

Also you should have one common variant of css animation for all component’s types:

.preloader:before{
  animation-name: preloader;
}

@keyframes preloader{
  /* your new css animation here */
}

Solution

Let’s start from this code optimising:

.preloader{
  width: 60px;
  height: 60px;
}

.preloader_lg{
  width: 120px;
  height: 120px;
}

So, here I need to set width and height properties twice. You may think that it’s OK. But what if I have not 2 variants, for example 5? I definitely would copy the code, like that:

.preloader{
  width: 60px;
  height: 60px;
}

.preloader_lg{
  width: 120px;
  height: 120px;
}

.preloader_xl{
  width: 150px;
  height: 150px;
}

.preloader_2xl{
  width: 180px;
  height: 180px;
}

.preloader_3xl{
  width: 200px;
  height: 200px;
}

That decision doesn’t optimal one, obviously. But how I can make it better?

You may notice that both width and height values for .preloader_lg element can be calculated as the same properties width and height of .preloader, multiplied by 2. So, is it possible to set it with CSS?

Luckily one friend of mine gave me the idea. I have to use em unit in this task. After checking it in Google, I understood that if I use em for the width and height, I can set them as follows:

We(He) = Wem(Hem) * Fs

We(He) — is the element width (or element height)
Wem(Hem) — is the width(height) property value in ems
Fs — is the font-size property value for the element

So, if I use ems as coefficients and multiply them by element font size, I will get new width and height for my .preloader. It’s exactly what I wanted! But there is one problem. What values should I set for the font-size, width and height in ems?

Firstly, let’s think how ems calculates. I have equal values in px for width and height here, so they will be also equal in ems. In this way I will use the width property only in my calculation further, because it’s easier for me. But how can I define this width value?

As I told before, I should multiply the with and height from .preloader class by 2 to get the width and height values for .preloader_lg element. Let’s try to use 2 as a value. For get element font-size I need to divide pixel value by 2, thus:

Fs(.preloader) = Wpx : Wem = 60px : 2 = 30px;
Fs(.preloader_lg) = Wpx : Wem = 120px : 2 = 60px;

After that I can change code as follows:

.preloader{
  width: 2em;
  height: 2em;
  font-size: 30px;
}

.preloader_lg{
  font-size: 60px;
}

Go further, let’s set in ems all width and height properties for .preloader:before and .preloader_lg:before:

.preloader:before{
  width: 15px;
  height: 15px;
}

.preloader_lg:before{
  width: 30px;
  height: 30px;
}

This turn my calculation has some difference, because I have known already font-size property inherited from .preloader element:

Wem = Wpx : Fs = 15px : 30px = 0.5em
Wem = Wpx : Fs = 30px : 60px = 0.5em
.preloader{
  width: 2em;
  height: 2em;
  font-size: 30px;
}

.preloader:before{
  width: 0.5em;
  height: 0.5em;
}

.preloader_lg{
  font-size: 60px;
}

That’s all, folks! I don’t have to set all new sizes except font-size to modify the element size in some new classes anymore.

But what’s about animation? I can optimise it too with ems. Let’s experiment this the first keyframe:

0%, 10%, 90%, 100%{
  transform: translate3d(-7.5px, -7.5px, 0) scale(0);
}

Next, let’s divide px values by font-size:

-7.5px : 30px = 0.25em
0%, 10%, 90%, 100%{
  transform: translate3d(-0.25em, -0.25em, 0) scale(0);
}

And do the same for the rest animation code, finally. After all calculations I will get this scenario:

@keyframes preloader{

  0%, 10%, 90%, 100%{
   transform: translate3d(-0.25em, -0.25em, 0) scale(0);
  }

  20%, 70%{
   transform: translate3d(-0.25em, -0.25em, 0) scale(1);
  }

  30%{
   transform: translate3d(-1em, -1em, 0) scale(1);
  }

  40%{
   transform: translate3d(0.5em, -1em, 0) scale(1);
  }

  50%{
   transform: translate3d(0.5em, 0.5em, 0) scale(1);
  }

  60%{
   transform: translate3d(-1em, 0.5em, 0) scale(1);
  }
}

Now I don’t need special animation code for .preloader-lg. All component code will be as follows:

.preloader{
  width: 2em;
  height: 2em;
  font-size: 30px;

  box-sizing: border-box;
  position: relative;
  color: #fff;
  border: 2px solid currentColor;
}

.preloader:before{
  content: "";
  width: 0.5em;
  height: 0.5em;
  background-color: currentColor;

  position: absolute;
  top: 50%;
  left: 50%;

  animation: preloader 3s ease-out infinite alternate backwards;
}

.preloader_lg{
  font-size: 60px;
}

@keyframes preloader{

  0%, 10%, 90%, 100%{
   transform: translate3d(-0.25em, -0.25em, 0) scale(0);
  }

  20%, 70%{
   transform: translate3d(-0.25em, -0.25em, 0) scale(1);
  }

  30%{
   transform: translate3d(-1em, -1em, 0) scale(1);
  }

  40%{
   transform: translate3d(0.5em, -1em, 0) scale(1);
  }

  50%{
   transform: translate3d(0.5em, 0.5em, 0) scale(1);
  }

  60%{
   transform: translate3d(-1em, 0.5em, 0) scale(1);
  }
}

Why you need to support the project

I want to share my knowledge with you but creating posts takes a long time. Therefore I'm looking for subscribers who will financially support me. Your money will go towards the development of the HCTask project and creating my book about HTML and CSS. Together we can do it. Thank you!

PayPal: https://www.paypal.me/melnik909

results matching ""

    No results matching ""