In this jsFiddle I have an interact.js rect that can be resized with a snap of 10px by 10px. The rect is positioned in x = 95px, and when I move the left side to the left, it moves to x = 90x. This is fine, however the right side also moves to the right and it shouldn't.
What's wrong with this code? The rect has handles, is that creating the problem?
.on('resizemove', function(event) {
const target = event.target.querySelector('rect');
for (const attr of ['width', 'height']) {
let v = Number(target.getAttribute(attr));
v += event.deltaRect[attr];
target.setAttribute(attr, Math.round(v/10)*10);
}
for (const attr of ['top', 'left']) {
const a = attr == 'left' ? 'x' : 'y';
let v = Number(target.getAttribute(a));
v += event.deltaRect[attr];
target.setAttribute(a, Math.round(v/10)*10);
}
findLocations(rect, handles);
});
Ahh, I see the problem. Here's what's happening: when the rectangle is resized, you're rounding both the size and the position. This has the following effect that:
x = 95
to x = 115
. It has its left side moved by -3
units. It is now 92
to 115
.115 - 92 = 23
units across, so you round to the nearest ten: 20
units.92
, so you move it to 90
. This slides the entire, now resized, rectangle over.You'll need to handle the top
and left
cases differently from the right
and bottom
cases, since the former two update the rectangles position on top of its size. On top of that, you'll have to only round the respective side that was changed: you don't want to round the bottom when you move the right.
left
or top
...
x
or y
to the new position, roundedwidth
or height
, since that will move right
or bottom
right
or bottom
...
x
or y
, since rounding them will budge the whole rectangle overx
or y
, since they'll be zero, but we can't round them!width
or hight
, but round this timeThat's quite a few cases to check for, but by using a function, this isn't too hard to see how it all works:
.on('resizemove', function(event) {
const target = event.target.querySelector('rect');
function changeVal(attr, change, round) {
let val = Number(target.getAttribute(change));
val += event.deltaRect[attr];
if (round) val = Math.round(val / 10) * 10;
target.setAttribute(change, val);
}
let round = false;
if (event.deltaRect.top != 0) round = true;
changeVal('top', 'y', round);
round = false;
if (event.deltaRect.left != 0) round = true;
changeVal('left', 'x', round);
round = false;
if (event.deltaRect.right != 0) round = true;
changeVal('width', 'width', round);
round = false;
if (event.deltaRect.bottom != 0) round = true;
changeVal('height', 'height', round);
findLocations(rect, handles);
});
Shortening this and changing to the same loop style as before:
.on('resizemove', function(event) {
const target = event.target.querySelector('rect');
const attributes = [
{ check: 'top', change: 'y' },
{ check: 'left', change: 'x' },
{ check: 'right', change: 'width' },
{ check: 'bottom', change: 'height' }
];
for (const {check, change} of attributes) {
let val = Number(target.getAttribute(change));
val += event.deltaRect[check];
if (event.deltaRect[check]) val = Math.round(val / 10) * 10;
target.setAttribute(change, val);
}
findLocations(rect, handles);
});
This uses ES6 destructuring assignment, so it won't work in IE.
There still seems to be some jankiness on the right edge when resizing the left edge, but I think that's an error with rounding...?
Even if not, I hope this is enough to get you started.