Swipeable Cards

In this example below you will see how to do a Swipeable Cards with some HTML / CSS and Javascript

Tinder like card swiping components made with React

Thumbnail
This awesome code was written by 025pro, you can see more from this user in the personal repository.
You can find the original code on Codepen.io
Copyright 025pro ©

Technologies

  • HTML
  • CSS
  • JavaScript
<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  <title>Swipeable Cards</title>
  
  
  <link rel='stylesheet prefetch' href='https://cdnjs.cloudflare.com/ajax/libs/materialize/0.98.2/css/materialize.min.css'>

      <link rel="stylesheet" href="css/style.css">

  
</head>

<body>

  <div id="root">
</div>
  <script src='https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js'></script>

  

    <script  src="js/index.js"></script>




</body>

</html>

/*Downloaded from https://www.codeseek.co/025pro/swipeable-cards-jGzjrK */
@charset "UTF-8";
#root {
  display: flex;
  justify-content: center;
  align-items: center;
}

.card {
  margin: 0;
}

.Card {
  width: 300px;
  box-shadow: 0 0 9px 1px rgba(0, 0, 0, 0.3);
  cursor: pointer;
}

.card:active {
  box-shadow: 0 0 15px 3px rgba(0, 0, 0, 0.3);
}

.card-image:before {
  content: ' ';
  position: absolute;
  top: 0;
  left: 0;
  z-index: 555;
  width: 100%;
  height: 100%;
  display: block;
}

.cardWrapper {
  position: relative;
  z-index: 999999;
}

.backCard {
  position: absolute;
}
.backCard .glower {
  width: 100%;
  height: 100%;
  background-color: white;
  opacity: 1;
  position: absolute;
  z-index: 200;
}

img {
  height: 200px;
}

.SwipableCards {
  width: 500px;
  height: 600px;
  background-color: #ccc;
  overflow: hidden;
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
}
.SwipableCards * {
  -webkit-touch-callout: none;
  /* iOS Safari */
  -webkit-user-select: none;
  /* Safari */
  -khtml-user-select: none;
  /* Konqueror HTML */
  -moz-user-select: none;
  /* Firefox */
  -ms-user-select: none;
  /* Internet Explorer/Edge */
  user-select: none;
  /* Non-prefixed version, currently
             supported by Chrome and Opera */
}

.result {
  position: absolute;
  left: calc(50% - 25px);
  top: calc(50% - 25px);
  font-size: 50px;
  opacity: 0;
  z-index: 9999;
  background: yellow;
  border-radius: 50%;
  text-align: center;
}

.result.left:before {
  content: '⬅';
  display: block;
  width: 50px;
  height: 50px;
  line-height: 50px;
}

.result.right:before {
  content: '⮕';
  display: block;
  width: 50px;
  height: 50px;
  line-height: 50px;
}

.result.top:before {
  content: '⬆';
  display: block;
  width: 50px;
  height: 50px;
  line-height: 50px;
}

.result.bottom:before {
  content: '⬇';
  display: block;
  width: 50px;
  height: 50px;
  line-height: 50px;
}

.result.left.top:before {
  content: '⬉';
  display: block;
  width: 50px;
  height: 50px;
  line-height: 50px;
}

.result.left.bottom:before {
  content: '⬋';
  display: block;
  width: 50px;
  height: 50px;
  line-height: 50px;
}

.result.right.top:before {
  content: '⬈';
  display: block;
  width: 50px;
  height: 50px;
  line-height: 50px;
}

.result.right.bottom:before {
  content: '⬊';
  display: block;
  width: 50px;
  height: 50px;
  line-height: 50px;
}


/*Downloaded from https://www.codeseek.co/025pro/swipeable-cards-jGzjrK */
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }

var SwipeModes = {
  LeftRight: 0,
  TopBottom: 1,
  Edges: 2,
  EdgesAndCorners: 3
};

