CSS animations made easy - Using a single keyframes declaration to switch animation direction

Is it possible to animate a div to the right when a button is clicked, then animate back to the left with just one keyframes declaration by swapping classes?

While attempting this in CodePen, I encountered an issue where the div snaps back after the first animation and fails to animate again the second time.

I opted not to use CSS transitions so that I could incorporate features of CSS animations such as a bouncing effect.

window.addEventListener("load", function() {
  var movedOver = false;

  document.querySelector("button").addEventListener("click", function() {
    var buttonEl = document.querySelector(".one");

    if (movedOver) {
      buttonEl.classList.remove("do-the-slide");
      buttonEl.classList.add("do-the-slide-back");

    } else {
      buttonEl.classList.remove("do-the-slide-back");
      buttonEl.classList.add("do-the-slide");
    }

  });

});
.container {
  position: absolute;
  perspective: 800px;
  >div {
    position: absolute;
    padding: 20px;
    text-align: center;
    width: 100px;
  }
  >div.do-the-slide {
    animation: moveOver 1s ease-out;
  }
  >div.do-the-slide-back {
    animation: moveOver 1s reverse ease-out;
  }
  >.one {
    background: red;
  }
}

button {
  margin-top: 100px;
}

@keyframes moveOver {
  from {
    transform: translateX(0px);
  }
  to {
    transform: translateX(100px);
  }
}
<div class="container">
  <div class="one">One</div>
</div>

<button>Clicky</button>

Codepen: http://codepen.io/anon/pen/Vaadao

Answer №1

After experimenting for a while, I have found a satisfactory solution to utilize one keyframes declaration for driving animations in both directions. The trick was to force the browser to essentially reset through a redraw, as detailed here: https://css-tricks.com/restart-css-animation/ (special thanks to Jacob Gray for sharing that reference).

Once I achieved the reset functionality, I incorporated javascript to handle the reverse direction when animating back (this can be done by adding a separate class and simply appending that className).

The setup now allows me to animate in both directions using just one keyframes declaration. This not only looks great but also significantly reduces the amount of css code needed.

window.addEventListener("load", function() {
  var movedOver = false;
  var direction = "";

  document.querySelector("button").addEventListener("click", function() {
    var el = document.querySelector(".one");

    el.classList.remove("do-the-slide");
    el.offsetWidth = el.offsetWidth;

    if (direction === "toRight") {
      direction = "toLeft";
      el.style.animationDirection = "reverse";

    } else {
      direction = "toRight";
      el.style.animationDirection = "";
    }

    el.classList.add("do-the-slide");

  });

});
.container {
  position: absolute;
  perspective: 800px;
  >div {
    position: absolute;
    padding: 20px;
    text-align: center;
    width: 100px;
  }
  >div.do-the-slide {
    animation: moveOver 1s ease-in-out;
    animation-fill-mode: forwards;
  }
  >.one {
    background: red;
  }
}

button {
  margin-top: 100px;
}

@keyframes moveOver {
  from {
    transform: translate3d(0px, 0, 0);
  }
  20% {
    transform: translate3d(-20px, 0, 0);
  }
  80% {
    transform: translate3d(120px, 0, 0);
  }
  to {
    transform: translate3d(100px, 0, 0);
  }
}
<div class="container">
  <div class="one">One</div>
</div>

<button>Clicky</button>

CodePen: http://codepen.io/risingtiger/pen/zqqMpv

Answer №2

Perhaps your intention is to seek forwards, however, a transition will still be necessary to simplify the process.

