3D Card Previewer

Alt Text

Introduction

I’ve been working on a movie review based site and I wanted to try and create a movie previewer. I’m incorporating CSS3 transform’s perspective and rotation. The previewer consists of a list of 3D Card movie posters tilted to the right and one featured movie blown up full size. You can switch in between featured movies by clicking on each movie poster.

3D Card Structure

Each card needs a card-container that provides the perspective and perspective origin. You can edit these properties to change the look of the perspective tilt.

<div class="container">
  <div class="card-container">
    <div class="card">
      <a href=""><img src="img/interstellar.jpg"></a>
    </div>
  </div>
  <div class="card-container">
    <div class="card">
      <a href=""><img src="img/imitation_game.jpg"></a>
    </div>
  </div>
  <div class="card-container">
    <div class="card">
      <a href=""><img src="img/hobbit.jpg"></a>
    </div>
  </div>
  <div class="card-container">
    <div class="card">
      <a href=""><img src="img/boxtrolls.jpg"></a>
    </div>
  </div>
  <div class="card-container active">
    <div class="card">
      <a href=""><img src="img/maze_runner.jpg"></a>
    </div>
  </div>
  <div class="clearfix"></div>
</div>

3D Card Styles

The 3D effect is achieved with a transform: rotateY of 30 degrees.

.card-container {
  position: relative;
  width: 70px;
  margin-top: 35px;

  -webkit-perspective: 800px;
  perspective: 800px;

  -webkit-perspective-origin: 0 50%;
  perspective-origin: 0% 50%;

  -webkit-transition: .4s;  
  transition: .4s;  
}
  .card {
    position: relative;
    width: 250px;
    margin-bottom: 10px;
    overflow: hidden;
    line-height: 0px;

    border-radius: 6px;
    -moz-border-radius: 6px;
    -webkit-border-radius: 6px;

    -webkit-transition: .4s;
    transition: .4s;
  }

    .card img { width: 100%; }
    .card a { margin: 0; }

    .card-container {
      float: left;
      left: 0;
    }
      .card-container:first-child .card {
        -webkit-box-shadow: -10px 10px 20px 0 rgba(0,0,0,.1);
        -moz-box-shadow: -10px 10px 20px 0 rgba(0,0,0,.1);
        box-shadow: -10px 10px 20px 0 rgba(0,0,0,.1);
      }
      .card-container .card {
        -webkit-transform: rotateY( 30deg );
        transform: rotateY( 30deg );

        -webkit-box-shadow: -10px 10px 20px 0 rgba(0,0,0,.4);
        -moz-box-shadow: -10px 10px 20px 0 rgba(0,0,0,.4);
        box-shadow: -10px 10px 20px 0 rgba(0,0,0,.4);
      }

    /* Active */
    .card-container.active {
      z-index: 1;
      margin: 0; 
      left: 60%;
    }
      .card-container.active .card {
        width: 300px;
        -webkit-transform: rotateY( 0deg );
        transform: rotateY( 0deg );
        -webkit-box-shadow: 0 10px 20px 0 rgba(0,0,0,.4);
        -moz-box-shadow: 0 10px 20px 0 rgba(0,0,0,.4);
        box-shadow: 0 10px 20px 0 rgba(0,0,0,.4);
      }

jQuery

The jQuery here simply toggles active classes on each card. The trickiest part is having to rebalance the active card because the offset changes based on where the card is.

var activeWidth = $('.card-container').width();
var activeImgWidth = $('.card-container .card a img').width();
function balanceCards() {
  // Calc offset for active card
  var index = $('.card-container').index($('.active'));
  var offset = -1 * ((index * activeWidth) + (activeImgWidth / 2));
  console.log($('.card-container.active .card img').width() / 2);

  $('.card-container.active').css({ "margin-left": offset + "px"});
}

balanceCards();

$('.card a').click(function (e) {
  e.preventDefault();
  $(this).parents('.card-container').siblings().not('.' + $(this).attr("class")).removeClass("active").css({ "margin-left": "0px"});
  $(this).parents('.card-container').addClass("active");
  balanceCards();
});