JSFiddle - React, Tailwind, and code Playground
HTML
<div id="viewport">
<ul id="list">
<li class="above">FOO</li>
<li class="selected">BAR</li>
<li class="below">BARF</li>
<li>BOO</li>
<li>HUH</li>
<li>WHA</li>
<li>OH</li>
</ul>
</div>
<a href="#" id="up">UP</a> -- <a href="#" id="down">DOWN</a>
CSS
#viewport{
height: 325px;
overflow: hidden;
padding: 0;
}
ul{
list-style-type: none;
padding: 0;
margin: 0;
}
li{
width: 140px;
height: 100px;
line-height: 100px;
padding: 0 0 0 60px;
margin: 5px;
font-family: Arial;
font-size: 2.2em;
border: 1px dashed #ccc;
color: #999;
background: #f5f5f5;
border-radius: 100px;
}
li.selected{
border: 1px solid #ccc;
border-radius: 40px;
background-color: #fe0
}
li.above{
border: 1px solid #ccc;
background-color: #fed;
border-radius: 40px;
border-top-left-radius: 85px;
}
li.below{
border: 1px solid #ccc;
background-color: #fed;
border-radius: 40px;
border-bottom-left-radius: 85px;
}
JavaScript
var viewport = $('#viewport'),
list = $('#list'),
itemH = $('li', list).first().outerHeight(),
btnUp = $('#up'),
btnDown = $('#down'),
busy = false,
spdClass = 250,
spdSlide = 600,
hD = false,
hU = false,
selected, above, below;
var update = function(){
if(hU || hD) { busy = false; return; }
selected = $('li:nth-of-type(2)', list).addClass('selected', spdClass);
above = selected.prev().addClass('above', spdClass);
below = selected.next().addClass('below', spdClass);
setTimeout(function(){ busy = false; }, spdClass + 100);
};
var goUp = function(){
if(busy) return; else busy = true;
$('li', list).removeClass('selected above below', hD ? 0 : spdClass);
setTimeout(function(){
list.animate({marginTop: -itemH}, hU ? 80 : spdSlide, hU ? 'linear' : 'easeOutBounce', function(){
list.css({marginTop: 0}).append($('li', list).first());
update();
});
}, spdClass);
}
var goDown = function(){
if(busy) return; else busy = true;
$('li', list).removeClass('selected above below', hD ? 0 : spdClass);
setTimeout(function(){
list
.css({marginTop: -itemH})
.prepend($('li',list).last())
.animate({marginTop: 0}, hD ? 80 : spdSlide, hD ? 'linear' : 'easeOutBounce', update);
}, spdClass);
}
btnUp.on('click', goUp);
btnDown.on('click', goDown);
$('body')
.keydown(function(e){
if(e.which == 38) { hU = true; goUp(); }
if(e.which == 40) { hD = true; goDown(); }
})
.keyup(function(e){
if(e.which == 38) { hU = false; }
if(e.which == 40) { hD = false; }
});