Skip to content

Instantly share code, notes, and snippets.

@ye7ia33
Created February 9, 2016 20:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ye7ia33/f71153120168a81d2ecf to your computer and use it in GitHub Desktop.
Save ye7ia33/f71153120168a81d2ecf to your computer and use it in GitHub Desktop.
Elastic SVG Sidebar Material Design
<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="http://codepen.io/suez/public/" target="_blank">pens</a>
</div>
</div>
</div>
<input type="text" class="chat__input" placeholder="Your message" />
</div>
</div>
$(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;
});
});
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
@import "compass/css3";
*, *:before, *:after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
$openSans: 'Open Sans', Helvetica, Arial, sans-serif;
$transTime: 200ms;
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: $openSans;
background: #6D7ADA;
&:before,
&: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;
transform: rotate(45deg);
animation: arrows 1.5s infinite;
}
&:before {
top: 15rem;
}
&:after {
top: 35rem;
}
&__text {
width: 9rem;
position: absolute;
top: 50%;
left: 50%;
margin-left: -5rem;
transform: translate3d(0,-50%,0);
color: #fff;
perspective: 1px;
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 $transTime, z-index 0s 0s;
background-color: #E9EAF3;
overflow: hidden;
&.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;
&__photo {
border-radius: 50%;
margin-right: 1.5rem;
}
&__name {
font-size: 1.2rem;
font-family: $openSans;
}
&__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;
&.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: 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);
&.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 $transTime;
opacity: 0;
&.active {
opacity: 1;
&:before {
width: 100%;
}
}
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 0.5rem;
background: #1CC6AE;
width: 0;
transition: width 0.2s;
}
&__back {
position: relative;
display: inline-block;
width: 2rem;
height: 2rem;
margin-top: 0.5rem;
margin-left: -0.6rem;
cursor: pointer;
&:hover:before {
transform: translateX(-0.3rem) rotate(-45deg);
}
&: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;
transform: rotate(-45deg);
transition: transform 0.3s;
}
}
&__status {
position: absolute;
top: 2rem;
right: 6.5rem;
font-size: 1.2rem;
font-family: $openSans;
text-transform: uppercase;
color: #B1A9A9;
}
&__person {
display: inline-block;
position: absolute;
top: 3rem;
right: 6.5rem;
font-size: 2rem;
font-family: $openSans;
color: #36343D;
}
&__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;
&.active {
opacity: 1;
}
}
&__messages {
position: absolute;
top: 7.5rem;
left: 2.5rem;
width: 27.5rem;
height: 37rem;
padding-right: 2.5rem;
overflow-y: auto;
}
&__msgRow {
margin-bottom: 0.5rem;
&:after {
content: "";
display: table;
clear: both;
}
}
&__message {
display: inline-block;
max-width: 60%;
padding: 1rem;
font-size: 1.4rem;
font-family: $openSans;
&.mine {
color: #2B2342;
border: 1px solid #DFDFDF;
}
&.notMine {
float: right;
color: #23244E;
background: #E9EAF3;
}
}
&__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: $openSans;
}
}
.ripple {
position: absolute;
width: 10rem;
height: 10rem;
margin-left: -5rem;
margin-top: -5rem;
background: rgba(0,0,0,0.4);
transform: scale(0);
animation: animRipple 0.3s;
border-radius: 50%;
}
@keyframes animRipple {
to {
transform: scale(2.5);
opacity: 0;
}
}
@keyframes arrows {
to {
transform: translateX(100%) rotate(45deg);
opacity: 0;
}
}
<link href="http://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment