How do I calculate top and left when resizing translated and rotated elements

Viewed 347

I have a system where I'm able to drag and resize DIVs. You can grab from the top and it will grow from the bottom. Grab from the bottom and it grows down from the top. Same with left, right, and corners.

This is all done with CSS width/height and translate(x,y) and works well.

Now I'm adding rotation and using something like this:

style="transform: translate(100px,100px) rotate(-30deg);width:100px;height:50px;"

Again, this is all good.

But when I change the width or height, the element shifts up/down or left/right.

See the following. The green div is the original, and the red one has the width expanded. You can see how it shifts.

<html>
  <head>
    <style>
        body {
            position: relative;
        }

        .thing {
            position:absolute;
            border: solid 1px #0f0;
            transform: translate(100px, 100px) rotate(-30deg);
        }

        .thing2 {
            position:absolute;
            border: solid 1px #f00;
            transform: translate(100px, 100px) rotate(-30deg);
        }
    </style>
  </head>

  <body>
    <div class="thing" style="width:100px;height:50px;"></div>
    <div class="thing2" style="width:150px;height:50px;"></div>
  </body>
</html>

I know I need to recalculate the top/left based on trig, but haven't found the right calculations to make it work.

Also, I'd like to keep the rotation pivot around the center, so I can't just change the pivot point to one of the corners.

2 Answers

here a solution with wrapper

<div class="thing-wrapper">
  <div class="thing left-top">expand right</div>
</div>

<div class="thing-wrapper">
  <div class="thing right-top">expand left</div>
</div>

<div class="thing-wrapper">
  <div class="thing left-bottom">expand top</div>
</div>

<div class="thing-wrapper">
  <div class="thing right-bottom">expand bottom</div>
</div>

.thing-wrapper {
  position: absolute;
}

.thing {
  border: solid 1px #0f0;
  width: 100px;
  height: 100px;
  position: absolute;
  transform: translate(100px, 100px) rotate(-44deg);
  transition: 2s;
}

.thing.expand-left:hover,
.thing.expand-right:hover{
  width: 200px !important;
  height: 100px !important;
}

.thing.expand-top:hover,
.thing.expand-bottom:hover{
  width: 100px !important;
  height: 200px !important;
}

.expand-left {
  left: 0;
  top: 0;
  transform-origin: left top;
}

.expand-right {
  right: 0;
  top: 0;
  transform-origin: right top;
}

.expand-top {
  left:0;
  bottom: 0;
  transform: translate(300px, 300px) rotate(-44deg);
  transform-origin: left bottom;
}

.expand-bottom {
  left: 0;
  right: 0;
  transform: translate(300px, 300px) rotate(-44deg);
  transform-origin: left top;
}

https://jsfiddle.net/dt7phfk9/32/

positioning the thing in the wrapper works for expanding in only one direction.

I'll post an answer here in case anyone else hits this problem.

After locating this post: https://www.py4u.net/discuss/914662 the math is a little more clear.

It first grabs the location of the object along with the rotation, then for the new position it gets the location and then calculates the diff. Here's my functionalized version:

function calcNewPos(curr_x, curr_y, curr_w, curr_h, new_w, new_h, angle) {
    // convert angle to radians
    angle = angle * Math.PI / 180

    //initial position.
    let pos = {left: curr_x, top: curr_y};
    
    //Get position after rotation with original size
    let x = -curr_width/2;
    let y = curr_height/2;
    let new_x = y * Math.sin(angle) + x * Math.cos(angle);
    let new_y = y * Math.cos(angle) - x * Math.sin(angle);
    let p1 = {left: new_x - x, top: new_y - y};

    //Get position after rotation with new size
    x = -new_w/2;
    y = new_h/2;
    new_x = y * Math.sin(angle) + x * Math.cos(angle);
    new_y = y * Math.cos(angle) - x * Math.sin(angle);
    let p2 = {left: new_x - x, top: new_y - y};

    //Get the difference between the two positions
    let offset = {left: p2.left - p1.left, top: p2.top - p1.top};

    //Calculate the correction
    return {left: pos.left - offset.left, top: pos.top + offset.top};
}

I'm sure the math could be reduced in size, but this example lays it out there in an easy to understand way.

Related