Home > Software design >  Absolute positioning messed up by CSS rotation
Absolute positioning messed up by CSS rotation

Time:12-11

I've made a tiny example below showcasing the behavior I currently get and the behavior I want.

// Rotated div
rotated.style.left = "50px";
rotated.style.top = "100px";
// Original "untouched" div
original.style.left = "50px";
original.style.top = "100px";
// Where the rotated div *should* be
expected.style.left = "-10px";
expected.style.top = "160px";
div {
  position: absolute;
  height: 80px;
  width: 200px;
  opacity: 0.5;
  mix-blend-mode: overlay;
}

#rotated {
  transform: rotateZ(90deg);
  background: blue;
}

#original {
  background: red;
}

#expected {
  transform: rotateZ(90deg);
  background: green;
}
<div id="rotated"></div>
<div id="original"></div>
<div id="expected"></div>

The red div is the "original" div that I have not applied any transformations to. The blue div is rotated by 90 degrees. The red and blue div are both shifted by the same values, but clearly their corners don't line up. The green div is the expected (desired) position of the blue div.

As you can see, the left and top is not really working as desired. I understand why it isn't, but I'm looking for some solutions or workarounds. I have searched online and found the transform-origin property but I've got some problems using it. This is because the elements I'm looking to move are created dynamically. They have unknown widths and heights, and on top of that, the widths and heights will change later on!

I know for this static example I can just add transform-origin: 40px 40px; to (which is just the height / 2 twice) div#rotated and it'll work, but in my project that means I'd have to set this property on every element and update it every time I update the element's dimensions.

I just don't think this is that great and I'm looking for one of two possible solutions:

  • A pure CSS solution that somehow gets the height of the selected element and uses that as the transform-origin (or just any pure CSS solution that works)

  • Using JavaScript to calculate the corrected position (in this static example, I should be able to get -10, 160 as the position of the element) every time I want to move an element.

--- update ---

This problem is further complicated because if the rotation is 180deg or 270deg then the transform-origin of 40px 40px no longer works. I'd have to compute a new transform-origin every time I want to move an element... This is something I'd really like to avoid...

CodePudding user response:

You could add a translate in your expected transform

.wrapper {
  position: relative;
  margin: 200px 0 0 200px;
  width: 200px;
  height: 200px;
  border: 1px solid black;
}

div {
  position: absolute;
  height: 80px;
  width: 200px;
  opacity: 0.5;
  mix-blend-mode: overlay;
}

#rotated {
  transform: rotateZ(90deg);
  background: blue;
}

#original {
  background: red;
}

#expected {
  transform: rotateZ(90deg) translateY(-100%);
  background: green;
  transform-origin: 0 0;
}
<div >
  <div id="rotated"></div>
  <div id="original"></div>
  <div id="expected"></div>
</div>

I put all in a wrapper with a border. Transform origin is from bounding box

CodePudding user response:

second snippet with several transform rotated translate

if I still didn't understand exactly the exact rotation you need, you can play with parameters translate:

  • translateY
  • translateX
  • translate with 2 values

always check the wrapper around it's the bounding box: 0 0 refers to top left

.wrapper1 {
  position: relative;
  top: 100px;
  left: 100px;
  width: 200px;
  height: 200px;
  border: 1px solid black;
}

.wrapper2 {
  position: relative;
  top: 250px;
  left: 100px;
  width: 200px;
  height: 200px;
  border: 1px solid black;
}

.wrapper3 {
  position: relative;
  top: 400px;
  left: 100px;
  width: 200px;
  height: 200px;
  border: 1px solid black;
}

.wrapper4 {
  position: relative;
  top: 550px;
  left: 100px;
  width: 200px;
  height: 200px;
  border: 1px solid black;
}

div {
  position: absolute;
  height: 80px;
  width: 200px;
  opacity: 0.5;
  mix-blend-mode: overlay;
}

.rotated1 {
  transform: rotateZ(90deg);
  background: blue;
}

.rotated2 {
  transform: rotateZ(90deg) translateY(-100%);
  transform-origin: 0 0;
  background: blue;
}

.rotated3 {
  transform: rotateZ(90deg) translateY(-50%);
  transform-origin: 0 0;
  background: blue;
}

.rotated4 {
  transform: rotateZ(90deg) translate(-50%, -50%);
  transform-origin: 0 0;
  background: blue;
}

.original {
  background: red;
}
<div >
  <div ></div>
  <div ></div>
</div>
<div >
  <div ></div>
  <div ></div>
</div>

<div >
  <div ></div>
  <div ></div>
</div>
<div >
  <div ></div>
  <div ></div>
</div>

  • Related