Pure CSS implementation of a loader animation

My attempt at creating a loader animation was inspired by this design on dribbble: . After looking at the comments, I came across a CodePen with a similar animation, but it was all done in JavaScript: https://codepen.io/AbubakerSaeed/full/yLaQVdq

tl2
  .set(".dC-1", { rotate: 0 })
  .set(".dC-2", { rotate: 30 })
  .set(".dC-3", { rotate: 60 })
  .set(".dC-4", { rotate: 90 })
  .set(".dC-5", { rotate: 120 })
  .set(".dC-6", { rotate: 150 })
  .set(".dC-7", { rotate: 180 })
  .set(".dC-8", { rotate: 210 })
  .set(".dC-9", { rotate: 240 })
  .set(".dC-10", { rotate: 270 })
  .set(".dC-11", { rotate: 300 })
  .set(".dC-12", { rotate: 330 })
  .to(".dC-1", {
  delay: s,
  duration: s,
    rotate: 30,
})

I attempted to convert this animation into pure CSS but struggling to understand how to do it. Could someone guide me on how to create these two timelines in SCSS and translate the JavaScript code into SCSS?

Answer №1

To achieve this effect, you can manage two animations simultaneously:

  • A rotating plane with blocks moving in and out
  • Displaying/hiding divs on a static plane

You can coordinate the animation to show/hide the blocks on the different plane when necessary.

For instance, if one rotation takes 5 seconds, the entire animation will last 10 seconds - with 5 seconds spent invisible and the other 5 visible.

In the example below, I've created a demonstration with two blocks. You can easily expand it to include as many blocks as needed. I've slightly shown the blocks when they should be invisible to illustrate the animation process.

body {
  background-color: #1f2227;
}

.wrapper {
  width: 200px;
  height: 200px;
  position: relative;
  border: 1px solid white;
  padding: 20px;
  transform: rotate(-90deg)
}

.spinner,
.static {
  position: absolute;
  left: 50%;
  top: 50%;
}

.spinner {
  animation: 5s linear infinite spin;
}

.block {
  width: 10px;
  height: 10px;
  background-color: #EEE;
  display: inline-block;
  position: absolute;
  left: calc(50% - 5px);
  top: calc(50% - 5px);
}

.static .b1 {
  transform: translateX(100px);
  animation: 10s ease-in-out infinite hide-1;
}

.static .b2 {
  transform: rotate(30deg) translateX(100px);
  animation: 10s ease-in-out infinite hide-2;
}

.static .b3 {
  transform: rotate(60deg) translateX(100px);
}

.spinner .b1 {
  transform: translateX(100px);
  animation: 10s ease-in-out infinite go-in-out-1;
}

