Drag and drop, resizing and multi-touch gestures with inertia and snapping for modern browsers (and also IE8+)

Download the minified version
15.6KB gzipped
interact.min.js
Download the development version
34.9KB gzipped
interact.js

Dragging

<!-- enable javascript to view a demo -->
<div id="drag-me" class="draggable">
    <p> Click or touch and drag this element around </p>
</div>
/* enable javascript to view a demo */
#drag-me {
    width: 25%;
    height: 100%;
    margin: 10%;

    background-color: #29e;
    color: white;
    
    border: solid 0.4em #666;
    border-radius: 0.75em;
    padding: 3%;
}

#drag-me::before {
    content: "#" attr(id);
    color: #000;
}
/* enable javascript to view a demo */
interact('.draggable')
    .draggable({
        onmove: function (event) {
            var target = event.target,
                x = (parseInt(target.getAttribute('data-x')) || 0) + event.dx,
                y = (parseInt(target.getAttribute('data-y')) || 0) + event.dy;

            target.style.webkitTransform =
            target.style.transform =
                'translate(' + x + 'px, ' + y + 'px)';

            target.setAttribute('data-x', x);
            target.setAttribute('data-y', y);
        },
        onend: function (event) {
            event.target.querySelector('p').textContent =
                'moved a distance of '
                + (Math.sqrt(event.dx * event.dx +
                             event.dy * event.dy)|0) + 'px';
        }
    })
    .inertia(true)
    .restrict({
        drag: "parent",
        endOnly: true,
        elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
    });

Drag and drop

<!-- enable javascript to view a demo -->
<div id="no-drop" class="draggable drag-drop"> #no-drop </div>

<div id="yes-drop" class="draggable drag-drop"> #yes-drop </div>

<div id="outer-dropzone" class="dropzone">
    #outer-dropzone
    <div id="inner-dropzone" class="dropzone">#inner-dropzone</div>
 </div>
/* enable javascript to view a demo */
#outer-dropzone {
    height: 140px;
}

#inner-dropzone {
    height: 80px;
}

.dropzone {
    background-color: #ccc;
    border: dashed 4px #666;
    border-radius: 4px;
    margin: 10px auto 30px;
    padding: 10px;
    width: 80%;
    transition: background-color 0.3s;
}

.drop-target {
    background-color: #29e;
}

.drag-drop {
    display: inline-block;
    min-width: 40px;
    padding: 2em 0.5em;

    background-color: #99e;
    border: solid 2px white;

    transition: background-color 0.3s;
}

.can-drop {
    background-color: #4e4;
}
/* enable javascript to view a demo */
interact('.dropzone')
    // enable draggables to be dropped into this
    .dropzone(true)
    // only accept elements matching this CSS selector
    .accept('#yes-drop')
    // listen for drop related events
    .on('dragenter', function (event) {
        var draggableElement = event.relatedTarget,
            dropzoneElement = event.target;

        // feedback the possibility of a drop
        dropzoneElement.classList.add('drop-target');
        draggableElement.classList.add('can-drop');
        draggableElement.textContent = 'Dragged in';
    })
    .on('dragleave', function (event) {
        // remove the drop feedback style
        event.target.classList.remove('drop-target');
        event.relatedTarget.classList.remove('can-drop');
        event.relatedTarget.textContent = 'Dragged out';
    })
    .on('drop', function (event) {
        event.relatedTarget.textContent = 'Dropped';
    });

Snapping

<!-- enable javascript to view a demo -->
<div id="grid-snap">
    Dragging is constrained to the corners of a grid
</div>
/* enable javascript to view a demo */
#grid-snap {
    width: 40%;
    background-color: #4e4;
    color: #fff;
    font-size: 1.2em;
    border-radius: 4px;
    padding: 2%;
    margin: 5%;
}

#grid-snap::after {
    content: attr(data-x) ", " attr(data-y);
    background-color: #29e;
    color: #fff;
    float: right;
}
/* enable javascript to view a demo */
var element = document.getElementById('grid-snap'),
    x = 0, y = 0;

