Maurice
Maurice

Reputation: 1157

How to add a simple card flip animation?

I am creating a solitary game. So far I got most things working, but the card flip animation is a pain in the a**.

See this fiddle (it might be a bit slow since the whole game code is in it)

When you drag and drop a card, the back of the card is replaced with a front. This is done in this part of the code:

upturn: function () {
            with(this) {
                if (is_downturned()) {
                    element.children('.downturned')
                        .removeClass('downturned').addClass(_SUITS[_suit()].color)
                        .append('<img height="80px" width="50px" border="0" src="http://mauricederegt.com/test/solitaire/cards/' + _RANKS[_rank()] + '' + _SUITS[_suit()].symbol + '.jpg">') //NEW
                    element.addClass(_scope());
                    if (element.hasClass('ui-draggable-disabled')) {
                        element.draggable('enable');
                    } else {
                        element.draggable({
                            containment: '#field',
                            revert: 'invalid',
                            revertDuration: 200,
                            zIndex: 99
                        });
                    }
                    if (!element.hasClass('ui-droppable-disabled')) {
                        element.droppable($.extend(DROPPABLE_OPTIONS, {
                            accept: _tableau_pile_scope()
                        }));
                    }
                }
            }
        }

Basically it removes the back of the card (.removeClass('downturned')) and adds a new class (.addClass(_SUITS[_suit()].color)) where suits are the hart, clover etc and the color is black or red.

Now when this action happens, I want to add a nice flipping animation.

I have played around with some tutorials on the web, but non gave me a working solution (mostly the cards starting to act weird or the game stopped working).

The closest I could get was adding:

-webkit-perspective: 600px;
-webkit-transform-style: preserve-3d;
-webkit-transform: rotateY(180deg);

/* -- transition is the magic sauce for animation -- */
-webkit-transition: 0.6s;

in the CSS in the class:

.red,
.black {
  cursor: pointer;
  <<<code added here>>>
}

This, however, resulted in some weird actions (though the card is flipping):

  1. Card flips, but after the image/front is already shown
  2. All cards are mirrored
  3. The drag/handle area of the cards is way off

So how can I add a nice card flipping animation? Hope someone can help me out.

EDIT:

By changing this.element.addClass('container').append('<div class="downturned">'); to this.element.addClass('container').append('<div class="downturned">').append('<div class="hiddencardfront">'); in the JS part, I can create an extra empty div, (which I think I need for the card flip animation), but I still can't make it work

Upvotes: 4

Views: 9478

Answers (4)

mfirdaus
mfirdaus

Reputation: 4592

I guess there are a couple of ways of doing it. But I guess I'm to suggest the old-ish way of doing it using only one element. Essentially since it's a 2D object, the front and back will never show at the same time and there is going to be a point where the card will "disappear". So we can split the animation into two. "Hiding" the card (Here we can animate the width to zero). Changing the image to the other card, then "showing" the card again (animate from zero to the original width again). So if we use jQuery, with an html of

<img class="card" src="http://mauricederegt.com/test/solitaire/cards/back.jpg" />

the jQuery will look something like:

var CARDWIDTH = 50;
function turnCompatible(elem, src) {
    $(elem).animate({
        width: 0, //animate to zero
        marginLeft: CARDWIDTH / 2,
        marginRight: CARDWIDTH / 2
    }, function () {
        this.src = src //change the image
        $(this).animate({
            width: CARDWIDTH, //show the image again
            marginLeft: 0,
            marginRight: 0,
        })
    })
}
$(".card").click(function(){
   turnCompatible(this, src)
})

And this will more or less work. Though the animation could be better improved for example with CSS transforms and animations but this solution would be compatible with browsers that don't support CSS transforms (mostly IE. IE<10). And with that you can do something like

function turnCSS(elem, src) {
    $(elem)
        .addClass("flipping")
        .bind("transitionend webkittransitionend", function () { //should add more prefixes
        this.src = src;
        $(this)
            .unbind("transitionend webkittransitionend")
            .removeClass("flipping")
    })
}

with the following CSS

.card {
    width:50px;
    height:80px;
    -webkit-transition:-webkit-transform 0.5s;
    transition:transform 0.5s;
    background:#FF0;
}
.flipping {
    transform: translate(0, 20px) rotateY(90deg);
    -webkit-transform: translate(0, 20px) rotateY(90deg);
}

Demo

So perhaps the look is not what you're looking for but it is possible to do the flip animation with just one element.

Upvotes: 3

Maurice
Maurice

Reputation: 1157

I think the big problem I am having is that when this was build, no flip animation was considered. So there is just 1 Div, where more are needed to make it work. Also the drag and drop code used is old and interrups with the animation code.

I guess the only solution is to start over from scratch, with animations in mind this time.

All thanks for helping and given solution, but starting over is the way to go I think

Greetings

Upvotes: 1

tnt-rox
tnt-rox

Reputation: 5548

Regarding your three issues with CSS 3d transforms.

  1. Card flips, but after the image/front is already shown
  2. All cards are mirrored
  3. The drag/handle area of the cards is way off

For points 1 & 2 add:

-webkit-backface-visibility:hidden; /* Chrome, Safari, Opera */
backface-visibility:hidden;

And to center your element use:

transform-origin:50% 50%;
-ms-transform-origin:50% 50%; /* IE 9 */
-webkit-transform-origin:50% 50%; /* Chrome, Safari, Opera */

Upvotes: 0

Mr. A
Mr. A

Reputation: 1231

