Skip to content

Instantly share code, notes, and snippets.

@shshaw
Last active March 11, 2023 11:31
Show Gist options
  • Save shshaw/c34779c20770ece7ee1051b9bd99bdea to your computer and use it in GitHub Desktop.
Save shshaw/c34779c20770ece7ee1051b9bd99bdea to your computer and use it in GitHub Desktop.
❏ Dragnabbit! ❏

❏ Dragnabbit, Vue! ❏

Simple draggable box component for Vue. Easily reusable.

A Pen by Shaw on CodePen.

License.

❏ Dragnabbit! ❏

Mini drag & drop library with a 3D pop-out effect. Compresses down to about 1kb (0.4kb gzipped)!

  • 2017-11-29 - Added scroll support

A Pen by Shaw on CodePen.

License.

<div data-dragnabbit class="drag-box"><h1> Dragnabbit! </h1></div>
<div data-dragnabbit class="drag-box">A simple drag & drop JS micro-library.</div>
<br />
<div data-dragnabbit><div class="card card--aod" onclick=""></div></div>
<div data-dragnabbit><div class="card card--aoc" onclick=""></div></div>
<div data-dragnabbit><div class="card card--aoh" onclick=""></div></div>
<div data-dragnabbit><div class="card card--aos" onclick=""></div></div>
<div data-dragnabbit><div class="card card--koh" onclick=""></div></div>
console.clear(1);
/* Looking for the Vue version? https://codepen.io/shshaw/pen/vWjZpa?editors=0010 */
function Dragnabbit(el){
var className = 'dragnabbit',
dragging = false,
offsetX = 0,
offsetY = 0,
events = {
'mousemove': onUpdate,
'mouseup': onUp,
'mouseleave': onUp,
'touchmove': onUpdate,
'touchend': onUp,
'touchleave': onUp,
'touchcancel': onUp
};
el.classList.add(className);
el.addEventListener('mousedown', onDown);
el.addEventListener('touchstart', onDown, { passive: false });
function setEvents(){
var method = ( dragging ? 'add' : 'remove' ) + 'EventListener';
for (var key in events) { document[ method ]( key, events[key]); }
}
function onDown(e){
el.classList.add(className+'--dragging');
var last = document.querySelector('.'+className+'--last');
if ( last ) { last.classList.remove(className + '--last'); }
dragging = true;
setEvents();
var rect = el.getBoundingClientRect();
e.preventDefault();
e = e.touches ? e.touches[0] : e;
offsetX = ( e.clientX - rect.left ) + el.offsetLeft;
offsetY = ( e.clientY - rect.top ) + el.offsetTop;
onUpdate(e);
}
function onUp(e){
el.classList.remove(className + '--dragging');
el.classList.add(className + '--last');
dragging = false;
setEvents();
e.preventDefault();
onUpdate(e);
}
// Update the element's transform
function onUpdate(e){
e = e.touches ? e.touches[0] : e;
var scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
el.style.transform = 'translate3d(' + (e.clientX - offsetX + scrollLeft) + 'px,' + (e.clientY - offsetY + scrollTop) + 'px,' + ( dragging ? 3 : 0 ) + 'px)';
}
// Destroy
return {
reset: function(){
el.style.transform = '';
},
destroy: function(){
dragging = false;
setEvents();
el.removeEventListener('mousedown', onDown);
el.removeEventListener('touchstart', onDown);
el.classList.remove(className, className + '--dragging', className + '--last');
}
}
}
var dragnabbits = Array.from( document.querySelectorAll('[data-dragnabbit]') ).map( Dragnabbit );
/* //////// */
var button = document.createElement('button');
button.className = 'button';
button.innerText = 'Reset';
document.body.appendChild(button);
button.addEventListener('click',function(){
dragnabbits.forEach( d => { d.reset(); });
});
<script src="https://codepen.io/shshaw/pen/epmrgO"></script>
@import url('https://fonts.googleapis.com/css?family=Fredoka+One');
/* Play with the perspective here to get the 3D effect you want. */
html { perspective: 30px; position: relative; }
.dragnabbit {
display: inline-block;
position: relative;
cursor: grab;
transition: transform 0.4s cubic-bezier(.15, .8, .3, 1);
will-change: transform;
transform-style: preserve-3d;
touch-action: none;
}
.dragnabbit--last { z-index: 2; }
.dragnabbit--dragging {
z-index: 999;
cursor: grabbing;
}
/* Stylistic choices */
.dragnabbit::before,
.dragnabbit::after {
content: ' ';
display: block;
position: absolute;
z-index: -1;
top: 0;
left: 0;
right: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateZ(-0.2px);
transition: opacity 0.4s ease-in-out, transform 0.4s cubic-bezier(.15, .8, .3, 1.2);
}
.dragnabbit::before {
outline: dashed 2px #000;//4A2C2C;
opacity: 0;
}
.dragnabbit:hover::before { opacity: 0.5; }
.dragnabbit--dragging::before,
.dragnabbit--dragging:hover::before {
opacity: 1;
transform: translateZ(-3px);
}
.dragnabbit::after {
background: #000;
opacity: 0.1;
box-shadow: 0 0px 30px 5px #000;
}
.dragnabbit:hover::after { opacity: 0.4; }
.dragnabbit--dragging:after,
.dragnabbit--dragging:hover::after {
opacity: 0.3;
transform: translateZ(-3px);
}
/* ////////////////////////////////////////////////////////////////////////// */
/* Extraneous Styles */
*, *:before, *:after { box-sizing: border-box; }
html { text-align: center; background: #8E3343; height: 100%; padding: 1em; }
html { height: 100%; display: flex; }
body { margin: auto; }
h1 {
font-family: 'Fredoka One', sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
font-size: 28px;
text-shadow: 0 1px 0px #4A2C2C;
margin: 0;
}
.drag-box {
background: #F86254;
color: #FFF;
padding: 1.5em;
margin: 0 20px 20px 0;
}
.card {
display: inline-block;
font-size: 50px;
@media screen and (min-width: 320px) {
font-size: ~"calc(50px + 100 * ((100vw - 320px) / 680))";
}
@media screen and (min-width: 1000px) {
font-size: 150px;
}
position: relative;
color: #292A18;
position: relative;
width: 1em;
line-height: 1.1;
border-radius: 0.1em;
background: #EEE;
text-align: center;
padding: 0em 0 0.125em;
box-shadow: 0 5px 20px rgba(0,0,0,0.2);
}
.card--aoc:after { content: '\1F0D1'; }
.card--aoh:after { content: '\1F0B1'; }
.card--koh:after { content: '\1F0BE'; }
.card--aod:after { content: '\1F0C1'; }
.card--aos:after { content: '\1F0A1'; }
.card--kos:after { content: '\1F0AE'; }
.card--aod, .card--aoh, .card--koh { color: #CC5840; }
.button {
display: block;
margin: 20px auto;
background: #583131;
border: none;
border-radius: 5px;
color: #FFF;
padding: 10px 20px;
font-family: 'Fredoka One', sans-serif;
font-weight: 400;
-webkit-font-smoothing: antialiased;
&:focus { outline: none; }
&:hover,
&:focus { cursor: pointer; background: #F86254; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment