Cards
The simpler of the two will be the game card. Like your standard playing card, it need a front and a back. The back will have a logo (the Polymer logo), the same on all cards. The front will have text that reflects some rank (in this game, it's basically 1-6 plus 3 special card types).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.game-card { | |
position: relative; | |
transition: transform .5s linear 0s; | |
backface-visibility: hidden; | |
} | |
.game-card .front { | |
transform: rotateY( 180deg); | |
position: absolute; | |
} | |
.game-card.front.flipped { | |
transform: rotateY( 0deg); | |
} | |
.game-card .back { | |
transform: rotateY( 0deg); | |
} | |
.game-card.back.flipped { | |
transform: rotateY( -180deg); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="game-card"> | |
<div id="cardFront" class$="front {{isFlipped(show)}}" style$="width: {{cardWidth}}px; height: {{cardHeight}}px" on-click="flip"> | |
<div class="rank left">{{rank}}</div> | |
<div class="rank right">{{rank}}</div> | |
</div> | |
<div id="cardBack" class$="back {{isFlipped(show)}}" style$="width: {{cardWidth}}px; height: {{cardHeight}}px" on-click="flip"></div> | |
</div> |
That flipped class is applied with Polymer's special attribute data binding, class$="front {{isFlipped(show)}}", which binds the calculation of the class to a function in game-card's script called "isFlipped", that is evaluated every time the value of the attribute "show" changes (internally to game-card, or applied to the element from elsewhere). When the class appears or disappears, the css animation is applied.
For now, an "on-click" event is present to flip the cards as needed.
Stacks
Cards in this game are played from stacks (or piles). Two things I wanted to do for stacks was
- Proportion the cards dimensions
- Fan the cards so I could see them all from top to bottom.
My first step was to make cards that had a respectable shape! Normal playing cards are 64mm x 89mm so we want to keep that proportion. Plus, in the card stacks I want to "fan out" a stack of up to 5 card and still fit in the width of the <card-stack> element (which is always resized with flexbox). A little calculation is needed, and the solution I've used was this (polymer properties removed):
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Polymer({ | |
is: 'card-stack', | |
behaviors: [Polymer.IronResizableBehavior], | |
listeners: { | |
"iron-resize": "resized" | |
}, | |
resized: function() { | |
var height = this.offsetHeight - 2; | |
var stackWidth = this.offsetWidth - 2; | |
if (height > 0) { | |
var calcWidth = height * 64 / 89; | |
if (calcWidth < stackWidth / 2) { | |
//cards will fit when fanned out, 1/4 of width showing for up to 4 cards, then full 5th card | |
this.cardHeight = height - 2; | |
this.cardWidth = calcWidth - 2; | |
} else { | |
//calc width and height to fit inside parent box | |
this.cardHeight = stackWidth / 2 * 89 / 64 - 2; | |
this.cardWidth = stackWidth / 2 - 2; | |
} | |
console.log('For stack ' + stackWidth + 'x' + height + ', setting cards to ' + this.cardWidth + 'x' + this.cardHeight); | |
} | |
} | |
}); |
With Polymer, getting the size of elements (or their parents) is tricky if not done at the right time where all the values are 0. Here I used the IronResizeableBehavior to catch an iron-resize event, and if the height is > 0 (meaning it actually rendered), performed some calculations to preserve the aspect ratio.
Fanning the cards needs to happen from inside the <card-stack>, but outside of <game-card>, so the aspects and behaviors of the cards are independent of the styles applied. Using Polymer's <dom-repeat> template, we can list the cards in an array, and with CSS just stack them on top of each other. This style was very calculated, so this was done with a function bound to three attributes of the <card-stack> (so will be recalculated each time).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template is="dom-repeat" items="{{data}}"> | |
<game-card style$="{{calcMargin(reverse, index, cardWidth)}}" class="card-in-stack" rank="{{item}}" card-width="[[cardWidth]]" card-height="[[cardHeight]]"> | |
</game-card> | |
</template> | |
<style include="shared-styles"> | |
.card-in-stack { | |
position: absolute; | |
} | |
</style> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
calcMargin: function(reverse, index, cardWidth) { | |
//console.log('Calculating the margin for reverse ' + reverse + ' index ' + index + ' width ' + cardWidth); | |
//Margin is 1/4 of a card width for every index, based on fan direction | |
var margin = 'margin-' + (reverse ? 'right' : 'left') + ': ' + Math.floor(index * (cardWidth / 4)) + 'px;'; | |
//z-index is based on index, so stack is correct | |
var zidx = 'z-index: ' + (index + 2) + ';'; | |
//position for absolute edge of card, based on fan direction | |
var position = (reverse ? 'right' : 'left') + ': 0;'; | |
return margin + zidx + position; | |
}, |
Tracking
This step is wrapped up, so I created a branch. https://github.com/chimmelb/polycardwar/tree/step-components
The resizing of cards (and where to bind values in the layout) took about 3 hours, the card flip was about 2, and the fanning elements was about 1. Measuring time is funny, because it really was about a day between meetings, lunch, the other projects that needs attention, etc . . . cramming 6 hours of good work into 9 hours of my day : )
No comments:
Post a Comment