JSFiddle - React, Tailwind, and code Playground

by alexb

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.2.9/interact.min.js"></script>
<div id="a"></div>
<div id="b"></div>

CSS

div {
  display: block;
  height: 100px;;
  width: 100px;
  position: absolute;
}

#a {
  background-color: red;
  left: 200px;
  top: 300px
}

#b {
  background-color: blue;
  left: 400px;
  top: 100px;
}

svg {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  width: 100%;
  height: 100%;
}

div {
  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAQAAAC0NkA6AAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfiCB8VExzAUWBnAAACEklEQVRYw+2YTU8aQRjHf7yctjQR00ioyknYrZ+hHmma1p48esar8UO011qPcm/14EnxUJvAQj8DhqNZkyYe1NgETzAeRmQXFmdg2EMb/lzIZOf5PfPMy/5nYVzNMTd2nzFlc0IFO0pEAReBoB4dpoeIECMRTe64oxkNRiJqFLnggiK16WNsXAR18izi4bFInvp0MSmOnwLm8PDIATZ1BGfMqwPENSBd/lBhi1agtUWJX8RITmssFqnHf/2RAMyzoNNdL4/2iPZrvRx1ymWsGWQGASCFZRyzv6tCIW84ZNcQY7HLj+CZ5oc4lPlAhphhbbKsUw4/Oh1+I6hReDZE8FgJlz3qhJYIV4HQg4zAODQQVFlRFmMJD48l5XN5/2stiZyLt5zzhXtFjl2WSQDLqBZ/m89kWKNMiVaMNN95D/zlRmPXJFgArugon+yS5iVwymYcgVB2MFEMktyyQ4o1LtmmSUJZriNgA08x6g6r7OHQYIdb2SRXQ5W8Mq+JJr4nW9N9GCxhfYzeZnRHR5KYn6SNIH4D9Sj/9LUocYZQTL5K4QYqoDSvjMs1dNQPWqIbo1FIDRmo//f1O4P8KxCLF6HtmgZKB2LxbdDkAOBwwFcdjA4kzms+sT9gMmz2+UjW0EAFcm4gcIcupu50778S07tiv6OmZaAmxMiPBefRIPoY+YsI4cdEiJCYUyrRImCij2oP+ni8krzSl5MAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTgtMDgtMzFUMTk6MTk6MjgrMDI6MDDLqzBaAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE4LTA4LTMxVDE5OjE5OjI4KzAyOjAwuvaI5gAAAABJRU5ErkJggg==);
  background-position: center center;
  background-repeat: no-repeat;
}

JavaScript

function createPathDef(x1, y1, x2, y2) {
	let dx = Math.abs(y2 - y1) / 2;
	let dy = Math.abs(x2 - x1) / 2;
	return `M ${x1} ${y1} C ${x1 - dx} ${y1 - dy}, ${x2 - dx} ${y2 - dy}, ${x2} ${y2}`;
}

const a = document.querySelector('#a');
const x1 = a.offsetLeft;
const y1 = a.offsetTop;

const b = document.querySelector('#b');
const x2 = b.offsetLeft;
const y2 = b.offsetTop;

var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.innerHTML = `<path d="${createPathDef(x1, y1, x2, y2)}" stroke="black" stroke-width="4"  stroke-linecap="round" fill="transparent"/>`;
a.offsetParent.appendChild(svg);

const path = svg.firstChild;
function updatePath() {
	path.setAttribute('d', createPathDef(
  	x1 + (parseFloat(a.dataset.x) || 0),
  	y1 + (parseFloat(a.dataset.y) || 0),
  	x2 + (parseFloat(b.dataset.x) || 0),
  	y2 + (parseFloat(b.dataset.y) || 0)
  ));
}

interact('div').draggable({
  onmove: e => {
        x = (parseFloat(e.target.dataset.x) || 0) + e.dx,
        y = (parseFloat(e.target.dataset.y) || 0) + e.dy;
    e.target.style.transform = `translate(${x}px, ${y}px)`;
    e.target.setAttribute('data-x', x);
    e.target.setAttribute('data-y', y);
    updatePath();
  }
});