How to implement a flip effect using Anuglar 2 animations?

14,918

Solution 1

this demo using animation in Angular

import { Component, OnInit, trigger, state, style, transition, animate } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <div class="tp-wrapper">
      <div class="tp-box" (click)="toggleFlip()" [@flipState]="flip">
        <div class="tp-box__side tp-box__front">Front
        </div>
        <div class="tp-box__side tp-box__back">Back
        </div>
      </div>
    </div>
  `,
  styles: [
    `
    .tp-wrapper {
      perspective: 800px;
    }

    .tp-box {
      position: relative;
      width: 200px;
      height: 100px;
      margin: 3rem auto;
      transform-style: preserve-3d;
      transition: transform 1s;
    }
    .tp-box__side {
      width: 100%;
      height: 100%;
      position: absolute;
      backface-visibility: hidden;
      color: #fff;
      text-align: center;
      line-height: 100px;
      font-size: 24px;
      font-weight: 700;
      cursor: pointer;
      user-select: none;
    }
    .tp-box__front {
      background: #f30d36;
    }
    .tp-box__back {
      background: #23262d;
      transform: rotateY(179.9deg);
    }

    `
  ],
  animations: [
    trigger('flipState', [
      state('active', style({
        transform: 'rotateY(179.9deg)'
      })),
      state('inactive', style({
        transform: 'rotateY(0)'
      })),
      transition('active => inactive', animate('500ms ease-out')),
      transition('inactive => active', animate('500ms ease-in'))
    ])  
  ]
})
export class AppComponent {

  flip: string = 'inactive';
  constructor() {}

  toggleFlip() {
    this.flip = (this.flip == 'inactive') ? 'active' : 'inactive';
  }

}

Online demo: https://plnkr.co/edit/RZ1v9M?p=preview

Solution 2

This works great in Chrome, but in Safari/Firefox/Opera the flipped side content shows through (in reverse) when the front side is visible, and vice-versa, but you just need to add the browser specific backface-visibility styles.

So replace this:

 backface-visibility: hidden;

with this:

-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-o-backface-visibility: hidden;
backface-visibility: hidden;

Then it displays in all browsers correctly.

Share:
14,918
Tomasz Cysewski
Author by

Tomasz Cysewski

Updated on June 30, 2022

Comments

  • Tomasz Cysewski
    Tomasz Cysewski almost 2 years

    I have been using pure css flip of cards in my project but this solution is not proper one. Can somebody present a flip in angular 2 on click of a button? I have found one in angularjs https://codepen.io/Zbeyer/pen/oXQrZg

        <div ng-app="cardFlipper" ng-controller="AppController">    
        <h1>Card Flipping AngularJS</h1>
        <div class="flip-container">
            <div class="flipper" ng-click="flipCard()" ng-class="{'flipToFront':isCardRevealed, 'flipToBack':!isCardRevealed}">
                <div class="back" ng-class="{'face-hidden':hideBackFace}">
                </div>
                <div class="front" ng-class="{'face-hidden':!hideBackFace}">
                    <h1>{{currentCard.title | uppercase}}</h1>
                    <p ng-if="currentCard.icon">{{currentCard.icon}}</p>
                    <br ng-if="currentCard.icon" />
                    <img ng-if="currentCard.imageUrl" src="{{currentCard.imageUrl}}" alt="currentCard.imageAlt" />
                    <p>{{currentCard.description}}</p>
                </div>
            </div>
        </div>
    
        <footer>
            <div ng-if="currentCard && currentCard.source">
                <a ng-href="{{currentCard.source}}" target="_blank">Source</a>
            </div>
        </footer>
        <br />
        <br />
    
    
    </div>
    <style>
        /* CARD DIMENSIONS */
    @width: 19em;
    @height: 27em;
    @shadow:1em 1em 2em #111111;
    /* MIXINS */
    .box-shadow (@string:@shadow) {
        -webkit-box-shadow: @string;
        -moz-box-shadow:    @string;
        box-shadow:         @string;
    }
    
    
    .box-sizing(@sizing:border-box) {
        -webkit-box-sizing: @sizing;
        -moz-box-sizing: @sizing;
        box-sizing: @sizing;
    }
    
    .border-radius (@radius: 0.5em) {
        -webkit-border-radius: @radius;
        -moz-border-radius: @radius;
        border-radius: @radius;
    }
    
    .top-perspective() {
            /*perspective*/
        -webkit-perspective:1000;
           -moz-perspective:1000;
            -ms-perspective:1000;
             -o-perspective:1000;
                perspective:1000;
    
    
    }
    
     .flipped-transform-back () {
        /*transform*/
        -webkit-transform:rotateY(180deg);
           -moz-transform:rotateY(180deg);
            -ms-transform:rotateY(180deg);
             -o-transform:rotateY(180deg);
                transform:rotateY(180deg);
    }
    
    .flipped-transform-front {
         -webkit-transform: rotateY(0deg);
            -moz-transform: rotateY(0deg);
              -o-transform: rotateY(0deg);
             -ms-transform: rotateY(0deg);
                 transform: rotateY(0deg);
    }
    
    .flipper-transform(@transition: 0.4s) {
        -webkit-transform-style: preserve-3d;
        -moz-transform-style: preserve-3d;
        -o-transform-style: preserve-3d;
        -ms-transform-style: preserve-3d;
        transform-style: preserve-3d;
    
        -webkit-transition: @transition;
        -moz-transition: @transition;
        -o-transition: @transition;
        -ms-transition: @transition;
        transition: @transition;
    }
    
    .back-face-should-be-hidden() {
    
        -webkit-backface-visibility: hidden;
        -moz-backface-visibility: hidden;
        -ms-backface-visibility: hidden;
        backface-visibility: hidden;
    }
    
    /* STYLE SHEET */
    
    body {
        font-family: "myriad-pro", sans-serif;
        font-size:100%;
        text-align:center;
    
        color:#D1D1D1;
    
        padding: 0;
        background-color:#333333;
    
        margin:0 auto;
        padding: 0;
    }
    
    footer {
        text-align:center;
        padding:1em;
        a {
            font-size:1em;
            color:RGB(255, 208, 128);
        }
    }
    
    
    .flip-container {
        display:block;
        margin:0 auto;
        width: @width * 1.0;
        height: @height *1.0;
    }   
    
    .flipToFront {
        .flipped-transform-front();
    }
    
    .flipToBack {
        .flipped-transform-back();
    }
    
    /* flip speed goes here */
    .flipper {
        .top-perspective();
        .flipper-transform();
        width:100%;
        height:100%;
        position:relative;
    }
    
    .flip-container, .front, .back {
        .back-face-should-be-hidden();
    
        text-align:center;
        color:#333333;        
        padding: 0.25em;
    
        h1, h2, h3, a {
            font-size: 1.25em;
        }
    }
    
    .face-hidden {
    
        /*
            .face-should-be-hidden();
        */
    }
    
    .front, .back {
        /* hide back of pane during swap */
        overflow:scroll;
    
        display:inline-block;
    
        /* Card overlay eachother */
        position: absolute;
        top: 0;
        left: 0;
    
        /* Make Pretty */
    
        .box-sizing();
        .border-radius();    
        .box-shadow();
        width:100%;
        height:100%;
    }
    
    /* front pane, placed above back */
    .front {
        text-align:center;
        z-index: 2;
        background-color:#FEFEFE;
        .flipped-transform-front ();    
        .box-sizing();
         border:0.5em solid #FEFEFE;
        img {
            width:100%;
            margin: 0 auto;
            height:auto;
            .border-radius();
        }
    }
    
    /* back, initially hidden pane */
    .back {
        background-color:#EBEBEB;
        .flipped-transform-back ();
    
        background-image: url('http://subtlepatterns.com/patterns/upfeathers.png');
        background-position: center;
        background-repeat: repeat;
        .box-sizing();
         border:1em solid #FEFEFE;
    
    
    }
    
    /* Media Queries */
    
    /*
    @highdensity: ~"only screen and (-webkit-min-device-pixel-ratio: 1.5)",
                  ~"only screen and (min--moz-device-pixel-ratio: 1.5)",
                  ~"only screen and (-o-min-device-pixel-ratio: 3/2)",
                  ~"only screen and (min-device-pixel-ratio: 1.5)";
    */
    
    @mobile:      ~"only screen and (max-width: 34em)";
    @tablet:      ~"only screen and (min-width: 34em) and (max-width: 55em)";
    @desktop:     ~"only screen and (min-width: 55em)";
    
    
    @media @mobile {
        h1, h2, h3 {
            font-size: 1.25em;
        }
    
        .flip-container, .front, .back {
            width: @width * 1.0;
            height: @height *1.0;
        }
    }
    /*
    @media @tablet {
            .flip-container, .front, .back {
            width: @width * 1.25;
            height: @height *1.25;
    
            h1, h2, h3 {
                font-size: 1.75em;
            }
        }
    }
    
    @media @desktop {
        .flip-container, .front, .back {
            width: @width * 1.5;
            height: @height *1.5;
    
        }
    }*/
    
    <script>
        angular.module('cardFlipper', [])
    .controller('AppController', ['$scope', '$interval', function($scope, $interval) {
    
        $scope.cards = [
        {
            title: "escheresque-dark",
            icon:"",
            imageUrl:"http://subtlepatterns.com/patterns/escheresque_ste.png",
            description:"Sublte Pattern Source image below...",
            source: "http://subtlepatterns.com/escheresque-dark/"
        },
        {
            title: "dark sharp edges",
            icon:"",
            imageUrl:"http://subtlepatterns.com/patterns/footer_lodyas.png",
            description:"Sublte Pattern Source image below...",
            source: "http://subtlepatterns.com/dark-sharp-edges/"
        },
        {
            title: "Grey Washed Wall",
            icon:"",
            imageUrl:"http://subtlepatterns.com/patterns/grey_wash_wall.png",
            description:"Sublte Pattern Source image below...",
            source: "http://subtlepatterns.com/grey-washed-wall/"
        }
    ];
        $scope.currentCard = {};
    
        $scope.isCardRevealed = false;
        $scope.flipCard = function() {
            $scope.isCardRevealed = !$scope.isCardRevealed;
            if($scope.isCardRevealed) {
                $scope.generateCard();
            } else {
    
                $scope.currentCard = {};
                /*            setTimeout(function() {
    //                $scope.isBackHidden = !$scope.isCardRevealed;
                }, 0.1 * 1000);
    */            
            }
            /*
    
    
    
            */
        }
    
        $scope.generateCard = function() {
            $scope.currentCard = {};
            var index = Math.floor((Math.random() * $scope.cards.length) + 0);
             $scope.currentCard = $scope.cards[index];
        }
    
    }]);
    </script>
    

    Can somebody turn it into angular2 or implement something different?

  • Gustavo
    Gustavo over 6 years
    It's not working on Mozzila, how can I make it work?
  • Malindu
    Malindu over 6 years
    for me, there is an odd behavior! stackblitz.com/edit/angular-card-flip. Could u please look into this?
  • Malindu
    Malindu over 6 years
    Checked it on Chrome, Safari, Firefox. Seems to like having a problem with Firefox.
  • Malindu
    Malindu over 6 years
    Tried @stephen-r-smith answer but still no luck.
  • Roger
    Roger over 5 years
    @Warapitiya Thanks for the link to stackblitz.com/edit/angular-card-flip. I made some minor style changes and it worked great for me! Tested fine on Chrome, Edge and Firefox v.61.0.2.
  • Roger
    Roger over 5 years
    This looks very similar, if not identical, to this example: stackblitz.com/edit/angular-card-flip, which works great for me (the Plunkr refuses to run).
  • Pooja Bansal
    Pooja Bansal over 5 years
    Still not able to flip it right in IE11. Facing same error while flipping and it shows reverse few of front box.