eroc83
eroc83

Reputation: 21

Animating material icons to transform: Hamburger to X

Long time reader, first-time poster. I want to have a material hamburger transform to an "X" when clicked and then transform back when clicked again in an Angular application. The website https://material.io/design/iconography/animated-icons.html#transitions shows there are animations just like what I want, but there are no docs on how to do it.

I tried a CSS approach but it is not showing and it would be good to keep the project consistent with the material icons.

<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
  <span class="sr-only">Toggle navigation</span>
  <span class="icon-bar top-bar"></span>
  <span class="icon-bar middle-bar"></span>
  <span class="icon-bar bottom-bar"></span>
</button>

CSS

navbar-toggle {
  border: none;
  background: transparent !important;

  &:hover {
    background: transparent !important;
  }

  .icon-bar {
    width: 22px;
    transition: all 0.2s;
  }
  .top-bar {
    transform: rotate(45deg);
    transform-origin: 10% 10%;
  }
  .middle-bar {
    opacity: 0;
  }
  .bottom-bar {
    transform: rotate(-45deg);
    transform-origin: 10% 90%;
  }
}

.navbar-toggle.collapsed {
  .top-bar {
    transform: rotate(0);
  }
  .middle-bar {
    opacity: 1;
  }
  .bottom-bar {
    transform: rotate(0);
  }
}

The above code is a workaround to the material icons. When I run the above code, I get a transparent button with no animations. If anyone knows how to do a transition from a hamburger to an "X" in angular with bootstrap, I would be very grateful.

Upvotes: 2

Views: 6318

Answers (1)

Douglas P.
Douglas P.

Reputation: 560

I made this simple StackBlitz using just Angular animations (no bootstrap) that still needs some work in the CSS of the states of the animation to make it nice and smooth but you can get the idea.

Create 3 bars wrapped in a container

<div (click)="onClick()" class="btn">
  <div class="icon-bar" [@hamburguerX]="isHamburguer ? 'hamburguer' : 'topX'"></div>
  <div class="icon-bar" [@hamburguerX]="isHamburguer ? 'hamburguer' : 'hide'"></div>
  <div class="icon-bar" [@hamburguerX]="isHamburguer ? 'hamburguer' : 'bottomX'"></div>
</div>

style your bars and cursor

.btn {
  cursor: pointer;
  margin: 20px;
}

.icon-bar {
  width: 22px;
  height: 2px;
  background: black;
  margin: 3px;
  position: relative;
}

Add animation to the those 3 bars to run on click

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('hamburguerX', [
      /*
        state hamburguer => is the regular 3 lines style.
        states topX, hide, and bottomX => used to style the X element
      */
      state('hamburguer', style({})),
      // style top bar to create the X
      state(
        'topX',
        style({
          transform: 'rotate(45deg)',
          transformOrigin: 'left',
          margin: '6px',
        })
      ),
      // hides element when create the X (used in the middle bar)
      state(
        'hide',
        style({
          opacity: 0,
        })
      ),
      // style bottom bar to create the X
      state(
        'bottomX',
        style({
          transform: 'rotate(-45deg)',
          transformOrigin: 'left',
          margin: '6px',
        })
      ),
      transition('* => *', [
        animate('0.2s'), // controls animation speed
      ]),
    ]),
  ],
})
export class AppComponent {
  // flag be consumed by the template
  isHamburguer = true;
  onClick() {
    this.isHamburguer = !this.isHamburguer;
  }
}

Upvotes: 6

Related Questions