Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS transform from hamburger icon to cross to arrow

I am trying to animate an icon from three line hamburger menu to cross to a right-pointing arrow using CSS transform. The first transformation work well, but I am stuck on the second one: cross to arrow.

Here is a fiddle:

https://jsfiddle.net/v0fgvdg2/25

And the relevant styles in SCSS:

.threebar {
  cursor: pointer;
  margin-top: 33px;
  margin-left: 33px;
  .bar {
    width: 70px;
    height: 22px;
    background: #000;
    margin-bottom: 11px;
    transition: all 0.5s ease;
    &:nth-child(1) {
      transform-origin: 50%;
    }
    &:nth-child(2) {
      transform-origin: 50%;
    }
  }
  &.cross {
    .bar:nth-child(1) {
      transform: translateY(75%)  rotate(45deg);
    }
    .bar:nth-child(2) {
      transform: translateY(-75%) rotate(-45deg);
    }
    .bar:nth-child(3) {
      opacity: 0;
    }
  }
  &.arrow {
    .bar:nth-child(1) {
      background: red;
      transform: scaleX(0.5) translateY(50%) rotate(45deg);
    }
    .bar:nth-child(2) {
      background: blue;
      transform: scaleX(0.5) translateY(50%) rotate(-45deg);
    }
    .bar:nth-child(3) {
      opacity: 0;
    }
  }
}

I can not really wrap my head around how to apply the transformations to the arrow-state.

I would like the right arrow to be something like the left side of the cross, hence the scaleX(0.5), but this has the effect of distorting the shape, instead of simply halving it as I had hoped.

Does anybody have any tips on how to think of this?

Help much appreciated!

like image 677
Rasmus Svensson Avatar asked Feb 25 '16 11:02

Rasmus Svensson


Video Answer


2 Answers

Here is one way to produce the effect that you are looking for. I have altered the positioning and other stuff a bit but the basic idea remains the same as your original one. I have positioned all the three bar elements absolutely and used transform: translateY() to get them into their original places. When the shape needs to change into an arrow, I have changed the transform-origin and then applied the necessary transforms.

SCSS Code:

.threebar {
  cursor: pointer;
  position: relative;
  height: 88px; /* 22px * 3 + space between (11px * 2) */
  margin-top: 33px;
  margin-left: 33px;
  .bar {
    position: absolute;
    width: 70px;
    height: 22px;
    background: #000;
    top: 33px;
    transition: all 0.5s ease;
    &:nth-child(1) {
      transform: translateY(-150%);
    }
    &:nth-child(3) {
      transform: translateY(150%);
    }
  }
  &.cross {
    .bar:nth-child(1) {
      transform: rotate(45deg);
    }
    .bar:nth-child(3) {
      transform: rotate(-45deg);
    }
    .bar:nth-child(2) {
      opacity: 0;
    }
  }
  &.arrow {
    .bar:nth-child(1) {
      transform-origin: right center;
      transform: translateY(33%) rotate(45deg);
    }
    .bar:nth-child(3) {
      transform-origin: right center;
      transform: translateY(-33%) rotate(-45deg);
    }
    .bar:nth-child(2) {
      opacity: 0;
    }
  }
}

Demo with compiled CSS:

//Hamburger to cross

$(document).on('click', '.hamburger', function() {
  $('.threebar')
    .removeClass('hamburger')
    .addClass('cross');
});

// Cross to Arrow 
$(document).on('click', '.cross', function() {
  $('.threebar')
    .removeClass('cross')
    .addClass('arrow');
});