p span {
  display: inline-block;
  transition: 1s;
}
p:hover span {
  animation: 1s moveOver forwards;
}
@keyframes moveOver {
  to {
    transform: translateX(100px);
  }
<p><span>span</span>
</p>

If you opt for a single animation, you may encounter challenges with paused and steps, and it might not be an easy or feasible task to manage. It seems like there's no enjoyment in this endeavor :)

To achieve a bounce effect, adjusting the final value at 50% should suffice:

p span {
  display: inline-block;
  transition: 1s;
}
p:hover span {
  animation: 2s moveOver infinite;
}
@keyframes moveOver {
  50% {
    transform: translateX(100px);
  }
<p><span>span</span>
</p>

Answer №3

I created a unique pen inspired by this post and the css-tricks article linked here on codepen

Custom HTML Layout

<h2>Innovative Keyframe-Driven Burger Collection</h2>
<p>(each span independently animates for both forward and backward actions)</p>

<div class="wrapper">
  <div class="cell">
    <h3>Default Style</h3>
    <div>
      <button class="burger">
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
      </button> 
    </div>
  </div>

  <div class="cell">
    <h3>Merge Animation</h3>
    <div>
      <button class="burger burger--merge">
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
      </button> 
    </div>
  </div>

  <div class="cell">
    <h3>Rotate Effect</h3>
    <div>
      <button class="burger burger--rotate">
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
      </button> 
    </div>
  </div>

  <div class="cell">
    <h3>Spin Animation</h3>
    <div>
      <button class="burger burger--spin">
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
        <span class="burger__bar"></span>
      </button> 
    </div>
  </div>

</div>

Exclusive CSS Styling

body {
  position: relative;
  padding: 0;
  margin: 0;
  width: 100%;
  height: 100vh;
  background: radial-gradient(#5E802B, #2F4016);
  font-family: sans-serif;
  text-align: center;
  h2,
  h3,
  p {
    color: rgb(255, 255, 255);
  }
}
.wrapper {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  display: flex;
}

.cell {
  flex-grow: 1;
  flex-basis: 50%;
  margin: 0 20px;
  display: flex;
  flex-direction: column;

  div {
    margin-top: auto;
  }
}

.burger {
  // Customized burger styles go here
}

/* Additional unique animations added */

JavaScript Functionality

burgers = document.querySelectorAll('.burger');
for (let i = 0; i < burgers.length; i++) {
     burgers[i].addEventListener("click", function(){
       this.classList.remove("animate");

       var bars = this.querySelectorAll('.burger__bar');
       for (let i = 0; i < bars.length; i++) {
         // Reset span animations - https://css-tricks.com/restart-css-animation/
         void bars[i].offsetWidth;
       }

       if (this.classList.contains("open")){
         this.classList.remove("open");
         this.classList.add("close");
       } else {
         this.classList.remove("close");
         this.classList.add("open");
       }  
       this.classList.add("animate");
    });
};

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

Customizing CSS Ordered Lists with :before Pseudo-element Margin Settings

I found a code snippet that I'm using to customize the styling of my ordered list. You can check it out here: http://codepen.io/sawmac/pen/txBhK Here is the HTML code: <ol class="custom-counter"> <li>This is the first item</li> < ...

What are some ways to address the performance challenges associated with resizing images for responsive design?

When it comes to certain images, I find that scaling them based on the page size is the best way to make them responsive. <img class="img_scale" src="img.png" alt"this img doesn't have width and height definition"> In my CSS: .img_scale{wid ...

How can I center one middle position for my inputs and have the span align to the left?

How can I center my input fields with all the spans aligned to the left? I would like the inputs to be centered on the page, with supporting spans aligned to the left or right of the input lane. I am utilizing some Bootstrap 4 classes for this layout. C ...

Surprising automatic scrolling upon pressing the keydown button in Bootstrap

My webpage appears normal with the Bootstrap framework, but whenever I press the down key, the screen scrolls down and displays unexpected white space due to reaching the end of the background-image sized at 1798px * 1080px. Please refer to the image: htt ...

What is the solution to adding values from a counter?

I am trying to create a JavaScript counter: function animateSun() { var $elie = $("#sun"); $({ degree: 0 }).animate({ degree: 360 }, { duration: 190999, easing: 'linear', step: function(val) { now = Math.round ...

What methods are available to test my website across different browsers such as outdated versions of Internet Explorer like IE8?

Currently, I am working on Chrome with Windows 7 OS installed. However, Windows 7 does not support Internet Explorer 8. This is causing issues when trying to view my web pages in the IE8 version and older. How can I resolve this problem? I have attempted ...

Eliminate the empty choice for Angular when dealing with dynamic option objects

Looking for assistance with this code snippet: <select ng-model='item.data.choice' > <option ng-if='item.data.simple_allow_blank == false' ng-show='choice.name' ng-repeat='choice in item.data.simple_choices&ap ...

Center Your Text Vertically on Google Sites

Trying to spice up my Google Sites page, I decided to take matters into my own hands and customize the banner at the bottom of the site. My goal was to create an orange rectangle with two lines of text perfectly centered both horizontally and vertically. D ...

Explore registrations by year and month

I am currently working on coding where a record needs to be displayed based on the date (Month, Year), for instance: 12-2022. However, with the current code I have, I am unable to achieve this. Can anyone offer some guidance on what steps I should take? ...

Tips for avoiding width miscalculations due to the appearance or disappearance of a vertical scrollbar

I recently created a website featuring a tree explorer in a sidebar. However, I encountered an issue with the width calculation of the explorer when toggling the subtrees. This problem specifically arises on Chrome version 111.0.5563.64 for Ubuntu Desktop ...

Using JavaScript/JQuery, change relative or viewport sizes to fixed sizes when the page loads

Wishing you a delightful day. As I work on my website, I find myself relying heavily on viewport units like vw and vh for all measurements such as font size, padding, margin, width, and height. These units provide the flexibility needed to ensure that the ...

Show either the abbreviated or complete form of the text

Initially, the default display should show the shortened version of each element (one line with "..." included). All items must consistently be shown in either the shortened or full-length version. If all items are in shortened mode - clicking on one item ...

Implementing a hamburger menu across various webpages

I recently followed a tutorial on adding a hamburger menu from this YouTube video. The menu works perfectly on the INDEX.html page, but when I try to add the same code to other pages like "contact" or "about", none of the menu features seem to work. I rea ...

Ensuring consistent placement and scrollability of two divs across all screen sizes

I am in need of a solution to fix the top and bottom divs in the given image. The scroll should only occur when there is overflow. <!DOCTYPE html> <html> <head> <script src="//code.jquery.com/jquery-1.9.1.min.js"></script> ...

Changing the text color of the Vuetify Table header is a simple way to customize the

I am currently working on a Vuetify table with the class condition-table. I have applied the following CSS styling: .condition-table { background-color: #e1f5fe70; } The styling seems to work fine so far. However, when I added this additional CSS: .co ...

The incorporation of zoom disrupts the smooth scrolling capability of the menu

My landing page has a menu that scrolls users to the selected section. However, my client prefers the page at a 90% zoom level. To accommodate this request, I added the following line of code: body { zoom:90%; } Unfortunately, when I click on a menu o ...

Designing Tables and Grids with HTML and CSS

Struggling to design the table below, I initially attempted using an HTML table. However, I encountered issues with applying box shadow properly as I needed to round the corners of the first and last td cells rather than the tr element (which doesn't ...

Items in the Vue.Draggable display can be arranged on both the left and right

Recently delving into Vue.js, I am utilizing Vuedraggable for item dragging in my project. Currently, the items in the draggable div are shown as Text 1 Text 2 Text 3 Text 4 Is there a way to rearrange them to display like this? Text 1 Text 2 Text 3 T ...

Is there a way to make height: 100% adjust to accommodate other elements with specific pixel heights within the same div?

I am facing a challenge with a container that has a specified height and contains two nested divs. The first div has a height defined in pixels, and I want the second div to fill up the remaining space in the container, which is essentially 100% minus the ...

The footer fails to remain at the bottom of the page when there is limited content available

Despite searching extensively on Google, I have not been able to find a solution to this frequently asked question. My challenge is to consistently position the footer at the bottom of the page, even when there is minimal content present. Below is an exce ...