JSFiddle - React, Tailwind, and code Playground

by Nickolay Ilchenko

HTML

<h2>Расставьте супергероев по полю.</h2>
  
  Требования:
  
  <ol>
    <li>Элементы с class="draggable" должно быть можно переносить.</li>
    <li>Поддержка документов со скроллом.</li>
  </ol>
  
  <div id="field">
    
  </div>

  <div class="hero draggable" id="hero1"></div>
  <div class="hero draggable" id="hero2"></div>
  <div class="hero draggable" id="hero3"></div>
  <div class="hero draggable" id="hero4"></div>
  <div class="hero draggable" id="hero5"></div>
  <div id="winnie" class="draggable"></div>

  <img src="http://ru.lookatcode.com/show/744548496324569/ball.png" class="draggable">

  <div style="clear:both"></div>

CSS

html, body {
  margin: 0;
  padding: 0;
}

.draggable
{
    cursor: move !important;
}

#field {
  background: url(http://ru.lookatcode.com/show/744548496324569/field.png);
  width: 800px;
  height: 600px;
  float: left;
}

.hero {
  background: url(http://ru.lookatcode.com/show/744548496324569/heroes.png);
  width: 105px;
  height: 128px;
  float: left;
}

#hero1 {
  background-position: 0 0;
}

#hero2 {
  background-position: 0 -128px;
}

#hero3 {
  background-position: -131px 0;
}

#hero4 {
  background-position: -131px -128px;
}

#hero5 {
  background-position: -236px 0;
  width: 130px;
}

#winnie {
  background: url(http://ru.lookatcode.com/show/744548496324569/winnie.png);
  width: 115px;
  height: 128px;
  float: left;
}

.draggable {
  cursor: pointer;
}

.dragging {
  z-index: 1000;
  position: absolute;
  cursor: move;
}

JavaScript

var dragElem = document.querySelectorAll('.draggable');
console.log(dragElem[4]);

for(var i = 0; i < dragElem.length; i++) {
    dragElem[i].onmousedown = function(event) {
        var self = this,
            coords = getCoords(self),
            shiftX = event.pageX - coords.left,
            shiftY = event.pageY - coords.top;
        
        prepare();
        moveAt(event);
        
        document.onmousemove = function(event) {
            moveAt(event);
        }
        
        document.onmouseup = function() {
            document.onmouseup = document.onmousemove = null;
        }
        
        function prepare() {
            self.style.position = 'absolute';
            self.style.zIndex = '1000';
        }
        
        function moveAt(event) {
            self.style.left = event.pageX - shiftX + 'px';
            self.style.top = event.pageY - shiftY + 'px';
        }
    }   
    
    dragElem[i].ondragstart = function() {
        return false;
    }
}

/**
 * Координаты элемента относительно документа.
 * Могут быть дробными
 * @param  {Element} elem Элемент
 * @return {object}      {top: верхняя, left: левая}
 */
function getCoords(elem) {
    var box = elem.getBoundingClientRect();

    var body = document.body;
    var docElem = document.documentElement;

    var scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop;
    var scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft;

    var clientTop = docElem.clientTop || body.clientTop || 0;
    var clientLeft = docElem.clientLeft || body.clientLeft || 0;

    var top  = box.top +  scrollTop - clientTop;
    var left = box.left + scrollLeft - clientLeft;

    return { top: top, left: left };
}