var App = function (_React$Component) {
  _inherits(App, _React$Component);

  function App() {
    _classCallCheck(this, App);

    return _possibleConstructorReturn(this, (App.__proto__ || Object.getPrototypeOf(App)).apply(this, arguments));
  }

  _createClass(App, [{
    key: "render",
    value: function render() {
      return React.createElement(
        "div",
        { className: "app" },
        React.createElement(
          SwipeableCards,
          {
            swipeMode: SwipeModes.EdgesAndCorners,
            onActionCompleted: function onActionCompleted(type, c) {
              console.log(type);
            }
          },
          React.createElement(Card, { img: "http://cdni.condenast.co.uk/646x430/a_c/ayada_cnt_25aug11_pr.jpg" }),
          React.createElement(Card, { img: "http://materializecss.com/images/sample-1.jpg" }),
          React.createElement(Card, { img: "http://www.gandex.ru/upl/oboi/gandex02.jpg" }),
          React.createElement(Card, { img: "http://www.hdwallpaperspulse.com/wp-content/uploads/2017/07/07/guitar-hd-wallpaper-for-desktop.jpg" }),
          React.createElement(Card, { img: "http://cdn.wonderfulengineering.com/wp-content/uploads/2016/01/Desktop-Wallpaper-3.jpg" }),
          React.createElement(Card, { img: "https://lh3.ggpht.com/dpVANeoetCnu6WZIMJb9SrfR3XQSwLfL4AT3CmzuHv_zQ9hw6fWgAlkm91HqedJ3VG8=h900" }),
          React.createElement(Card, { img: "http://www.hdwallpapery.com/static/images/Beautiful-Bridge-Nature-Wallpaper-For-Desktop.jpg" }),
          React.createElement(Card, { img: "http://www.wallpaperspots.com/wp-content/uploads/2013/07/Music-Desktop-Backgrounds-28-1024x768.jpg" })
        )
      );
    }
  }]);

  return App;
}(React.Component);

var SwipeableCards = function (_React$Component2) {
  _inherits(SwipeableCards, _React$Component2);

  function SwipeableCards(props) {
    _classCallCheck(this, SwipeableCards);

    return _possibleConstructorReturn(this, (SwipeableCards.__proto__ || Object.getPrototypeOf(SwipeableCards)).call(this, props));
  }

  _createClass(SwipeableCards, [{
    key: "cardMouseDown",
    value: function cardMouseDown(e) {
      this.cardPressed = true;

      this.shiftX = e.pageX;
      this.shiftY = e.pageY;
    }
  }, {
    key: "cardMouseUp",
    value: function cardMouseUp(e) {
      this.cardPressed = false;
      this.handleMouseUp(e);
    }
  }, {
    key: "cardMouseMove",
    value: function cardMouseMove(e) {
      if (!this.cardPressed) {
        return;
      }

      var left = e.pageX - this.shiftX;
      var top = e.pageY - this.shiftY;

      this.setState({
        top: top,
        left: left
      });

      var minOpacity = Math.min((this.acceptableOffset - Math.abs(left)) / this.acceptableOffset, (this.acceptableOffset - Math.abs(top)) / this.acceptableOffset);
      var calcedOpacity = minOpacity < this.minOpacity ? this.minOpacity : minOpacity;

      var resultType = this.getTypeForPosition(left, top);

      if (this.state.resultType !== resultType) {
        this.setState({
          resultType: resultType.trim(" "),
          resultOpacity: 1 - calcedOpacity,
          glowOpacity: minOpacity,
          cardOpacity: calcedOpacity
        });
      } else {
        this.setState({
          resultOpacity: 1 - calcedOpacity,
          glowOpacity: minOpacity,
          cardOpacity: calcedOpacity
        });
      }
    }
  }, {
    key: "getTypeForPosition",
    value: function getTypeForPosition(left, top) {
      var acceptableOffset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.acceptableOffset;

      var type = "";

      if (left < 0 && left <= -acceptableOffset && this.swipeMode !== SwipeModes.TopBottom) {
        type += " left";
      }
      if (left > 0 && left >= acceptableOffset && this.swipeMode !== SwipeModes.TopBottom) {
        type += " right";
      }

      if (top < 0 && top <= -acceptableOffset && this.swipeMode !== SwipeModes.LeftRight) {
        type += " top";
      }
      if (top > 0 && top >= acceptableOffset && this.swipeMode !== SwipeModes.LeftRight) {
        type += " bottom";
      }
      return type;
    }
  }, {
    key: "handleMouseUp",
    value: function handleMouseUp(e) {
      var left = this.state.left;
      var top = this.state.top;

      var type = this.getTypeForPosition(left, top).trim(' ');
      if ((Math.abs(left) >= this.acceptableOffset || Math.abs(top) >= this.acceptableOffset) && type !== '') {
        var currentCard = this.props.children.splice(0, 1);

        if (this.onActionCompleted) {
          this.onActionCompleted(type, currentCard);
        }

        this.resetStyle();
      } else {
        this.animateToStart();
      }
    }
  }, {
    key: "resetStyle",
    value: function resetStyle() {
      this.setState({
        glowOpacity: 1,
        resultOpacity: 0,
        cardOpacity: 1,
        top: 0,
        left: 0
      });
    }
  }, {
    key: "animateToStart",
    value: function animateToStart() {
      var x = Math.lerp(parseInt(this.card.style.left), 0, 0.1);
      var y = Math.lerp(parseInt(this.card.style.top), 0, 0.1);

      var left = x;
      var top = y;

      this.setState({
        top: y,
        left: x
      });

      var calcedOpacity = Math.min((this.acceptableOffset - Math.abs(left)) / this.acceptableOffset, (this.acceptableOffset - Math.abs(top)) / this.acceptableOffset);
      calcedOpacity = calcedOpacity < this.minOpacity ? this.minOpacity : calcedOpacity;

      this.setState({
        resultOpacity: 1 - calcedOpacity,
        glowOpacity: 1,
        cardOpacity: calcedOpacity
      });
      if (x === 0 && y === 0) {
        return;
      }
      requestAnimationFrame(this.animateToStart);
    }
  }, {
    key: "componentWillMount",
    value: function componentWillMount() {
      this.resetStyle();
    }
  }, {
    key: "componentDidMount",
    value: function componentDidMount() {
      this.containerWidth = this.container.clientWidth;
      this.containerHeigth = this.container.clientHeight;

      this.cardWidth = this.card.clientWidth;
      this.cardHeight = this.card.clientHeight;

      this.cardPressed = false;
      this.swipeMode = this.props.swipeMode;
      this.onActionCompleted = this.props.onActionCompleted;

      this.acceptableOffset = this.props.acceptableOffset || (this.containerWidth - this.cardWidth) / 2;
      this.opacityOffset = this.acceptableOffset / 100 * 10;
      this.minOpacity = this.props.minOpacity || 0.1;

      this.cardMouseDown = this.cardMouseDown.bind(this);
      this.cardMouseUp = this.cardMouseUp.bind(this);
      this.cardMouseMove = this.cardMouseMove.bind(this);
      this.animateToStart = this.animateToStart.bind(this);

      this.card.addEventListener("mousedown", this.cardMouseDown);
      this.card.addEventListener("mouseup", this.cardMouseUp);
      this.container.addEventListener("mousemove", this.cardMouseMove);
    }
  }, {
    key: "componentWillUnmount",
    value: function componentWillUnmount() {
      this.card.removeEventListener("mousedown", this.cardMouseDown);
      this.card.removeEventListener("mouseup", this.cardMouseUp);
      this.container.removeEventListener("mousemove", this.cardMouseMove);
    }
  }, {
    key: "render",
    value: function render() {
      var _this3 = this;

      var children = this.props.children;

      var activeCard = children[0];
      var backCard = children[1];
      return React.createElement(
        "div",
        {
          className: "SwipableCards",
          ref: function ref(container) {
            return _this3.container = container;
          }
        },
        React.createElement("div", {
          style: { opacity: this.state.resultOpacity },
          className: "result " + this.state.resultType
        }),
        backCard && React.createElement(
          "div",
          { className: "backCard" },
          React.createElement("div", { className: "glower", style: { opacity: this.state.glowOpacity } }),
          React.cloneElement(backCard)
        ),
        activeCard && React.createElement(
          "div",
          {
            style: { opacity: this.state.cardOpacity, top: this.state.top, left: this.state.left }, ref: function ref(card) {
              return _this3.card = card;
            }, className: "cardWrapper" },
          React.cloneElement(activeCard)
        )
      );
    }
  }]);

  return SwipeableCards;
}(React.Component);

