JSFiddle - React, Tailwind, and code Playground
by Dogbert
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.6/d3.min.js"></script>
<div id="menu-container">
<div id="menu">
</div>
<svg viewBox="0 0 20 100" preserveAspectRatio="none"></svg>
</div>
SCSS
$closed-width: 50px;
$opened-width: 200px;
$curve-width: 35px;
$curve-padding-right: 50px;
* {
margin: 0;
padding: 0;
}
body {
background: white;
}
#menu-container {
background: #eee;
float: left;
position: relative;
height: 100vh;
#menu {
position: absolute;
left: 0;
top: 0;
height: 100%;
background: #eee;
padding-right: $curve-width;
}
&,
#menu {
width: $closed-width;
transition: width .5s ease;
}
svg {
position: absolute;
left: $closed-width;
height: 100%;
width: $curve-width;
padding-right: $curve-padding-right;
path {
fill: #eee;
}
}
&.open {
&,
#menu {
width: $opened-width;
}
svg {
width: 0;
}
}
}
JavaScript
// ratio of height of curve to window height, in %
var heightRatio = 200;
var duration = 500;
var svg = d3.select('#menu-container svg');
var line = svg.append('path').attr('d', d);
var d;
svg.on('mouseenter', function() {
go(true);
}).on('mouseleave', function() {
line.transition().duration(duration).attr('d', d);
}).on('mousemove', function() {
go(false)
});
var container = d3.select('#menu-container');
d3.select("#menu-container #menu").on('mouseenter', function() {
container.classed('open', true);
});
container.on('mouseleave', function() {
container.classed('open', false);
});
var suppress = false;
function go(didEnter) {
var y = (d3.event.offsetY - container.node().offsetTop) / container.node().offsetHeight * 100;
d = 'M 0 ' + (y - heightRatio / 2) + ' Q 0 ' + y + ' 0 ' + (y + heightRatio / 2);
var d2 = 'M 0 ' + (y - heightRatio / 2) + ' Q 15 ' + y + ' 0 ' + (y + heightRatio / 2);
if (didEnter) {
suppress = true;
line.attr('d', d);
line.transition()
.ease(d3.easeCubic)
.duration(duration)
.attr('d', d2)
.on('end', function() {
suppress = false;
})
} else if (!suppress) {
line.attr('d', d2)
}
}