Elastic SVG Sidebar Material Design

In this example below you will see how to do a Elastic SVG Sidebar Material Design with some HTML / CSS and Javascript

Based on this awesome dribbble shot - https://dribbble.com/shots/1949368-Sidebar-animationCompleted without any animation libs, just simple js with requestAnimationFrame and custom easings.

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

Technologies

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

<head>
  <meta charset="UTF-8">
  <title>Elastic SVG Sidebar Material Design</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=yes">
  
  <link rel='stylesheet prefetch' href='https://fonts.googleapis.com/css?family=Open+Sans'>

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

  
</head>

<body>

  <div class="demo">
  <svg class="sidebar" viewBox="0 0 300 500">
    <path class="s-path" fill="#fff" d="M0,0 50,0 a0,250 0 1,1 0,500 L0,500" />
  </svg>
  <div class="static">
    <div class="static__text">Pull white sidebar to the right</div>
  </div>
  <div class="sidebar-content">
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-man.png" alt="" class="contact__photo" />
      <span class="contact__name">Ethan Hawke</span>
      <span class="contact__status online"></span>
    </div>
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-girl.png" alt="" class="contact__photo" />
      <span class="contact__name">Natalie Portman</span>
      <span class="contact__status online"></span>
    </div>
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-man.png" alt="" class="contact__photo" />
      <span class="contact__name">Kevin Spacey</span>
      <span class="contact__status online"></span>
    </div>
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-girl.png" alt="" class="contact__photo" />
      <span class="contact__name">Rosamund Pike</span>
      <span class="contact__status online"></span>
    </div>
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-man.png" alt="" class="contact__photo" />
      <span class="contact__name">Robert Redford</span>
      <span class="contact__status online"></span>
    </div>
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-girl.png" alt="" class="contact__photo" />
      <span class="contact__name">Scarlett Johansson</span>
      <span class="contact__status online"></span>
    </div>
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-man.png" alt="" class="contact__photo" />
      <span class="contact__name">Tom Cruise</span>
      <span class="contact__status"></span>
    </div>
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-girl.png" alt="" class="contact__photo" />
      <span class="contact__name">Eva Green</span>
      <span class="contact__status"></span>
    </div>
    <div class="contact">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-man.png" alt="" class="contact__photo" />
      <span class="contact__name">Paul Newman</span>
      <span class="contact__status"></span>
    </div>
    <div class="search">
      <img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-srch.png" alt="" class="search__img" />
      <input type="text" class="search__input" placeholder="Search" />
    </div>
  </div>
  <div class="chat">
    <span class="chat__back"></span>
    <span class="chat__status">status</span>
    <div class="chat__person">
      <span class="chat__online active"></span>
      <span class="chat__name">Huehue Huehue</span>
    </div>
    <div class="chat__messages">
      <div class="chat__msgRow">
        <div class="chat__message mine">Such SVG, much Javascript, very CSS!</div>
      </div>
      <div class="chat__msgRow">
        <div class="chat__message notMine">Wow!</div>
      </div>
      <div class="chat__msgRow">
        <div class="chat__message notMine">Very elastic! Such easings!</div>
      </div>
      <div class="chat__msgRow">
        <div class="chat__message mine">
          Check out my other <a href="https://codepen.io/suez/public/" target="_blank">pens</a>
        </div>
      </div>
    </div>
    <input type="text" class="chat__input" placeholder="Your message" />
  </div>
</div>
  <script src='http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js'></script>

  

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




</body>

</html>

/*Downloaded from https://www.codeseek.co/suez/elastic-svg-sidebar-material-design-emjwvP */
*, *:before, *:after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html, body {
  font-size: 62.5%;
  height: 100%;
}

button, input {
  border: 0;
  outline: none;
}

