JSFiddle - React, Tailwind, and code Playground
HTML
<script src="http://threedubmedia.com/inc/js/jquery.event.drag-2.2.js"></script>
<div style="width: 100%;
height: 55px;"></div>
<div data-drag="0" class="thing">
<div class="circle"></div>
</div>
<div class="magnet-zone">
<div class="magnet"></div>
</div>
CSS
.thing, .thing .circle, .magnet {
border-radius: 50%;
width: 60px;
height: 60px;
}
.thing .circle, .magnet-zone {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
-webkit-perspective: 1000;
perspective: 1000;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.explain {
position: absolute;
left: 0;
right: 0;
top: 50%;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
html {
height: 100%;
}
body {
width: 100%;
height: 100%;
overflow: hidden;
text-align: center;
font-family: 'Open Sans', 'Lato', 'Helvetica Neue', Arial, sans-serif;
}
* {
-webkit-tap-highlight-color: transparent;
-webkit-tap-highlight-color: transparent;
/* For some Androids */
}
.thing {
position: absolute;
-webkit-transform: translate(0, 0);
-ms-transform: translate(0, 0);
transform: translate(0, 0);
margin: 0px;
cursor: pointer;
}
.thing .circle {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #888;
background-image: url(http://gravatar.com/avatar/84eac3a27d1acf0ef0d835d92c999b0d?s=80);
background-size: contain;
background-repeat: no-repeat;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1);
-webkit-transition: -webkit-transform 50ms linear;
transition: transform 50ms linear;
}
.thing.transition {
-webkit-transition: -webkit-transform 150ms cubic-bezier(0.175, 0.885, 0.145, 1.25);
transition: transform 150ms cubic-bezier(0.175, 0.885, 0.145, 1.25);
}
.thing.edge {
-webkit-transition: all 400ms cubic-bezier(0.175, 0.885, 0.345, 1.1);
transition: all 400ms cubic-bezier(0.175, 0.885, 0.345, 1.1);
}
.magnet-zone {
pointer-events: none;
-webkit-transition: -webkit-transform 300ms cubic-bezier(0.175, 0.885, 0.145, 1.32);
...
JavaScript
var magnet = document.querySelector('.magnet-zone');
function isOverlapping(el1, el2) {
var rect1 = el1.getBoundingClientRect(), rect2 = el2.getBoundingClientRect();
return !(rect1.top > rect2.bottom || rect1.right < rect2.left || rect1.bottom < rect2.top || rect1.left > rect2.right);
}
function moveToPos(x, y, here) {
here.style.transform = 'translate(' + Math.round(x, 10) + 'px, ' + Math.round(y, 10) + 'px) translateZ(0)';
here.style.webkitTransform = 'translate(' + Math.round(x, 10) + 'px, ' + Math.round(y, 10) + 'px) translateZ(0)';
}
function moveMagnet(x, y) {
var dist = 12, width = $('body').width() / 2, height = $('body').height(), direction = x > width ? 1 : -1, percX = x > width ? (x - width) / width : -(width - x) / width, percY = Math.min(1, (height - y) / (height / 2));
magnet.style.marginLeft = Math.round(dist / 1.5 * percX) + 'px';
magnet.style.marginBottom = Math.round(dist * percY) + 'px';
}
function move(event) {
var el = this, magnetRect = magnet.getBoundingClientRect(), elRect = el.getBoundingClientRect();
x = this._posOrigin.x + event.pageX - this._touchOrigin.x;
y = this._posOrigin.y + event.pageY - this._touchOrigin.y;
moveMagnet(x + elRect.width / 2, y + elRect.height / 2);
$('body').addClass('moving');
var touchPos = {
top: y,
right: x + elRect.width,
bottom: y + elRect.height,
left: x
};
overlapping = !(touchPos.top > magnetRect.bottom || touchPos.right < magnetRect.left || touchPos.bottom < magnetRect.top || touchPos.left > magnetRect.right);
if (overlapping) {
var mx = magnetRect.width / 2 + magnetRect.left;
var my = magnetRect.height / 2 + magnetRect.top;
x = mx - elRect.width / 2;
y = my - elRect.height / 2;
if (!$(el).hasClass('overlap')) {
$(el).addClass('transition');
setTimeout(function () {
$(el).removeClass('transition');
}, 150);
...