.spinner .b2 {
  transform: rotate(30deg) translateX(100px);
  animation: 10s ease-in-out infinite go-in-out-2;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@keyframes go-in-out-1 {
  0% {
    transform: translateX(100px);
    opacity: 1;
  }
  4%,
  50% {
    transform: translateX(0px);
    opacity: 1;
  }
  54% {
    transform: translateX(100px);
    opacity: 1;
  }
  54.1%,
  100% {
    transform: translateX(100px);
    opacity: .1;
  }
}

@keyframes go-in-out-2 {
  4% {
    transform: translateX(100px);
    opacity: 1;
  }
  8%,
  46% {
    transform: translateX(10px);
    opacity: 1;
  }
  50% {
    transform: translateX(100px);
    opacity: 1;
  }
  50.1%,
  100% {
    transform: translateX(100px);
    opacity: .1;
  }
}

@keyframes hide-1 {
  0% {
    opacity: .1;
  }
  4%,
  50% {
    opacity: .1;
  }
  50.1%,
  100% {
    opacity: 1;
  }
}

@keyframes hide-2 {
  4% {
    opacity: .1;
  }
  8%,
  54% {
    opacity: .1;
  }
  54.1%,
  100% {
    opacity: 1;
  }
}
<div class="wrapper">
  <div class="static">
    <div class="block b1"> </div>
    <div class="block b2"> </div>
    <div class="block b3"> </div>
  </div>
  <div class="spinner">
    <div class="block b1"> </div>
    <div class="block b2"> </div>
  </div>
</div>

Answer №2

Hi Lars, your assistance is greatly appreciated. I've been working on a project involving 12 blocks, but the animation seems to be behaving unpredictably. Do you think my calculations for timing or percentages might be incorrect?

Below is the code I've written in SCSS for better readability:

.spinner {
  animation: 5s linear infinite spin;
}
    
$rotation: 0deg;
    
    .static {
      @for $i from 1 to 12 {
        .b#{$i} {
          transform: rotate($rotation) translateX($loader-size / 2);
          animation: 10s ease-in-out infinite hide-#{$i};
        }
    
        $rotation: $rotation + 30deg;
      }
    }
    
    $rotation: 0deg;
    
    .spinner {
      @for $i from 1 to 12 {
        .b#{$i} {
          transform: rotate($rotation) translateX($loader-size / 2);
          animation: 10s linear infinite go-in-out-#{$i};
        }
    
        $rotation: $rotation + 30deg;
      }
    }
    
    @keyframes spin {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    
    $first: 0;
    $second: 4;
    $third: 50;
    $fourth: 54;
    $fifth: 54.1;
    
    @for $i from 1 to 12 {
      @keyframes go-in-out-#{$i} {
        #{$first * 1%} {
          transform: translateX($loader-size / 2);
          opacity: 1;
        }
    
        #{$second * 1%},
        #{$third * 1%} {
          transform: translateX($block-size * ($i - 1));
          opacity: 1;
        }
    
        #{$fourth * 1%} {
          transform: translateX($loader-size / 2);
          opacity: 1;
        }
    
        #{$fifth * 1%},
        100% {
          transform: translateX($loader-size / 2);
          opacity: 0;
        }
      }
    
      $first: $first + 4;
      $second: $second + 4;
      $third: $third - 4;
      $fourth: $fourth - 4;
      $fifth: $fifth - 4;
    }
    
    $first-2: 0;
    $second-2: 4;
    $third-2: 50;
    $fourth-2: 50.1;
    
    @for $i from 1 to 12 {
      @keyframes hide-#{$i} {
        #{$first-2 * 1%} {
          opacity: 0;
        }
    
        #{$second-2 * 1%},
        #{$third-2 * 1%} {
          opacity: 0;
        }
    
        #{$fourth-2 * 1%},
        100% {
          opacity: 1;
        }
      }
    
      $first-2: $first-2 + 4;
      $second-2: $second-2 + 4;
      $third-2: $third-2 + 4;
      $fourth-2: $fourth-2 + 4;
    }

Answer №3

Check out this example showcasing an Angular Material loader using only HTML and CSS

@import url(https://fonts.googleapis.com/css?family=RobotoDraft:500);
* {
  font-family: 'RobotoDraft', sans-serif;
  font-size: 15px;
  padding: 20px;
}

.material_block {
  width: 580px;
  padding: 20px;
  background-color: #fff;
  box-shadow: 0 2px 5px rgba(0, 0, 0, .4);
  margin: auto;
}

.spinner {
  -webkit-animation: rotation 1.35s linear infinite;
  animation: rotation 1.35s linear infinite;
}

@-webkit-keyframes rotation {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(270deg);
    transform: rotate(270deg);
  }
}

@keyframes rotation {
  0% {
    -webkit-transform: rotate(0deg);
    transform: rotate(0deg);
  }
  100% {
    -webkit-transform: rotate(270deg);
    transform: rotate(270deg);
  }
}