body {
  background: linear-gradient(45deg, #636f85, #6960a0);
}

.demo {
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -25rem;
  margin-left: -15rem;
  width: 30rem;
  height: 50rem;
  box-shadow: 0 1rem 5rem rgba(0, 0, 0, 0.3);
}

.static {
  height: 100%;
  font-size: 1.8rem;
  font-family: "Open Sans", Helvetica, Arial, sans-serif;
  background: #6D7ADA;
}
.static:before, .static:after {
  content: "";
  position: absolute;
  left: 7rem;
  width: 2rem;
  height: 2rem;
  margin-left: -1rem;
  margin-top: -1rem;
  border: 2px solid #fff;
  border-left: none;
  border-bottom: none;
  -webkit-transform: rotate(45deg);
          transform: rotate(45deg);
  -webkit-animation: arrows 1.5s infinite;
          animation: arrows 1.5s infinite;
}
.static:before {
  top: 15rem;
}
.static:after {
  top: 35rem;
}
.static__text {
  width: 9rem;
  position: absolute;
  top: 50%;
  left: 50%;
  margin-left: -5rem;
  -webkit-transform: translate3d(0, -50%, 0);
          transform: translate3d(0, -50%, 0);
  color: #fff;
  -webkit-perspective: 1px;
          perspective: 1px;
  -webkit-backface-visibility: hidden;
          backface-visibility: hidden;
}

.sidebar-content {
  z-index: -1;
  position: absolute;
  top: 0;
  left: 0;
  width: 20rem;
  height: 100%;
  padding-top: 1rem;
  opacity: 0;
  transition: opacity 200ms, z-index 0s 0s;
  background-color: #E9EAF3;
  overflow: hidden;
}
.sidebar-content.active {
  z-index: 2;
  opacity: 1;
}

.contact {
  position: relative;
  width: 100%;
  height: 5rem;
  padding-left: 1.7rem;
  display: flex;
  align-items: center;
  cursor: pointer;
  overflow: hidden;
}
.contact__photo {
  border-radius: 50%;
  margin-right: 1.5rem;
}
.contact__name {
  font-size: 1.2rem;
  font-family: "Open Sans", Helvetica, Arial, sans-serif;
}
.contact__status {
  position: absolute;
  top: 2.1rem;
  right: 1.5rem;
  width: 8px;
  height: 8px;
  border: 2px solid #00B570;
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.3s;
}
.contact__status.online {
  opacity: 1;
}

.search {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 5.5rem;
  padding-left: 1.5rem;
  background: #fff;
  display: flex;
  align-items: center;
}

svg {
  overflow: visible;
}

.sidebar {
  z-index: 1;
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.s-path {
  cursor: -webkit-grab;
  cursor: grab;
}

.cloned {
  position: absolute;
  z-index: 10;
  transition: top 0.3s, left 0.3s;
  transition-delay: 0.2s;
  transition-timing-function: cubic-bezier(0.55, 0.055, 0.675, 0.19);
}
.cloned.removed {
  transition: opacity 0.2s 0;
  opacity: 0;
}

.chat {
  display: none;
  z-index: 5;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 2.5rem 0 5.5rem 2.5rem;
  transition: opacity 200ms;
  opacity: 0;
}
.chat.active {
  opacity: 1;
}
.chat.active:before {
  width: 100%;
}
.chat:before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 0.5rem;
  background: #1CC6AE;
  width: 0;
  transition: width 0.2s;
}
.chat__back {
  position: relative;
  display: inline-block;
  width: 2rem;
  height: 2rem;
  margin-top: 0.5rem;
  margin-left: -0.6rem;
  cursor: pointer;
}
.chat__back:hover:before {
  -webkit-transform: translateX(-0.3rem) rotate(-45deg);
          transform: translateX(-0.3rem) rotate(-45deg);
}
.chat__back:before {
  content: "";
  position: absolute;
  display: block;
  top: 0.4rem;
  left: 0.6rem;
  width: 1.2rem;
  height: 1.2rem;
  border: 2px solid #545675;
  border-right: none;
  border-bottom: none;
  -webkit-transform: rotate(-45deg);
          transform: rotate(-45deg);
  transition: -webkit-transform 0.3s;
  transition: transform 0.3s;
  transition: transform 0.3s, -webkit-transform 0.3s;
}
.chat__status {
  position: absolute;
  top: 2rem;
  right: 6.5rem;
  font-size: 1.2rem;
  font-family: "Open Sans", Helvetica, Arial, sans-serif;
  text-transform: uppercase;
  color: #B1A9A9;
}
.chat__person {
  display: inline-block;
  position: absolute;
  top: 3rem;
  right: 6.5rem;
  font-size: 2rem;
  font-family: "Open Sans", Helvetica, Arial, sans-serif;
  color: #36343D;
}
.chat__online {
  position: absolute;
  top: 50%;
  left: -1.5rem;
  margin-top: -3px;
  width: 8px;
  height: 8px;
  border: 2px solid #00B570;
  border-radius: 50%;
  opacity: 0;
  transition: opacity 0.3s;
}
.chat__online.active {
  opacity: 1;
}
.chat__messages {
  position: absolute;
  top: 7.5rem;
  left: 2.5rem;
  width: 27.5rem;
  height: 37rem;
  padding-right: 2.5rem;
  overflow-y: auto;
}
.chat__msgRow {
  margin-bottom: 0.5rem;
}
.chat__msgRow:after {
  content: "";
  display: table;
  clear: both;
}
.chat__message {
  display: inline-block;
  max-width: 60%;
  padding: 1rem;
  font-size: 1.4rem;
  font-family: "Open Sans", Helvetica, Arial, sans-serif;
}
.chat__message.mine {
  color: #2B2342;
  border: 1px solid #DFDFDF;
}
.chat__message.notMine {
  float: right;
  color: #23244E;
  background: #E9EAF3;
}
.chat__input {
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 5.5rem;
  padding: 1rem 1rem 1rem 4rem;
  background-image: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/142996/elastic-search.png");
  background-repeat: no-repeat;
  background-position: 1rem 1.5rem;
  background-color: #E9EAF3;
  color: #2B2342;
  font-size: 1.4rem;
  font-family: "Open Sans", Helvetica, Arial, sans-serif;
}

.ripple {
  position: absolute;
  width: 10rem;
  height: 10rem;
  margin-left: -5rem;
  margin-top: -5rem;
  background: rgba(0, 0, 0, 0.4);
  -webkit-transform: scale(0);
          transform: scale(0);
  -webkit-animation: animRipple 0.3s;
          animation: animRipple 0.3s;
  border-radius: 50%;
}

@-webkit-keyframes animRipple {
  to {
    -webkit-transform: scale(2.5);
            transform: scale(2.5);
    opacity: 0;
  }
}

@keyframes animRipple {
  to {
    -webkit-transform: scale(2.5);
            transform: scale(2.5);
    opacity: 0;
  }
}
@-webkit-keyframes arrows {
  to {
    -webkit-transform: translateX(100%) rotate(45deg);
            transform: translateX(100%) rotate(45deg);
    opacity: 0;
  }
}
@keyframes arrows {
  to {
    -webkit-transform: translateX(100%) rotate(45deg);
            transform: translateX(100%) rotate(45deg);
    opacity: 0;
  }
}


/*Downloaded from https://www.codeseek.co/suez/elastic-svg-sidebar-material-design-emjwvP */
$(document).ready(function() {
  var $svg = $(".sidebar"),
      $demo = $(".demo"),
      $path = $(".s-path"),
      $sCont = $(".sidebar-content"),
      $chat = $(".chat"),
      demoTop = $demo.offset().top,
      demoLeft = $demo.offset().left,
      diffX = 0,
      curX = 0,
      finalX = 0,
      frame = 1000 / 60,
      animTime = 600,
      sContTrans = 200,
      animating = false;

  var easings = {
    smallElastic: function(t,b,c,d) {
      var ts = (t/=d)*t;
      var tc = ts*t;
      return b+c*(33*tc*ts + -106*ts*ts + 126*tc + -67*ts + 15*t);
    },
    inCubic: function(t,b,c,d) {
      var tc = (t/=d)*t*t;
      return b+c*(tc);
    }
  };

  function createD(top, ax, dir) {
    return "M0,0 "+top+",0 a"+ax+",250 0 1,"+dir+" 0,500 L0,500";
  }

  var startD = createD(50,0,1),
      midD = createD(125,75,0),
      finalD = createD(200,0,1),
      clickMidD = createD(300,80,0),
      clickMidDRev = createD(200,100,1),
      clickD = createD(300,0,1),
      currentPath = startD;

  function newD(num1, num2) {
    var d = $path.attr("d"),
        num2 = num2 || 250,
        nd = d.replace(/\ba(\d+),(\d+)\b/gi, "a" + num1 + "," + num2);
    return nd;
  }

  function animatePathD(path, d, time, handlers, callback, easingTop, easingX) {
    var steps = Math.floor(time / frame),
        curStep = 0,
        oldArr = currentPath.split(" "),
        newArr = d.split(" "),
        oldLen = oldArr.length,
        newLen = newArr.length,
        oldTop = +oldArr[1].split(",")[0],
        topDiff = +newArr[1].split(",")[0] - oldTop,
        nextTop,
        nextX,
        easingTop = easings[easingTop] || easings.smallElastic,
        easingX = easings[easingX] || easingTop;

    $(document).off("mousedown mouseup");

    function animate() {
      curStep++;
      nextTop = easingTop(curStep, oldTop, topDiff, steps);
      nextX = easingX(curStep, curX, finalX-curX, steps);
      oldArr[1] = nextTop + ",0";
      oldArr[2] = "a" + Math.abs(nextX) + ",250";
      oldArr[4] = (nextX >= 0) ? "1,1" : "1,0";
      $path.attr("d", oldArr.join(" "));
      if (curStep > steps) {
        curX = 0;
        diffX = 0;
        $path.attr("d", d);
        currentPath = d;
        if (handlers) handlers1();
        if (callback) callback();
        return;
      }
      requestAnimationFrame(animate);
    }
    animate();
  }

  function handlers1() {

    $(document).on("mousedown touchstart", ".s-path", function(e) {
      var startX =  e.pageX || e.originalEvent.touches[0].pageX;

      $(document).on("mousemove touchmove", function(e) {
        var x = e.pageX || e.originalEvent.touches[0].pageX;
        diffX = x - startX;
        if (diffX < 0) diffX = 0;
        if (diffX > 300) diffX = 300;
        curX = Math.floor(diffX/2);
        $path.attr("d", newD(curX));
      });
    });

    $(document).on("mouseup touchend", function() {
      $(document).off("mousemove touchmove");
      if (animating) return;
      if (!diffX) return;
      if (diffX < 40) {
        animatePathD($path, newD(0), animTime, true);
      } else {
        animatePathD($path, finalD, animTime, false, function() {
          $sCont.addClass("active");
          setTimeout(function() {
            $(document).on("click", closeSidebar);
          }, sContTrans);
        });
      }
    });

  }

  handlers1();

  function closeSidebar(e) {
    if ($(e.target).closest(".sidebar-content").length ||
        $(e.target).closest(".chat").length) return;
    if (animating) return;
    animating = true;
    $sCont.removeClass("active");
    $chat.removeClass("active");
    $(".cloned").addClass("removed");
    finalX = -75;
    setTimeout(function() {
      animatePathD($path, midD, animTime/3, false, function() {
        $chat.hide();
        $(".cloned").remove();
        finalX = 0;
        curX = -75;
        animatePathD($path, startD, animTime/3*2, true);
        animating = false;
      }, "inCubic");
    }, sContTrans);
    $(document).off("click", closeSidebar);
  }

  function moveImage(that) {
    var $img = $(that).find(".contact__photo"),
        top = $img.offset().top - demoTop,
        left = $img.offset().left - demoLeft,
        $clone = $img.clone().addClass("cloned");

    $clone.css({top: top, left: left});
    $demo.append($clone);
    $clone.css("top");
    $clone.css({top: "1.8rem", left: "25rem"});
  }

  function ripple(elem, e) {
    var elTop = elem.offset().top,
        elLeft = elem.offset().left,
        x = e.pageX - elLeft,
        y = e.pageY - elTop;
    var $ripple = $("<div class='ripple'></div>");
    $ripple.css({top: y, left: x});
    elem.append($ripple);
  }

  $(document).on("click", ".contact", function(e) {
    if (animating) return;
    animating = true;
    $(document).off("click", closeSidebar);
    var that = this,
        name = $(this).find(".contact__name").text(),
        online = $(this).find(".contact__status").hasClass("online");
    $(".chat__name").text(name);
    $(".chat__online").removeClass("active");
    if (online) $(".chat__online").addClass("active");
    ripple($(that),e);
    setTimeout(function() {
      $sCont.removeClass("active");
      moveImage(that);
      finalX = -80;
      setTimeout(function() {
        $(".ripple").remove();
        animatePathD($path, clickMidD, animTime/3, false, function() {
          curX = -80;
          finalX = 0;
          animatePathD($path, clickD, animTime*2/3, true, function() {
            $chat.show();
            $chat.css("top");
            $chat.addClass("active");
            animating = false;
          });
        }, "inCubic");
      }, sContTrans);
    }, sContTrans);
  });

  $(document).on("click", ".chat__back", function() {
    if (animating) return;
    animating = true;
    $chat.removeClass("active");
    $(".cloned").addClass("removed");
    setTimeout(function() {
      $(".cloned").remove();
      $chat.hide();
      finalX = 100;
      animatePathD($path, clickMidDRev, animTime/3, false, function() {
        curX = 100;
        finalX = 0;
        animatePathD($path, finalD, animTime*2/3, true, function() {
          $sCont.addClass("active");
          $(document).on("click", closeSidebar);
          animating = false;
        });
      }, "inCubic");
    }, sContTrans);
  });

  $(window).on("resize", function() {
    demoTop = $demo.offset().top;
    demoLeft = $demo.offset().left;
  });

});

Comments