var Card = function (_React$Component3) {
  _inherits(Card, _React$Component3);

  function Card(props) {
    _classCallCheck(this, Card);

    return _possibleConstructorReturn(this, (Card.__proto__ || Object.getPrototypeOf(Card)).call(this, props));
  }

  _createClass(Card, [{
    key: "render",
    value: function render() {
      return React.createElement(
        "div",
        { className: "Card" },
        React.createElement(
          "div",
          { className: "card" },
          React.createElement(
            "div",
            { className: "card-image" },
            React.createElement("img", { src: this.props.img }),
            React.createElement(
              "span",
              { className: "card-title" },
              "Card Title"
            )
          ),
          React.createElement(
            "div",
            { className: "card-content" },
            React.createElement(
              "p",
              null,
              "I am a very simple card. I am good at containing small bits of information. I am convenient because I require little markup to use effectively."
            )
          ),
          React.createElement(
            "div",
            { className: "card-action" },
            React.createElement(
              "a",
              { href: "#" },
              "This is a link"
            )
          )
        )
      );
    }
  }]);

  return Card;
}(React.Component);

ReactDOM.render(React.createElement(App, null), document.getElementById("root"));

// Obtient une valeur comprise dans un interval
Math.clamp = function (value, min, max) {
  if (value < min) {
    return min;
  } else if (value > max) {
    return max;
  }

  return value;
};

// Obtient une interpolation linéaire entre 2 valeurs
Math.lerp = function (value1, value2, amount) {
  amount = amount < 0 ? 0 : amount;
  amount = amount > 1 ? 1 : amount;
  return value1 + (value2 - value1) * amount;
};

Comments