.circle {
  stroke-dasharray: 180;
  stroke-dashoffset: 0;
  -webkit-transform-origin: center;
  -ms-transform-origin: center;
  transform-origin: center;
  -webkit-animation: turn 1.35s ease-in-out infinite;
  animation: turn 1.35s ease-in-out infinite;
}

@-webkit-keyframes turn {
  0% {
    stroke-dashoffset: 180;
  }
  50% {
    stroke-dashoffset: 45;
    -webkit-transform: rotate(135deg);
    transform: rotate(135deg);
  }
  100% {
    stroke-dashoffset: 180;
    -webkit-transform: rotate(450deg);
    transform: rotate(450deg);
  }
}

@keyframes turn {
  0% {
    stroke-dashoffset: 180;
  }
  50% {
    stroke-dashoffset: 45;
    -webkit-transform: rotate(135deg);
    transform: rotate(135deg);
  }
  100% {
    stroke-dashoffset: 180;
    -webkit-transform: rotate(450deg);
    transform: rotate(450deg);
  }
}

svg:nth-child(1) {
  stroke: #e51c23;
}

svg:nth-child(2) {
  stroke: #e91e63;
}

svg:nth-child(3) {
  stroke: #9c27b0;
}

svg:nth-child(4) {
  stroke: #673ab7;
}

svg:nth-child(5) {
  stroke: #3f51b5;
}

svg:nth-child(6) {
  stroke: #5677fc;
}

svg:nth-child(7) {
  stroke: #03a9f4;
}

svg:nth-child(8) {
  stroke: #00bcd4;
}

svg:nth-child(9) {
  stroke: #009688;
}

svg:nth-child(10) {
  stroke: #259b24;
}

svg:nth-child(11) {
  stroke: #8bc34a;
}

svg:nth-child(12) {
  stroke: #cddc39;
}

svg:nth-child(13) {
  stroke: #ffeb3b;
}

svg:nth-child(14) {
  stroke: #ffc107;
}

svg:nth-child(15) {
  stroke: #ff9800;
}

svg:nth-child(16) {
  stroke: #ff5722;
}

svg:nth-child(17) {
  stroke: #795548;
}

svg:nth-child(18) {
  stroke: #9e9e9e;
}

svg:nth-child(19) {
  stroke: #607d8b;
}
<div class="material_block">
  <svg class="spinner" width="65px" height="65px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg"><circle class="circle" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle></svg>
  <svg class="spinner" width="65px" height="65px" viewBox="0 0 66 66" xmlns="http://www.w3.org/2000/svg"><circle class="circle" fill="none" stroke-width="6" stroke-linecap="round" cx="33" cy="33" r="30"></circle></svg>
  <!-- Add more SVG elements here as needed -->
</div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Resolve the issue of text overflow in PrimeNG Turbo Table cells

When utilizing Primeng table and enabling the scrollable feature, the table is expected to display a scrollbar while ensuring that the text within the cells does not overflow. Unfortunately, this expected behavior is not occurring. To see an example of th ...

How to extract a div from its parent using jQuery

I need to stack two div boxes on top of each other, and my goal is to hide the upper one when it's being hovered over. HTML: <div id="left_middle"> <div id="rfinder_UP"></div> <div id="rfinder_DWN"></div> </div> C ...

Achieve a sleek, modern look by using CSS to add a border with crisp square

Hello everyone, I'm currently attempting to add a border to only one side of an a element. However, when I add the border to just one side, it creates a sharp diagonal edge: https://i.stack.imgur.com/pAqMM.png My goal is to eliminate the sharp edge ...

Guide on positioning two background images next to each other on a page?

Looking to replicate a design using CSS that requires editable background images via a CMS. The left image needs an angled/slanted right edge with a gold border, while the right image should be parallel. Content placement is not a concern at this stage. V ...

Enhancing CSS to create a more visually appealing loading spinner

I am completely new to CSS and I've been attempting to customize the CSS in the angular-loading-bar module to create a more visually appealing loading spinner. Currently, I have a square spinner that rotates in the center of the page, with a black bor ...