interact(element)
    .draggable(true)
    .snap({
        mode: 'grid',
        grid: { x: 30, y: 30 },
        range: Infinity,
        elementOrigin: { x: 0, y: 0 }
    }) 
    .on('dragmove', function (event) {
        x += event.dx;
        y += event.dy;

        event.target.style.webkitTransform =
        event.target.style.transform =
            'translate(' + x + 'px, ' + y + 'px)';
    })
    .inertia(true)
    .restrict({
        drag: element.parentNode,
        elementRect: { top: 0, left: 0, bottom: 1, right: 1 },
        endOnly: true
    });

Resizing

<!-- enable javascript to view a demo -->
<div class="resize-container">
    <div class="resize"
         style="width: 200px; height: 200px">
         Resize from the bottom and left edges
    </div>
</div>
/* enable javascript to view a demo */
.resize {
  background-color: #29e;
  color: white;
  font-size: 20px;
  font-family: sans-serif;
  border-radius: 8px;
  box-sizing: border-box;
  padding: 20px;
  margin: 20px;

  /* The width is set in the element's HTML
   * so that it can be read by the resizing
   * function
   */
}

.resize-container {
  width: 240px;
  height: 240px;
}
/* enable javascript to view a demo */
interact('.resize')
  .resizable(true)
  .on('resizemove', function (event) {
    var target = event.target;

    // add the change in coords to the previous width of the target element
    var
      newWidth  = parseFloat(target.style.width ) + event.dx,
      newHeight = parseFloat(target.style.height) + event.dy;

    // update the element's style
    target.style.width  = newWidth + 'px';
    target.style.height = newHeight + 'px';

    target.textContent = newWidth + '×' + newHeight;
  });

Multi-touch Rotation

<!-- enable javascript to view a demo -->
<div id="rotate-area">
    <div id="angle-info">0&deg;</div>
    <svg id="arrow" viewBox="0 0 100 100">
        <polygon
            points="50,0 75,25 62.5,25 62.5,100 37.5,100 37.5,25 25,25" 
            fill="#29e" />
    </svg>
</div>
/* enable javascript to view a demo */
#rotate-area {
    overflow: hidden;
}

#arrow {
    width: 100%;
    height: 100%;
}

#angle-info {
    color: #666;
    font-size: 2em;
    position: absolute;
}
/* enable javascript to view a demo */
var angle = 0;

interact('#rotate-area').gesturable({
    onmove: function (event) {
        var arrow = document.getElementById('arrow');

        angle += event.da;

        arrow.style.webkitTransform =
        arrow.style.transform =
            'rotate(' + angle + 'deg)';

        document.getElementById('angle-info').textContent =
            angle.toFixed(2) + '°';
    }
});

Gesture Pinch-to-zoom

<!-- enable javascript to view a demo -->
<div id="gesture-area">
    <!-- thanks //commons.wikimedia.org/wiki/User:Booyabazooka -->
    <img id="scale-element" src="img/rcube.png">
</div>
/* enable javascript to view a demo */
#scale-element {
    width: 100%;
}

#scale-element.reset {
    transition: -webkit-transform 0.3s ease-in-out;
    transition: transform 0.3s ease-in-out;
}
/* enable javascript to view a demo */
var scale = 1,
    scaleElement = document.getElementById('scale-element'),
    gestureArea = document.getElementById('gesture-area'),
    resetTimeout;

interact(gestureArea).gesturable({
    onstart: function (event) {
        clearTimeout(resetTimeout);
        scaleElement.classList.remove('reset');
    },
    onmove: function (event) {
        scale = scale * (1 + event.ds);
        
        scaleElement.style.webkitTransform =
        scaleElement.style.transform =
            'scale(' + scale + ')';
    },
    onend: function (event) {
        resetTimeout = setTimeout(reset, 1000);
        scaleElement.classList.add('reset');
    }
});

function reset () {
    scale = 1;
    scaleElement.style.webkitTransform =
    scaleElement.style.transform =
        'scale(1)';
}

// prevent browser's native drag on the image
gestureArea.addEventListener('dragstart', function (event) {
    event.preventDefault();
})

Use in SVG files

<!-- enable javascript to view a demo -->
<object id="star-demo" type="image/svg+xml" data="repo/demo/star.svg"></object>

<p>
interact.js is referenced within the <a href="repo/demo/star.svg">star.svg</a>
file as well as <a
    href="https://github.com/taye/interact.js/tree/master/demo/js/star.js">another
script</a> to bind and handle drag event listeners
</p>
/* enable javascript to view a demo */
#star-demo {
    display: block;
    margin: auto;
}