Card Circular Slider

In this tutorial, you will learn how to create a circular card slider with CSS and Vue.js. When a card is clicked, it is then flipped to reveal the content of its other side. Watch the video below for a detailed tutorial.


HTML

<body>
  <div id="container" @mouseup="documentMouseUp($event)">
    <div id="cards-container">
      <div id="cards">
        <div class="card default" @mousedown="cardsMouseDown($event)" @mouseup="cardsMouseUp($event)">
          <div class="front"></div>
          <div class="back">
            <img src="joker.png">
          </div>
        </div>
        <div v-for="index in 10" class="card" @mousedown="cardsMouseDown($event)" @mouseup="cardsMouseUp($event)">
          <div class="front"></div>
          <div class="back" v-text="index"></div>
        </div>
      </div>
    </div>
  </div>

  <script>
    var app = new Vue({
      el: '#container',
      data: {
        container: null,
        cardsContainer: null,
        cards: [],
        cardsLength: 0,
        calcDeg: 0,
        extraDeg: 0,
        currentCard: null,
        clickedCardIndex: null,
      },
      mounted:function(){
        this.container = document.getElementById('container')
        this.cardsContainer = document.getElementById('cards-container')
        this.cards = [...document.getElementsByClassName('card')]
        this.cardsLength = this.cards.length
        this.calcDeg = 270/this.cardsLength
        this.currentCard = this.cards[0]

        this.rotationPosition()
      },
      methods: {
        rotationPosition: function(exceptIndex) {
          exceptIndex = exceptIndex | 0;
          var i = 1;

          for (var x = 0; x < this.cardsLength; x++) {
            var card = this.cards[x]
            this.extraDeg = exceptIndex > this.cardsLength/2 ? 360 : 0

            if(x === exceptIndex) {
              card.classList.add('active')
              card.style.transform = 'rotate(' + this.extraDeg + 'deg)'
            } else {
              card.style.transform = 'rotate(' + ( (i * this.calcDeg) + 45 ) + 'deg)'
              i++
            }
          }
        },
        documentMouseUp: function(e) {
          this.clickedCardIndex = null
        },
        cardsMouseDown: function(e) {
          if(this.clickedCardIndex == null && e.which == 1) {
            this.clickedCardIndex = this.cards.indexOf(e.target.parentNode)
          }
        },
        cardsMouseUp: function(e) {
          if(this.clickedCardIndex === this.cards.indexOf(e.target.parentNode)) {
            this.currentCard = e.target.parentNode
            this.pushIndex(this.cards.indexOf(e.target.parentNode))
          }
        },
        pushIndex: function(index) {
          for (var j = 0; j < this.cardsLength; j++) {
            this.cards[j].classList.remove('active')
          }
          this.rotationPosition(index)
        }
      }
    })
  </script>
</body>

CSS

body {
  background: #2e3537;
  margin: 0;
  font-family: 'Roboto', sans-serif;
}

#container {
  height: 100vh;
  text-align: center;
  display: flex;
  justify-content: center;
}

.card {
  width: 74px;
  height: 100px;
  background-color: #0e5d92;
  box-shadow: 0 0 3px 2px rgba(0, 0, 0, 0.1);
  border-radius: 3px;
  position: absolute;
  top: 33%;
  margin-left: -37px;
  color: white;
  padding: 7px;
  box-sizing: border-box;
  cursor: pointer;

  transform-origin: center 175%;
  -webkit-transform-origin: center 175%;
  -ms-transform-origin: center 175%;
  -moz-transform-origin: center 175%;

  transition: transform 0.5s;
  -webkit-transition: transform 0.5s;
  -ms-transition: transform 0.5s;
  -moz-transition: transform 0.5s;
}

.card.default img {
  width: 50px;
  margin-top: 15px;
}

.card .front {
  height: 100%;
  background-image: url('logo.png');
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

.card .front img {
  width: 50px;
}

.card .back {
  display: none;
  text-align: center;
  color: #0e5d92;
  font-size: 30px;
  font-weight: 900;
  line-height: 80px;
}

.card.active .front {
  display: none;
}

.card.active .back {
  display: block;
}

.card.active {
  background-color: #dcdcdd;
}

.card:not(.active):hover {
  background-color: #1c81c5;
}