// Arrow to Hamburger 
$(document).on('click', '.arrow', function() {
  $('.threebar')
    .removeClass('arrow')
    .addClass('hamburger');
});
.threebar {
  cursor: pointer;
  position: relative;
  height: 88px; /* 22px * 3 + space between (11px * 2) */
  margin-top: 33px;
  margin-left: 33px;
}
.threebar .bar {
  position: absolute;
  width: 70px;
  height: 22px;
  background: #000;
  top: 33px;
  transition: all 0.5s ease;
}
.threebar .bar:nth-child(1) {
  transform: translateY(-150%);
}
.threebar .bar:nth-child(3) {
  transform: translateY(150%);
}
.threebar.cross .bar:nth-child(1) {
  transform: rotate(45deg);
}
.threebar.cross .bar:nth-child(3) {
  transform: rotate(-45deg);
}
.threebar.cross .bar:nth-child(2) {
  opacity: 0;
}
.threebar.arrow .bar:nth-child(1) {
  transform-origin: right center;
  transform: translateY(33%) rotate(45deg);
}
.threebar.arrow .bar:nth-child(3) {
  transform-origin: right center;
  transform: translateY(-33%) rotate(-45deg);
}
.threebar.arrow .bar:nth-child(2) {
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='threebar hamburger'>
  <div class='bar'></div>
  <div class='bar'></div>
  <div class='bar'></div>
</div>

Demo with different dimensions:

The output seems reasonably responsive too. Below is a demo with different dimensions for the bars.

//Hamburger to cross

$(document).on('click', '.hamburger', function() {
  $('.threebar')
    .removeClass('hamburger')
    .addClass('cross');
});

// Cross to Arrow 
$(document).on('click', '.cross', function() {
  $('.threebar')
    .removeClass('cross')
    .addClass('arrow');
});

// Arrow to Hamburger 
$(document).on('click', '.arrow', function() {
  $('.threebar')
    .removeClass('arrow')
    .addClass('hamburger');
});
.threebar {
  cursor: pointer;
  position: relative;
  height: 90px;
  margin-top: 33px;
  margin-left: 33px;
}
.threebar .bar {
  position: absolute;
  width: 75px;
  height: 25px;
  background: #000;
  top: 25px;
  transition: all 0.5s ease;
}
.threebar .bar:nth-child(1) {
  transform: translateY(-150%);
}
.threebar .bar:nth-child(3) {
  transform: translateY(150%);
}
.threebar.cross .bar:nth-child(1) {
  transform: rotate(45deg);
}
.threebar.cross .bar:nth-child(3) {
  transform: rotate(-45deg);
}
.threebar.cross .bar:nth-child(2) {
  opacity: 0;
}
.threebar.arrow .bar:nth-child(1) {
  transform-origin: right center;
  transform: translateY(33%) rotate(45deg);
}
.threebar.arrow .bar:nth-child(3) {
  transform-origin: right center;
  transform: translateY(-33%) rotate(-45deg);
}
.threebar.arrow .bar:nth-child(2) {
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='threebar hamburger'>
  <div class='bar'></div>
  <div class='bar'></div>
  <div class='bar'></div>
</div>

Demo with arrow exactly like left half of cross:

My original assumption was that you just needed the cross to change into an arrow (resembling the left half of the cross). If you meant for it to be exactly same as the left half (that is, scaled down) then you could use the below snippet.

//Hamburger to cross

$(document).on('click', '.hamburger', function() {
  $('.threebar')
    .removeClass('hamburger')
    .addClass('cross');
});

// Cross to Arrow 
$(document).on('click', '.cross', function() {
  $('.threebar')
    .removeClass('cross')
    .addClass('arrow');
});

// Arrow to Hamburger 
$(document).on('click', '.arrow', function() {
  $('.threebar')
    .removeClass('arrow')
    .addClass('hamburger');
});
.threebar {
  cursor: pointer;
  position: relative;
  height: 88px;
  margin-top: 33px;
  margin-left: 33px;
}
.threebar .bar {
  position: absolute;
  width: 70px;
  height: 22px;
  background: #000;
  top: 33px;
  transition: all 0.5s ease;
}
.threebar .bar:nth-child(1) {
  transform: translateY(-150%);
}
.threebar .bar:nth-child(3) {
  transform: translateY(150%);
}
.threebar.cross .bar:nth-child(1) {
  transform: rotate(45deg);
}
.threebar.cross .bar:nth-child(3) {
  transform: rotate(-45deg);
}
.threebar.cross .bar:nth-child(2) {
  opacity: 0;
}
.threebar.arrow .bar:nth-child(1) {
  transform-origin: right center;
  transform: translateX(-33.33%) translateY(33%) rotate(45deg) scaleX(0.66);
}
.threebar.arrow .bar:nth-child(3) {
  transform-origin: right center;
  transform: translateX(-33.33%) translateY(-33%) rotate(-45deg) scaleX(0.66);
}
.threebar.arrow .bar:nth-child(2) {
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='threebar hamburger'>
  <div class='bar'></div>
  <div class='bar'></div>
  <div class='bar'></div>
</div>
like image 185
Harry Avatar answered Oct 15 '22 19:10

Harry


The problem in your example is the order of the transform operations. You need to remember, that the right-most operation is performed first, in your case the rotation. Only then the scaling is applied which results in the weird angles you see.

So, just turn around the order and it will be clearer to you and you do not need to change origins or use skew operations:

transform: translateY(50%) rotate(45deg) scaleX(0.5);

Simple DEMO (you still need to adjust translation)

like image 22
Paul Avatar answered Oct 15 '22 20:10

Paul