v3

by dapo

HTML

<script src="https://gistcdn.githack.com/dasDaniel/f3cebc1274339055729c6e887ca8d2ad/raw/6862a35fe666dc53fe72f20e3277d1de408e0c5a/vue.global.js"></script>
<div id="app"></div>

<template id="appTemplate">
  <my-div>
    <svg width="100%" height="400px" class="draggable">
      <my-svg-rect></my-svg-rect>
      <g transform="matrix(1 0 0 1 50 150)">
        <svg-group>
          <text fill="#aaddff" stroke="#06131d" font-size="38">
            HI THERE
          </text>
          >
        </svg-group>
      </g>
    </svg>
  </my-div>
</template>

<template id="myDiv">
  <div ref="el" class="dragbox dragable" :style="style">
    <slot></slot>
  </div>
</template>

<template id="mySvgRect">
  <rect ref="el" v-bind="svgProps.attr" width="300" height="200" fill="#6daad8">
    <slot></slot>
  </rect>
</template>

<template id="svgGroup">
  <g ref="el" v-bind="svgProps.attr">
    <slot></slot>
  </g>
</template>

CSS

.dragable {
  touch-action: none;
  user-select: none;
}

.dragbox {
  font-family: "Lucida Sans", Geneva, Verdana, sans-serif;
  width: 40%;
  max-width: 90%;
  min-width: 320px;
  min-height: 6.5em;
  margin: 0;
  color: rgb(109, 170, 216);
  background-color: rgb(187, 195, 209);
  border-radius: 16px;
  padding: 16px;
  -webkit-transform: translate(0px, 0px);
  transform: translate(0px, 0px);
  transition: transform 0.1s ease-in, box-shadow 0.1s ease-out;
}

JavaScript

const {
  reactive,
  computed,
  ref,
  onMounted,
  watch
} = Vue;

const makeDragable = element => {
  const position = reactive({
    init: false,
    x: 0,
    y: 0,
    width: 0,
    height: 0,
    isDraging: false,
    dragStartX: null,
    dragStartY: null
  });

  const onMouseDown = e => {
    e.stopPropagation();
    let {
      clientX,
      clientY
    } = e;
    position.dragStartX = clientX - position.x;
    position.dragStartY = clientY - position.y;

    position.isDraging = true;

    window.addEventListener("mouseup", onMouseUp);
    window.addEventListener("mousemove", onMouseMove);
  };

  const onMouseMove = e => {
    e.stopPropagation();
    let {
      clientX,
      clientY
    } = e;
    position.x = clientX - position.dragStartX;
    position.y = clientY - position.dragStartY;
  };

  const onMouseUp = e => {
    e.stopPropagation();
    let {
      clientX,
      clientY
    } = e;
    position.isDraging = false;
    position.dragStartX = null;
    position.dragStartY = null;
    window.removeEventListener("mouseup", onMouseUp);
    window.removeEventListener("mousemove", onMouseMove);
  };

  watch(element, (element, prevElement, onCleanup) => {
    let rect;
    if (element instanceof HTMLElement) {
      rect = element.getBoundingClientRect(element);
    } else if (element instanceof SVGGraphicsElement) {
      rect = element.getBBox(element);
    }
    if (rect === undefined) return {
      position: {}
    };

    position.init = true;
    position.x = Math.round(rect.x);
    position.y = Math.round(rect.y);
    position.width = Math.round(rect.width);
    position.height = Math.round(rect.height);

    element.addEventListener("mousedown", onMouseDown);

    onCleanup(() => {
      document.removeEventListener("mouseup", onMouseUp);
      document.removeEventListener("mousemove", onMouseMove);
      if (element instanceof HTMLElement) {
        element.removeEventListener("mousedown", onMouseDown);
      }
    })
  });

 ...