Drag/resize/rotate

Touch-enabled dragging, resizing, rotating

by nichollascarter

HTML

<script src="https://rawgit.com/nichollascarter/js-drag-resize-rotate-tool/master/dist/js/subjx.js"></script>
<link rel="stylesheet" href="https://rawgit.com/nichollascarter/js-drag-resize-rotate-tool/master/dist/style/subjx.css">
<div style="width:100%; height:100%;">
  <div style="position: absolute; display: inline; box-sizing: border-box; color: black">
    Double click on element to activate/deactivate

    <div id="move">
      Move:
      <span></span>
    </div>
    <div id="resize">
      Resize:
      <span></span>
    </div>
    <div id="rotate">
      Rotate:
      <span></span>
    </div>
  </div>

  <svg id="svg-container" width="100%" height="50%" transform="scale(1, 1)">

    <rect class="drag-svg" x="552" y="209" width="30" height="30" stroke="black" fill="transparent" stroke-width="5" transform="rotate(0 567 224)"></rect>
    <rect class="drag-svg" x="469" y="211" rx="10" ry="10" width="30" height="30" stroke="black" fill="transparent" stroke-width="5" transform="rotate(0 484 226)"></rect>

    <circle cx="792" cy="152" r="20" stroke="red" fill="transparent" stroke-width="5" transform="rotate(0 792 152)"></circle>
    <ellipse class="drag-svg" cx="358" cy="222" rx="22.000000000000004" ry="17" stroke="red" fill="transparent" stroke-width="5" transform="rotate(0 358 222)"></ellipse>

    <line class="drag-svg" x1="626" x2="666" y1="200" y2="240" stroke="orange" fill="transparent" stroke-width="5" transform="rotate(0 646 220)"></line>
    <polyline class="drag-svg" points="599 73 604 83 609 78 614 93 619 88 624 103 629 98 634 113 639 108" stroke="orange" fill="transparent" stroke-width="5" transform="rotate(0 619 93)"></polyline>

    <polygon class="drag-svg" points="356 71 361 91 376 91 366 101 371 116 356 106 341 116 346 101 336 91 351 91" stroke="green" fill="transparent" stroke-width="5" transform="rotate(0 356 93.5)"></polygon>

    <path class="drag-svg" d="M451,91 Q471,66,481,91 T521,91 " fill="none" stroke="blue" stroke-width="5"...

CSS

html,
body {
  margin: 0;
  padding: 0;
  height: 100%;
}

.draggable {
  position: absolute;
  width: 150px;
  height: 50px;
  background-color: yellow;
}

.clonable {
  position: absolute;
  width: 50px;
  height: 50px;
  left: 0;
}

JavaScript

class ZoomContainer {

  constructor() {
    this.scale = 1;
  }

  zoom(e) {
    e.preventDefault();
    if (!e.shiftKey) return;
    let {
      scale
    } = this;
    const newScale = scale + (e.wheelDelta < 0 ? -0.25 : 0.25);
    scale = newScale > 2 || newScale < 0.5 ? scale : newScale;
    subjx(e.currentTarget).css({
      transform: `scale(${scale},${scale})`
    });
    this.scale = scale;
  }

}

const zoomSvg = new ZoomContainer(),
  zoomHtml = new ZoomContainer();

subjx('#svg-container')
  .on('mousewheel', e => {
    zoomSvg.zoom(e);
  });

subjx('#stack')
  .on('mousewheel', e => {
    zoomHtml.zoom(e);
  });

// define methods
const methods = {
  onInit(el) {
    console.log('init');
  },
  onMove({
    dx,
    dy
  }) {
    subjx('#move > span')[0].innerHTML = `x: ${dx}px; y: ${dy}px`;
  },
  onResize({
    dx,
    dy
  }) {
    subjx('#resize > span')[0].innerHTML = `x: ${dx}px; y: ${dy}px`;
  },
  onRotate({
    delta
  }) {
    subjx('#rotate > span')[0].innerHTML = `${Math.floor(delta * 180 / Math.PI)}deg`;
  },
  onDrop() {
    console.log('is dropped');
  },
  onDestroy() {
    console.log('is destroyed');
  }
};
// additional options
const svgOptions = {
  container: '#svg-container',
  //restrict: '#svg-container',
  rotationPoint: true,
  snap: {
    x: 30,
    y: 30,
    angle: 25
  },
  ...methods
};

const svgs =
  subjx('.drag-svg').drag(svgOptions);

subjx(svgs.controls).on('dblclick', () => {
  svgs.disable();
});

// double click activating/deactivating the drag method
subjx('.drag-svg').on('dblclick', e => {
  if (e.currentTarget.classList.contains('sjx-drag')) return;
  const xDraggable = subjx(e.currentTarget).drag(svgOptions);
  // adding event to controls
  const controls = xDraggable.controls;
  subjx(controls).on('dblclick', () => {
    xDraggable.disable();
  });
});

const options = {
  container: '#container',
  //restrict: '#stack',
  //proportions: true,
  each: {
    move: true,
    resize: true
  },
  snap: {
    x:...