PDA

View Full Version : Experimental card flip transition - nearly working



murrah
1 Feb 2014, 3:42 PM
Hi,

I am experimenting with trying build a card layout that flips between two cards.

This example gave me a start: http://bannockburn.io/2013/06/flipping-an-extjs-panel-with-css3/

So, starting from there I modified it so that it becomes a component - eventually a card layout extension. I have it almost working. The card flips and the front becomes reversed but the back card remains invisible. It has a higher z-order and looking at the computed css I cant see why it wouldn't be displaying above the front card.

See demo here (use Chrome): http://www.murrah.com.au/fliptest/
My experiment is focussing on webkit only (just to simplify the testing) and I am keeping the css classes lean and without inheritance for testing. And, of course, all the render handlers will be hidden, etc. Once I get this working with "ordinary" panels I intend to try to make a card layout version in a similar vein to http://www.sencha.com/forum/showthread.php?266273-Card-Layout-Crossfade-Override-(ExtJS-4.2) which is great.

If I remove the
-webkit-backface-visibility: hidden; from the .flipface class (see css below) the back card does become visible and is reversed, and the flip button does flip it. It is just that I am only able to flip the front card, or the back card, but not both back-to-back.

Any help would be greatly appreciated!

Thanks,
Murray

Ext.onReady(function () {

Ext.create('Ext.panel.Panel', {
id: 'myCmp',
renderTo: 'mycontainer',
height: 300,
width: 300,

currentItem: 0,

dockedItems: [{
xtype: 'toolbar',
dock: 'top',
items: [{
text: 'Flip me',
handler: function (btn) {
var panel = btn.up('panel')
++panel.currentItem;

if (panel.currentItem > 1) panel.currentItem = 0;

(panel.currentItem == 1) ? panel.addBodyCls('flipped') : panel.removeBodyCls('flipped');

}
}]
}],
items: [{
id: 'frontcard',
height: 300,
bodyStyle: {"background-color": "aqua"},
html: '<h1>Front</h1>',
listeners: {
'render': function (panel) {
panel.getEl().addCls('flipface flipfront');
}
}
}, {
id: 'backcard',
height: 300,
bodyStyle: {"background-color": "yellow"},
html: '<h1>Back</h1>',
listeners: {
'render': function (panel) {
panel.getEl().addCls('flipface flipback');
}
}
}],
listeners: {
'render': function (panel) {
panel.addCls('flipcontainer');
panel.addBodyCls('flipcard');
}
}
});
});


/* on the container panel element */
.flipcontainer {
-webkit-perspective: 1000;
}

/* on the container panel body element */
.flipcard {
width: 100%;
height: 100%;
-webkit-transform-style: preserve-3d;
-webkit-transition: 0.9s;
}

/* common to both cards */
.flipface {
-webkit-backface-visibility: hidden;
z-index: 2;
text-align: center;
}

/* front card */
.flipfront {
position: absolute;
z-index: 1;
}

/* back card */
.flipback {
-webkit-transform: rotateY(180deg);
}

/* The class that gets added / removed from the container panel body
in response to the button click to make the cards 'flip' */
.flipped {
-webkit-transform: rotateY(180deg);
}


<html>
<head>
<title>Card Flip Test</title>

<link rel="stylesheet" type="text/css" href="http://cdn.sencha.com/ext/gpl/4.2.1/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="flip.css" />
<script type="text/javascript" src="http://cdn.sencha.com/ext/gpl/4.2.1/ext-all.js"></script>
<script type="text/javascript" src="flip.js"></script>
</head>
<body style="padding:40px">
Click the Flip button to toggle flip the panel
<div id="mycontainer"></div>
</body>
</html>

murrah
2 Feb 2014, 1:36 PM
OK... Some progress. Flipping works now.
http://www.murrah.com.au/fliptest/

One issue remains, see below.

There were two problems preventing the back card from appearing:
1. The class selector for the flipped class needed to be:
.flipcontainer .flipcard.flipped {
-webkit-transform: rotateY(180deg);
} ie the inheritance path is necessary and notice the lack of a space between the class names: .flipcard.flipped

2. I was putting the flipcard class on the wrong div in the dom. It needed to be the container panel body inner-Ct

Revised js and css below.

One remaining issue. When the cards flip, the 3d perspective is present but the container panel is clipping the top and bottom of the cards so you lose some of the effect. Watch the demo (http://www.murrah.com.au/fliptest/) and you will notice that the flipping cards do not extend above and below the panel container.

As an experiment I added 10px margins to the cards so they had space between the cards and their container panel body and the 3d effect "worked" in that the cards didnt get clipped because they didnt intersect with the panel body. So, it looks like they are "behind" the panel. I experimented with manipulating the dom directly but couldnt get them above the panel (if that is indeed the problem).

Anyway, at least I have a useable flip effect. Now on to getting this effect with a card layout and setActiveItem().

Cheers,
Murray


/* on the container panel element */
.flipcontainer {
-webkit-perspective: 1000;
}

/* on the container panel body element */
.flipcard {
width: 100%;
height: 100%;
-webkit-transform-style: preserve-3d;
-webkit-transition: 0.9s;
}

/* common to both cards */
.flipface {
-webkit-backface-visibility: hidden;
z-index: 2;
text-align: center;
}

/* front card */
.flipfront {
position: absolute;
z-index: 1;
}

/* back card */
.flipback {
-webkit-transform: rotateY(180deg);
}

/* The class that gets added / removed from the container panel body inner-ct
in response to the button click to make both the cards 'flip'*/
.flipcontainer .flipcard.flipped {
-webkit-transform: rotateY(180deg);
}


Ext.onReady(function () {

Ext.create('Ext.panel.Panel', {
id: 'myCmp',
renderTo: 'mycontainer',
height: 300,
width: 300,

currentItem: 0,

dockedItems: [{
xtype: 'toolbar',
dock: 'top',
items: [{
text: 'Flip me',

// Handle the flip button click
handler: function (btn) {
// Get a ref to the container panel body inner-Ct div
var panel = btn.up('panel'),
cardEl = panel.body.getById('myCmp-innerCt').el;

// Change the card number ( 0 || 1 )
++panel.currentItem;
if (panel.currentItem > 1) panel.currentItem = 0;

// If the front card is showing, add the class.
// If the back is showing, remove the class.
// Remember that the back card was initialised in it's reversed state by the
// css so when the flipped class is added to the back card
// it is flipping from reversed to "normal"
(panel.currentItem == 1) ? cardEl.addCls('flipped') : cardEl.removeCls('flipped');

}
}]
}],
items: [
// The front card panel
{
id: 'frontcard',
height: 300,
bodyStyle: {
"background-color": "aqua"
},
html: '<h1>Front</h1>',
listeners: {
'render': function (panel) {
panel.getEl().addCls('flipface flipfront');
}
}
},
// The back card panel
{
id: 'backcard',
height: 300,
bodyStyle: {
"background-color": "yellow"
},
html: '<h1>Back</h1>',
listeners: {
'render': function (panel) {
panel.getEl().addCls('flipface flipback');
}
}
}
],

listeners: {
// Add the flipcontainer class to the panel body
// and the flipcard class to the panel body inner-Ct
'render': function (panel) {
panel.addBodyCls('flipcontainer');
var cardEl = panel.body.getById('myCmp-innerCt').el;
cardEl.addCls('flipcard');
}
}
});
});