问题
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);
});
回答1:
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:
- You have a rect whose sides go from
x = 95
tox = 115
. It has its left side moved by-3
units. It is now92
to115
. - You check the width: it is
115 - 92 = 23
units across, so you round to the nearest ten:20
units. - You check the position: It is on
92
, so you move it to90
. 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.
- For
left
ortop
...- Move the
x
ory
to the new position, rounded - Don't round
width
orheight
, since that will moveright
orbottom
- Move the
- For
right
orbottom
...- Do nothing to
x
ory
, since rounding them will budge the whole rectangle over - Well, we can still change
x
ory
, since they'll be zero, but we can't round them! - Only change the
width
orhight
, but round this time
- Do nothing to
That'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.
来源:https://stackoverflow.com/questions/61010102/resizing-with-snap-in-interact-js