Brecherchef
Brecherchef

Reputation: 397

CSS: Calculate the overflow of two overlapping rectangles after one has been rotated

kind of an odd question I know, but I wanted to know if it's possible to calculate the width a rectangle needs to be to perfectly fit into another rectangle after the first one has been rotate with transform: rotate. Here's a picture of what I'm trying to say:

Example of what I'm trying to achieve

Ideally I want this do be dynamic, so it doesn't depend on the actual with of the first rectangle.

I'm using SCSS and this is what I got so far:

.background-box {
  background: black;
  padding: rem(30px 40px);
  position: relative;
  width: auto;
  &:after {
    content: "";
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    @include transform(rotate(5deg));
    border: rem(2px) solid $primary;
    background: transparent;
    z-index: -1;
  }
}

HTML looks like this:

<div class="background-box">
    //CONTENT
</div>

Now I want the width of rectangle 2 (the one built by :after) to be X to fit inside the parent element. Something like width: calc(100% - overflow).

Is this possible or do I need to work around with something like width: 98%?

Upvotes: 2

Views: 818

Answers (1)

Temani Afif
Temani Afif

Reputation: 272842

Here is an illustration to better show the issue and the calculation we need to do:

.box {
  margin: 50px;
  width: 200px;
  height: 100px;
  outline: 2px solid;
  box-sizing: border-box;
}

.rect {
  width: 100%;
  height: 100%;
  border: 1px solid green;
  box-sizing: border-box;
  transform-origin: bottom left;
  background: linear-gradient(to bottom right, transparent 49%, red 49%, red 52%, transparent 52%);
  animation: change 5s linear infinite alternate;
}

@keyframes change {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(53deg);
  }
}
<div class="box">
  <div class="rect">
  </div>
</div>

As you can see, we have the diagonal line (the red one) that we need to decrease in order to avoid the overflow.

enter image description here

In the below figure we have our rotation angle (in blue) and the angle in purple that is based on the dimension of the rectangle. From this we have the following formula:

cos(PurpleAngle) = width/X
cos(PurpleAngle - BlueAngle) = width/X1 

where X1 is the width needed for our diagonal (delimited by the yellow cross) and X is the width of the diagonal (the full red line).

We can then write this:

X/X1 = cos(PurpleAngle - BlueAngle)/cos(PurpleAngle)

then

X/X1 = (cos(PurpleAngle)*cos(BlueAngle) + sin(PurpleAngle)*sin(BlueAngle))/cos(PurpleAngle)

We simplify to

X/X1 = cos(BlueAngle) + tan(PurpleAngle)*sin(BlueAngle)

We know the BlueAngle which is our rotation angle and we can easily find tan(PurpleAngle) which is simply Height/Width. Yes, we need to know the width and heigth or at least the ratio between both. In my case I used a 0.5 ratio so will have:

X/X1 = cos(BlueAngle) + 0.5*sin(BlueAngle)

Now we can see this as another rotation in the Y axis where we will have cos(B) = X1/X.

Example:

.box {
  margin: 50px;
  width: 200px;
  height: 100px;
  outline: 2px solid;
  box-sizing: border-box;
}

.rect {
  width: 100%;
  height: 100%;
  border: 1px solid green;
  box-sizing: border-box;
  transform:rotate(20deg) rotateY(25.84deg);
  background: linear-gradient(to bottom right, transparent 49%, red 49%, red 52%, transparent 52%);
  animation: change 5s linear infinite alternate;
}
<div class="box">
  <div class="rect">
  </div>
</div>

Or we can also see it as a scale transform with a factor equal to X1/X:

Example:

.box {
  margin: 50px;
  width: 200px;
  height: 100px;
  outline: 2px solid;
  box-sizing: border-box;
}

.rect {
  width: 100%;
  height: 100%;
  border: 1px solid green;
  box-sizing: border-box;
  transform:rotate(20deg) scale(0.9);
  background: linear-gradient(to bottom right, transparent 49%, red 49%, red 52%, transparent 52%);
  animation: change 5s linear infinite alternate;
}
<div class="box">
  <div class="rect">
  </div>
</div>

So all you want to do is to know the ratio, and the rotation angle then use the fomula to find the scale value or rotation value.

More examples:

.box {
  margin: 20px;
  width: 150px;
  height: 75px;
  display:inline-block;
  vertical-align:top;
  outline: 2px solid;
  box-sizing: border-box;
}

.rect {
  width: 100%;
  height: 100%;
  border: 1px solid green;
  box-sizing: border-box;
}
<!-- 1/(cos(20deg) + 0.5*sin(20deg)) -->
<div class="box">
  <div class="rect" style="
  transform:rotate(20deg) scale(0.9);">
  </div>
</div>
<!-- 1/(cos(45deg) + 1*sin(45deg)) -->
<div class="box" style="height:150px">
  <div class="rect" style="
  transform:rotate(45deg) scale(0.707);">
  </div>
</div>

<!-- 1/(cos(5deg) + 0.333*sin(5deg)) -->
<div class="box" style="height:50px">
  <div class="rect" style="
  transform:rotate(5deg) scale(0.972);">
  </div>
</div>

<!-- 1/(cos(15deg) + 2*sin(15deg)) -->
<div class="box" style="height:300px;">
  <div class="rect" style="
  transform:rotate(15deg) scale(0.674);">
  </div>
</div>


Using SASS you can consider this link (https://unindented.org/articles/trigonometry-in-sass/) where you can find the definition of cos() sin() then you simply do:

@function scale-factor($angle, $ratio) {
  @return 1/(cos($angle) + $ratio*sin($angle)); 
}


div {
  transform:rotate(15deg) scale(scale-factor(15deg,2));
}

And you will get this:

div {
  transform: rotate(15deg) scale(0.6740525224);
}

For the CSS solution, we can also rely on calc() to make the calculation easier by doing scale(calc(1 / (cos(a) + r*sin(a))) where we only need to calculate cos() and sin()

Upvotes: 2

Related Questions