My problem, when I drag elements, the element is far behind the cursor, so it looks like it's a ratio issue potentially?
The code:
interact('.element', {
context: '.lipstick__plane'
})
.draggable({
snap: {
targets: [
interact.createSnapGrid({x: 10, y: 10})
],
range: Infinity
},
inertia: true,
restrict: {
restriction: '#lipstick__plane__main',
elementRect: {top: 0, left: 0, bottom: 1, right: 1},
endOnly: true
}
})
.on('dragmove', function (event) {
$scope.$apply(function () {
var target = event.target,
x = (parseFloat(target.getAttribute('x')) || 0) + ((1020 / window.outerWidth) * event.dx),
y = (parseFloat(target.getAttribute('y')) || 0) + ((1020 / window.outerHeight) * event.dy);
var indx = event.target.getAttribute('data-index');
_.set($scope.stage.elements[indx], 'meta.XCord', x);
_.set($scope.stage.elements[indx], 'meta.YCord', y);
});
});
I am diving there, which got it closer to the cursor, but i could be trying numbers all day long...
My svg init block:
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1024" width="1024px" height="1024px" xml:space="preserve" id="lipstick__plane__main">
One thing I do think could be a problem, but i doubt it, is that the Angular digest happens after a requery to get the new x
and y
attribute?
This problem comes to happen since the inner setup of the SVG is set to 1024 Pixel in width and Height, but displayed in a different Size, smaller in this case, so the applied translation from the drag is scaled down and the element appears to be slower than the cursor.
To address this, you need to scale the translation vector accordingly. One way to gain the scaling factors sx
and sy
could be:
- query the resulting bounding box
bb
of the svg by getClientBoudingRect
sx = bb.width / 1024 // (original size), same for sy
Or, if you can access the svg
element you can use .getScreenCTM()
to get the transformation matrix. Which should look like this:
| a c e |
| b d f |
| 0 0 1 |
a
should represent sx
and d
sy
.
Edit
I have setup a little fiddle here which shows the basic approach. All in all, the solution is a matrix transformation of a vector and the puzzle to solve is to find the right matrix. Since any svg element may have its own transform, or as child of a group, may gain transform from its parents, it is not sufficient to use the root <svg>
's Current Transformation Matrix. That is why each SVGElement
implements the the SVGLocatable Interface, what enables you to gain the transform from the root <svg>
to the SVGElement
(getCTM), or, the document to the SVGElement
(getScreenCTM()). The opposite direction is described by the inverse of those matrices. (explanation on SO)
let svg = document.querySelector('svg'),
e = svg.querySelector('.myelement'),
ctm = e.getScreenCTM(),
point = svg.createSVGPoint(),
point2;
point.x = …;
point.y = …;
point2 = point.matrixTransform(ctm);
To conclude: Using the transformation matrix should be the preferred way, since it will (at least should) take all transformations into account, which could be applied to the svg by css, like rotations, 3d transforms and so on. The more specific you are, meaning the more you know the element you would like to transform, the higher the precision will be.
Because your SVG is scaled, your mouse coordinates are not 1:1 with your SVG coordinates. So you need to convert the mouse/screen coordinates to SVG coordinates in order to get the correct position for the dragged object.
See this question for how to do the conversion.
How do you convert screen coordinates to document space in a scaled SVG?