Try this

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<style>
/* Chrome, Safari, Opera */
@-webkit-keyframes myfirst1 {
      from {
        -webkit-transform : rotateY(180deg);
        -moz-transform : rotateY(180deg);
        -ms-transform : rotateY(180deg);
        transform: rotateY(180deg);}
      to {
        -webkit-transform : rotateY(90deg);
        -moz-transform : rotateY(90deg);
        -ms-transform : rotateY(90deg);
        transform: rotateY(90deg);}
}
@-webkit-keyframes myfirst2 {
      from {
        -webkit-transform : rotateY(90deg);
        -moz-transform : rotateY(90deg);
        -ms-transform : rotateY(90deg);
        transform: rotateY(90deg);}
      to {
        -webkit-transform : rotateY(0deg);
        -moz-transform : rotateY(0deg);
        -ms-transform : rotateY(0deg);
        transform: rotateY(0deg);}
}

/* Standard syntax */
@keyframes myfirst1 {
      from {
        -webkit-transform : rotateY(180deg);
        -moz-transform : rotateY(180deg);
        -ms-transform : rotateY(180deg);
        transform: rotateY(180deg);}
      to {
        -webkit-transform : rotateY(90deg);
        -moz-transform : rotateY(90deg);
        -ms-transform : rotateY(90deg);
        transform: rotateY(90deg);}
}
@keyframes myfirst2 {
      from {
        -webkit-transform : rotateY(90deg);
        -moz-transform : rotateY(90deg);
        -ms-transform : rotateY(90deg);
        transform: rotateY(90deg);}
      to {
        -webkit-transform : rotateY(0deg);
        -moz-transform : rotateY(0deg);
        -ms-transform : rotateY(0deg);
        transform: rotateY(0deg);}
}
@-webkit-keyframes mylast1 {
    from {
        -webkit-transform : rotateY(0deg);
        -moz-transform : rotateY(0deg);
        -ms-transform : rotateY(0deg);
        transform: rotateY(0deg);}
    to {
        -webkit-transform : rotateY(90deg);
        -moz-transform : rotateY(90deg);
        -ms-transform : rotateY(90deg);
        transform: rotateY(90deg);}
}
@-webkit-keyframes mylast2 {
    from {
        -webkit-transform : rotateY(90deg);
        -moz-transform : rotateY(90deg);
        -ms-transform : rotateY(90deg);
        transform: rotateY(90deg);}
    to {
        -webkit-transform : rotateY(180deg);
        -moz-transform : rotateY(180deg);
        -ms-transform : rotateY(180deg);
        transform: rotateY(180deg);}
}
@keyframes mylast1 {
   from {
        -webkit-transform : rotateY(0deg);
        -moz-transform : rotateY(0deg);
        -ms-transform : rotateY(0deg);
        transform: rotateY(0deg);}
    to {
        -webkit-transform : rotateY(90deg);
        -moz-transform : rotateY(90deg);
        -ms-transform : rotateY(90deg);
        transform: rotateY(90deg);}
}
@keyframes mylast2 {
   from {
        -webkit-transform : rotateY(90deg);
        -moz-transform : rotateY(90deg);
        -ms-transform : rotateY(90deg);
        transform: rotateY(90deg);}
    to {
        -webkit-transform : rotateY(180deg);
        -moz-transform : rotateY(180deg);
        -ms-transform : rotateY(180deg);
        transform: rotateY(180deg);}
}

</style>
<script>
$(document).ready(function()
  {
  $("#btn2").hide();
  $("#btn1").click(function(){
    $("#btn1").hide();
    $("#box1").css({  'z-index':'2', 
    '-webkit-animation': 'myfirst1 5s',
    'animation': 'myfirst1 5s'
    });

    $("#box2").css({ 'z-index':'0' ,
    '-webkit-animation': 'mylast1 5s',
    'animation': 'mylast1 5s'
    });
    setTimeout(function(){
      $("#box1").css({  'z-index':'0', 
      '-webkit-animation': 'myfirst2 5s',
      'animation': 'myfirst2 5s'
      });

      $("#box2").css({ 'z-index':'2' ,
      '-webkit-animation': 'mylast2 5s',
      'animation': 'mylast2 5s'
      });
    }, 5000);
    setTimeout(function(){$("#btn2").show();}, 10000);
  });

  $("#btn2").click(function(){ 
    $("#btn2").hide();   
    $("#box2").css({ 'z-index':'2', 
    '-webkit-animation': 'myfirst1 5s',
    'animation': 'myfirst1 5s'
    });

    $("#box1").css({  'z-index':'0',
    '-webkit-animation': 'mylast1 5s',
    'animation': 'mylast1 5s'
    });
    setTimeout(function(){
     $("#box2").css({ 'z-index':'0', 
     '-webkit-animation': 'myfirst2 5s',
     'animation': 'myfirst2 5s'
     });

     $("#box1").css({  'z-index':'2',
     '-webkit-animation': 'mylast2 5s',
     'animation': 'mylast2 5s'
     });
    }, 5000);
    setTimeout(function(){$("#btn1").show()}, 10000);
  });
});

</script>
</head>
<body>

<button id="btn1">flip</button>
<button id="btn2">Reset</button>
<div id="box1" style="position:fixed;background:#98bf21;height:100px;width:100px;margin:6px;">
</div><div id="box2" style="position:fixed;background:#bf2198;height:100px;width:100px;margin:6px;">
</div>

</body>
</html>

In this I have just animated two div kept back to back (you can replace it with necessary images,set positions and put things into function to reduce code. I have done this specially for you. so I didn't optimize it hence I ask you to customize to soot your requirement.

Upvotes: 0

Related Questions