Using CSS background-image URL in .NET MVC tutorials

What is the correct way to route to an image in an MVC web application? MVC has default routes, meaning you can access the same page in multiple ways: 1. localhost 2. localhost/home/index However, you can also do it this way, changing the root directory. ...

Leverage the power of Sass styles throughout your Vue.js project

Attempting to utilize sass globally in a vue.js app, I followed this method: const { defineConfig } = require('@vue/cli-service') module.exports = { css:{ loaderOptions:{ sass:{ data:`@import "@/sass/t ...

Is there a way to make a sass file universally accessible without having to import it into every file and causing an increase in bundle size?

Question How can I efficiently import variables.scss globally without the need to duplicate them in every file and reference them in my build instead of importing? Setup I am using Vue2 with laravel-mix, where I have imported index.scss in my main.js ...

css hover function not operational with back to top button

I have implemented a back to top button using the following resource: Although the button is functioning as expected, I am encountering an issue with the hover effect. In Chrome inspector, manually forcing hover on .cd-top does not apply the CSS style as ...

Tips for keeping the footer consistently at the bottom of the page without using a fixed position (to avoid overlapping with the main content)

I've been struggling to keep the footer at the bottom of my webpage. I came across a solution online suggesting to use fixed CSS positioning. However, when I applied this fix, the footer ended up overlapping with the actual content on the page. Is the ...

Access information from multiple div elements using JavaScript data-attributes

Having trouble retrieving data-attribute values from multiple divs with the same class when clicked. The goal is to display the data value of the clicked div. function playSound(e) { const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`) ...

I attempted to craft a toggle button by applying and removing an active class that I had previously designed, but unfortunately, it did not function as intended

Every time I click on a button, I keep encountering this error message. I am certain that my selector is correct, but I can't seem to figure out why I'm getting the Uncaught TypeError: Cannot read property 'classList' of undefined at HT ...

Instructions for sketching a square at predetermined coordinates

Looking for a simple function to draw a box at specific coordinates x and y. I've searched for guides, but they all seem to provide excessive or insufficient information. Thanks in advance! ...

A guide on rotating loaders and inserting custom text within loaders using CSS

Seeking assistance to modify my code for creating a unique loader design. The inner loader needs to remain static with only top and bottom segments, while the middle loader rotates anti-clockwise and the outer loader rotates clockwise. Additionally, the ...

Could the difficulty in clicking the first entry in Firefox be a bug?

Be sure to focus on the section highlighted in blue in the initial table. If you'd like, you can view the issue here view image http://example.com/static/error.gif UPDATE you can replicate it by double-clicking on the top portion of the first entry ...

What is the technique to enable this div to be clickable?

I am trying to make each "card" of a WordPress plugin clickable on my website. I have inserted a Pure JS element with the following code: document.getElementsByClassName('fc_card-container').onclick = function() {alert('It works!');} ...

Why does Javascript Tic-Tac-Toe delay notifying the user until the entire board is full?

There have been a lot of questions about Tic-Tac-Toe javascript, but my issue is unique. My code works fine, but it doesn't declare a winner or a tie game until the entire board is filled. I suspect that my if statements are causing problems and can&a ...

Tips for aligning an image on top of another image

Here is the code I am working with (also pasted below). My goal is to create a layout with two columns, where the first column features two images and the second column displays some text. In the first column, I specifically want the first image to have a ...

How can a CSS class be used to toggle the visibility of a DIV element with JavaScript?

I've been researching and came across several scripts that allow for toggling the contents of a DIV by clicking on a button, however, they all use IDs. What I am looking to achieve is similar but using classes instead of IDs. This way, if I want to h ...

Modifying the weight of fonts in TVML components

Currently, I'm in the process of developing a TVML app specifically for the Apple TV. Lately, I've been experimenting with making some style adjustments to various elements within the app. Following the guidance provided in the TVML documentatio ...