Ionic Swoosh Cards

Demo GitHub

Swoosh Cards is a directive that more smoothly transitions cards forward as they are discarded than previous iterations of Tinder Cards as it solely relies on the collide.js animation engine and does not rely on drag events. The front most card is discarded onclick by a 'discard' button, a fly away animation is triggered on the card, and finally all remaining cards are pushed forward.

Getting started with Swoosh Cards shouldn't differ much from Swoosh Cards or Tinder Cards if you've implemented those modules previously. They both have similar bower requirements, includes, templates, controllers, and CSS styles. But if you're not familiar with it, let's talk about how to get it set up.


Install

Navigate to your project folder and get the Swoosh Card files via Bower. You should then see two modules (collide/ionic-swoosh-cards) in the folder you've specified as being the bower component recipient. Default is bower_components but in this example we've specified the folder lib.

  bower install ionic-swoosh-cards

index.html

Reference ionic.swoosh.cards.css, collide.js, and ionic.swoosh.cards.js in your index.html file.

<!-- swooshcards css -->  
<link rel="stylesheet" href="lib/ionic-swoosh-cards/ionic.swoosh.cards.css">

<!-- collide animation engine -->  
<script src="lib/collide/collide.js"></script>

<!-- swooshcards js -->  
<script src="lib/ionic-swoosh-cards/ionic.swoosh.cards.js"></script>  

Template

In the template, we need to add <swoosh-cards> as the enclosing parent of <swoosh-card>, which is the individual element that will be repeated for every element in $scope.cards. The loop is triggered by the ng attribute... ng-repeat="card in cards".

<ion-pane ng-controller="CardsCtrl">  
  <div ng-if="cards">
    <swoosh-cards spacing="30">
      <swoosh-card ng-repeat="card in cards" on-destroy="cardDestroyed($index)">
        <div ng-controller="CardCtrl">

          <div class="top">
            <img ng-src="{{ card.image }}" />
          </div>
          <div class="bottom">
            <h1>{{ card.country }}</h1>
            <p>{{ card.text }}</p>
            <div class="discard" ng-click="discard(card)">DISCARD</div>
          </div>
        </div>
      </swoosh-card>
    </swoosh-cards>
  </div>
</ion-pane>  

There are four aspects of the template to point out.

The first is the snippet <div ng-if="cards"> enclosing all of our markup. This is included to ensure that the cards are loaded only when $scope.cards is defined...might be useful if you're attaching an array returned from a promise.

The second is the attribute on-destroy="cardDestroyed($index)" which is attached to the <swoosh-card> element and is a remnant of the old Tinder Card module. This was left for those whom wanted to define a callback in CardCtrl to be invoked whenever a card has been destroyed or removed from the card deck.

The third is the ng-click attribute ng-click="discard(card)" attached to the discard button inside the card which is new in this module and is required in order for the directive to know when it should 'fly away' the front-most card and move all succeeding cards forward by a step. If you wish, you can listen on the 'discard' event and specify a callback here rather than as an attribute as referenced in the second blurb above.

The fourth is the spacing attribute on the <swoosh-cards> parent element which accepts a number, is a read-only attribute, and indicates the number of pixels the cards are equally spaced from each other.

Name Scope Options Action
`spacing` @ Number Number of pixels between each card

Controller

This is a sample of a barebones CardsCtrl where we attach an array of items to the scope and define the function cardDestroyed which is attached to the on-destroy attribute of <swoosh-card>. Alternatively, I've included the listener to the discard event and you can specificy what you'd like to happen on card destroy here as well.

There's plenty more that can be added here. Some recommendations might be:

  1. addCard - a function to add cards to the deck
  2. refreshCards - a function to reload the cards onto the view
.controller('CardsCtrl', function($scope, $timeout) {

  $scope.cards = [
    // card 1,
    // card 2,
    // card 3,
    // card 4,
    // card 5,
    // card 6,
    // card 7,
    // card 8,
  ];

  // Removes the card from 
  $scope.cardDestroyed = function(index) {
    var card = $scope.cards.active.splice(index, 1);
  };

  // When a card is destroyed, the swoosh directive broadcasts the event 
  // and this listener enables you to do something when it is heard.
  $scope.$on('discard', function(event, element, card) {

  });

})

CSS

The CSS styles are broken into two components Core and Demo.

All the Core styles are necessary for positioning the cards correctly and ensuring that the animations perform as intended. The styles inside Core can still be tinkered to style them to your liking.

All Demo Styles are for the content inside the card only and means you can do whatever you like with them...they won't change anything within the directive.

/* CORE */
swoosh-cards {  
  display: block;
}

swoosh-card {  
  position: absolute;
  top: calc(50% - 175px);
  left: calc(50% - 100px);
  width: 200px;
  height: 350px;
  border-radius: 6px;
  background-color: #FFF;
  overflow: hidden;
}

swoosh-card .overlay {  
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #333;
  opacity: 0;
  z-index: 11;
}

swoosh-card .content {  
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 10;
}

/* DEMO */
swoosh-card .top {  
  width: 100%;
  height: 160px;
  border-radius: 4px 4px 0 0;
  overflow: hidden;
}

swoosh-card img {  
  width: 100%;
}

swoosh-card .bottom {  
  border-radius: 0 0 4px 4px;
}

swoosh-card .bottom h1 {  
  margin: 10px 0 0 0;
  font-size: 14px;
  text-align: center;
}

swoosh-card .bottom p {  
  margin: 10px 15px;
  font-size: 11px;
}

.discard {
  position: absolute;
  left: calc(50% - 35px);
  bottom: 10px;
  padding: 7px 10px;
  color: #FFF;
  font-weight: bold;
  font-size: 12px;
  background-color: rgba(0, 0, 0, 0.7);
  border-radius: 20px 20px 20px 20px;
}

.background-grey {
  background-color: #333 !important;
}

That wraps up the demo. If you have any questions, post them as an issue in the Github repo. Thanks!