javanumero
javanumero

Reputation: 125

How to properly animate a bar gliding?

I have two buttons, when a user clicks on them it gets underlined. However, I'd like the .underline to be animated/glide horizontally to the button that is being clicked on.

Demo: https://jsfiddle.net/ds1wr736/11/

As of right now, the .underline just appears and disapears when a button is clicked. How can I animate this to smoothly glide (x values changing) to the selected button without hacks and JQuery?

function switchTab(tab) {
  if (tab === 1) {
    document.getElementById("tab2").classList.add("underline");
    document.getElementById("tab1").classList.remove("underline");
  }
  else if (tab === 2) {
    document.getElementById("tab1").classList.add("underline");
    document.getElementById("tab2").classList.remove("underline");
  }
}
.bar {
  background-color: gray;
  padding: 20px;
}

.underline {
  border-bottom: 5px solid red;
}

button {
  width: 100px;
  height: 50px;
  background-color: white;
}

button:focus {
  outline: none;
}
<div class="bar">
  <button id='tab1' class="underline" onclick='switchTab(2)'>Tab 1</button>
  <button id='tab2' onclick='switchTab(1)'>Tab 2</button>
</div>

Upvotes: 5

Views: 99

Answers (2)

Wild Beard
Wild Beard

Reputation: 2927

Rather than animating a border I've created an additional element that reacts to the the click events. This allows us to track the position of the "underline" and scale and animate it between buttons when clicked.

This can be modified to accept hover events instead using mouseover instead of click.

let buttons = document.querySelectorAll('button');

buttons.forEach(button => {
  button.addEventListener('mouseover', hoverboard); // Hover event
  //button.addEventListener('click', hoverboard);
});

function hoverboard(e) {

  const board = document.querySelector('.hoverboard');
  // - 1 due to the border of the button
  let width = this.offsetWidth - 1;
  const firstChild = document.querySelector('.bar button:first-child');
  const lastChild = document.querySelector('.bar button:last-child');
  // - 19 due to padding being 20px on the left and removing 1 for the button's border
  let left = this.offsetLeft - 19;

  board.style.cssText = 'transform: translateX(' + left + 'px); width: ' + width + 'px;';

}
.bar {
  position: relative;
  background-color: gray;
  padding: 20px;
}

.underline {
  border-bottom: 5px solid red;
}

button {
  width: 100px;
  height: 50px;
  background-color: white;
}

button:focus {
  outline: none;
}

.hoverboard {
  position: absolute;
  width: 100px;
  height: 3px;
  background: red;
  transition: transform .25s ease, width .25s ease;
}
<div class="bar">
  <button id='tab1'>Tab 1</button>
  <button id='tab2' style="width: 65px;">Tab 2</button>
  <button>Tab 3</button>
  <div class="hoverboard"></div>
</div>

Upvotes: 2

Georgios Dimitriadis
Georgios Dimitriadis

Reputation: 685

Here ya go. Only the edited classes are here:

.underline:after {
  border-bottom: 5px solid red;
  animation-name: slideIn;
  animation-duration: 1s;
  width: 100%;
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
}

@keyframes slideIn {
    from {width: 0;}
    to {width: 100%;}
}

button{
  position: relative;
  width: 100px;
  height: 50px;
  background-color: white;
}

What I did is that I used the abstract after element on the buttons and positioned it absolute to it's relative button. And used css animation.

Upvotes: 2

Related Questions