I have spent many days trying to make an item resizable that is rotated with interact.js.
This is the code that I have at this moment, I will try to explain the concept.
We have a selector item for two reasons, because the container could be scaled with css transform (like a zoom), and we need to have the selector outside and because we have a multiselection, and the selector grow if I have two rectangle selected, but in this case this is not the main problem and we have calculated the scaled proportion without problems and other things.
When the selector is resize, it take the rectangle, and make the same with the width, height, left, top and rotation.
Javascript:
// TAP - CLICK EVENT (just for positioning the selector)
interact('#rectangle').on('tap', event => {
console.log('Tap Box!');
event.stopPropagation();
const $rectangleCloned = $('#rectangle').clone();
const previousTransform = $rectangleCloned.css('transform');
$rectangleCloned.css('transform', 'none');
$rectangleCloned.css('opacity', '0');
$rectangleCloned.css('display', 'block');
$('#container').append($rectangleCloned);
const values = $rectangleCloned[0].getBoundingClientRect();
// This is just a trick for fast implementation:
$('#selector').css('top', values.y);
$('#selector').css('left', values.x);
$('#selector').css('width', values.width);
$('#selector').css('height', values.height);
$('#selector').css('transform', previousTransform);
$rectangleCloned.remove();
return values;
});
interact('.pointer9').draggable({
max: 1,
onmove: event => {
const angleDeg =
Math.atan2(
centerRotate.posY - event.pageY,
centerRotate.posX - event.pageX
) *
180 /
Math.PI;
console.log(this.rotate);
const prevAngle = this.rotate - angleInitial;
const angle = parseInt(angleDeg) + prevAngle;
this.$rectangle.css({
transform: 'rotate(' + angle + 'deg)'
});
this.$selector.css({
transform: 'rotate(' + angle + 'deg)'
});
},
onstart: event => {
const data = event.interactable.getRect(event.target.parentNode);
this.centerRotate = {
posX: data.left + data.width / 2,
posY: data.top + data.height / 2
};
this.angleInitial =
Math.atan2(
centerRotate.posY - event.pageY,
centerRotate.posX - event.pageX
) *
180 /
Math.PI;
this.$rectangle = $('#rectangle');
this.$selector = $('#selector');
this.rotate = $rectangle.attr('angle') || 0;
},
onend: event => {
const $box = $('#selector');
const matrix = $box.css('transform');
const values = matrix
.split('(')[1]
.split(')')[0]
.split(',');
var a = values[0];
var b = values[1];
var angle = Math.round(Math.atan2(b, a) * (180 / Math.PI));
$rectangle.attr('angle', angle);
}
});
interact('#selector')
.resizable({
// resize from all edges and corners
edges: {
left: true,
right: true,
bottom: true,
top: true
},
// keep the edges inside the parent
restrictEdges: {
outer: 'parent',
endOnly: true,
},
// minimum size
restrictSize: {
min: {
width: 100,
height: 50
},
},
inertia: true,
})
.on('resizemove', function(event) {
var target = event.target,
x = parseFloat($(target).offset().left) || 0,
y = parseFloat($(target).offset().top) || 0;
// update the element's style
target.style.width = event.rect.width + 'px';
target.style.height = event.rect.height + 'px';
// translate when resizing from top or left edges
x += event.deltaRect.left;
y += event.deltaRect.top;
target.style.left = x + 'px';
target.style.top = y + 'px';
$('#rectangle')[0].style.left = target.style.left;
$('#rectangle')[0].style.top = target.style.top;
$('#rectangle')[0].style.width = target.style.width;
$('#rectangle')[0].style.height = target.style.height;
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
});
CSS:
#container {
width: 500px;
height: 400px;
top: 0;
left: 0;
position: absolute;
background-color: #CCC;
}
#rectangle {
top: 50px;
left: 50px;
width: 120px;
height: 60px;
background-color: red;
position: absolute;
}
#selector {
display: inline-block;
position: absolute;
pointer-events: none;
z-index: 9999;
top: -1000px;
/*Not showing at start*/
}
#selector .pointers {
display: inline-block;
position: absolute;
z-index: 2;
width: 10px;
height: 10px;
pointer-events: all;
}
#selector .pointers .point {
width: 10px;
height: 10px;
background-color: #fff;
border: 2px solid rgba(0, 0, 0, 0.9);
border-radius: 50%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
#selector .pointers.pointer1 {
top: -5px;
left: -5px;
}
#selector .pointers.pointer2 {
bottom: -5px;
left: -5px;
}
#selector .pointers.pointer3 {
top: -5px;
right: -5px;
}
#selector .pointers.pointer4 {
bottom: -5px;
right: -5px;
}
#selector .pointers.pointer-north {
top: -5px;
left: calc(50% - 5px);
}
#selector .pointers.pointer-south {
bottom: -5px;
left: calc(50% - 5px);
}
#selector .pointers.pointer-east {
right: -5px;
top: calc(50% - 5px);
}
#selector .pointers.pointer-west {
left: -5px;
top: calc(50% - 5px);
}
#selector .pointer-rotate {
border: 2px solid rgba(0, 0, 0, 0.9);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
border-radius: 50%;
cursor: rotate;
}
#selector .pointer9 {
bottom: -70px;
left: calc(50% - 11px);
display: inline-block;
width: 20px;
height: 20px;
background-color: #fff;
pointer-events: all;
position: absolute;
}
#selector .rotate-line {
border-left: 1px dashed #5f5f5f;
height: 40px;
position: absolute;
top: -40px;
left: calc(50% - 1px);
width: 1px;
}
HTML:
<div id="container">
<div id="rectangle">
</div>
<div id="selector">
<div class="pointers pointer1">
<div class="point"></div>
</div>
<div class="pointers pointer2">
<div class="point">
</div>
</div>
<div class="pointers pointer3">
<div class="point">
</div>
</div>
<div class="pointers pointer4">
<div class="point">
</div>
</div>
<div class="pointers pointer-north">
<div class="point">
</div>
</div>
<div class="pointers pointer-east">
<div class="point">
</div>
</div>
<div class="pointers pointer-south">
<div class="point">
</div>
</div>
<div class="pointers pointer-west">
<div class="point">
</div>
</div>
<span class="topline lines-resize" />
<span class="rightline lines-resize" />
<span class="botline lines-resize" />
<span class="leftline lines-resize" />
<div class="pointer-rotate pointer9" />
<div class="rotate-line" />
</div>
</div>
Fiddle for testing:
https://jsfiddle.net/ub70028c/46/
I have read about other people trying to make the same without not results...
Thanks!