Interact.js: dragging SVG element when using ViewBox

2.2k Views Asked by At

I'm building an application in which I'm hoping to use SVG and interact.js together for drag and drop of SVG elements. My SVG is using a ViewBox in order to scale the graphics correctly on different resolutions, but the issue is that when I drag, the mouse moves "faster" than the element its moving.

I understand that is is an issue between the coordinate system of the screen and the SVG, and I've found what appears to be an answer here: Interact JS, Drag svg elements inside viewboxed svg?

However, I can't figure out how to tie the marked answer at that link above into my code. Every different thing I try results in even more bizarre behavior. I'm trying to integrate it into the drag and drop example provided on the interact.js site:

interact('.draggable')
    .draggable({
        // enable inertial throwing
        inertia: true,
        // keep the element within the area of it's parent
        restrict: {
            restriction: "parent",
            endOnly: true,
            elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
        },
        // enable autoScroll
        autoScroll: true,

        // call this function on every dragmove event
        onmove: dragMoveListener,
        // call this function on every dragend event
        onend: function (event) {
            //removed this code for my test
        }
    });

function dragMoveListener (event) {
    var target = event.target,
        // keep the dragged position in the data-x/data-y attributes
        x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
        y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

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

    // update the posiion attributes
    target.setAttribute('data-x', x);
    target.setAttribute('data-y', y);
}

The closest I've managed to get is with this code in place of the dragMoveListener above:

function dragMoveListener(event) {
    var target = event.target;

    var svg = document.querySelector('svg');
    var svgRect = svg.getBoundingClientRect();
    var ctm = target.getScreenCTM();
    var point = svg.createSVGPoint();
    var point2;

    point.x = event.clientX;
    point.y = event.clientY;

    point2 = point.matrixTransform(ctm);

    var x = point2.x;
    var y = point2.y;

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

    // update the posiion attributes
    target.setAttribute('data-x', x);
    target.setAttribute('data-y', y);
}

But this causes the element to be very far from the mouse, though it moves almost in sync (seems to be a bit off still). How can I get this to work, and what am I possibly misunderstanding about the solution presented at the linked question based on my "almost" solution above?

Here is the relevant HTML. All of the settings are just for testing purposes and may not be final (e.g. 2000 and 4000 are likely larger than I will ultimately need).

<svg id="svgArea" style="width:100%; border: 1px solid black" viewBox="0 0 2000 4000">
    <rect id="item" width="100" height="200" stroke="#000000" stroke-width="5" fill="#ff0000" class="draggable" ></rect>
</svg>
1

There are 1 best solutions below

1
Sergey Rudenko On BEST ANSWER

here is the working snippet where drag rotate is using proper SVG to DOM coordinates conversion:

Codepen drag rotate snippet

And this in particular what you are missing:

  mouse.x = event.clientX;
  mouse.y = event.clientY;
  mouse = mouse.matrixTransform(mainSVG.getScreenCTM().inverse());

So in your case you should getCTM from svgArea element.