Home > Net >  Calculate the position of an element that has been translated and scaled down
Calculate the position of an element that has been translated and scaled down

Time:11-07

I'm building a "3D carousel" from scratch.

The carousel contains 3 slides, which are stacked on-top on each other using CSS Grid (they all have the same grid-area: 1/1).

The slide 1 stays at the center of the carousel.

I'm using a translate to offset slides 2 (to the left) and 3 (to the right), which gives a basic 3D effect.

I would like the container of my carousel to perfectly fit the content (the 3 slides), so I'm setting a padding on the left and right side of my carousel, to compensate from the "overflow" of slides 2 and 3.

So far, my padding is just equal to the offset from slides 2 and 3. (If I translateX(-100px) my slide 2, a padding-left: 100px works fine.

It gets trickier when I scale(0.5) my slides 2 and 3: the value of the padding is off, I can manually find a value that works but I can't find the formula which would pfit everytime I want to change either the main slide width, the offset or the scale factor.

I have made a Codepen to illustrate the issue, which occurs at less than 1024px (you have to resize the window to see the problem.

https://codepen.io/AymarTissedre/pen/gOKMEwa

window.onload = function (event) {
  document.querySelector("span").innerHTML = window.innerWidth   'px'
};

window.onresize = function (event) {
  document.querySelector("span").innerHTML = window.innerWidth   'px';
};
* {
  box-sizing: border-box;
}

div {
  --offset: 100px;
  --scaling: 0.75;
  --width: 200px;
  --padding: calc(
    var(--width) / 2 * var(--scaling)
  );
  border: 1px dashed black;
  background: lightgrey;
  display: inline-flex;
  width: var(--width);
  padding: 0 var(--padding);
  box-sizing: content-box;
}
div ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
}
@media screen and (max-width: 1025px) {
  div ul {
    --offset: 75px;
    --scaling: 0.75;
    --width: 150px;
  }
}
div ul li {
  grid-area: 1/1;
  border: 1px solid black;
  width: 200px;
  height: 200px;
}
div ul li:nth-child(1) {
  background-color: red;
  z-index: 1;
}
div ul li:nth-child(2) {
  background-color: blue;
  transform: translateX(var(--offset)) scale(var(--scaling));
}
div ul li:nth-child(3) {
  background-color: green;
  transform: translateX(calc(var(--offset) * -1)) scale(var(--scaling));
}
<div>
  <ul>
    <li>Card 1</li>
    <li>Card 2</li>
    <li>Card 3</li>
  </ul>
</div>
<p>At less than 1024px resolution wide, padding is not correctly calculated (width of the Card 1 changes, offset of Cards 2 and 3 too.)</p>
<p>The padding (lightgrey zone) should <strong>NOT</strong> be visible at the left / right side under 1024 pixels </p>
<p> Current viewport width: <span></span> </p>

Here is a list of padding values that I found manually, that perfectly fit with the corresponding width, offset and scaling. I didn't manage to find the common formula that links them. Beware as the values may be rounded (e.g. 68px = 67.5px).

  //     width(px)  offset(px)  scaling        padding(px)
  //        150       50          0.50        =    12
  //        300      100          0.50        =    25
  //        200      100          0.50        =    50
  //        150      100          0.50        =    62
  //        350      150          0.50        =    62
  //        250      100          0.75        =    68
  //        300      175          0.50        =   100
  //        300      200          0.50        =   125
  //        300      200          0.75        =   162

Happy to provide more details if needed.

CodePudding user response:

The formula should be:

--padding: calc(var(--offset) - var(--width)*(1 - var(--scaling))/2)

From the offset your remove the difference between the non scaled element and the scaled one (1 - var(--scaling)) then you divide by 2 because we only remove one side.

Also make sure to update the variables on the div not the ul because the padding and width are defined on the div

window.onload = function (event) {
  document.querySelector("span").innerHTML = window.innerWidth   'px'
};

window.onresize = function (event) {
  document.querySelector("span").innerHTML = window.innerWidth   'px';
};
* {
  box-sizing: border-box;
}

div {
  --offset: 100px;
  --scaling: 0.75;
  --width: 200px;
  --padding: calc(var(--offset) - var(--width)*(1 - var(--scaling))/2);
  border: 1px dashed black;
  background: lightgrey;
  display: inline-flex;
  width: var(--width);
  padding: 0 var(--padding);
  box-sizing: content-box;
}
div ul {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  width: 100%;
}
@media screen and (max-width: 1025px) {
  div {
    --offset: 75px;
    --scaling: 0.75;
    --width: 150px;
  }
}
div ul li {
  grid-area: 1/1;
  border: 1px solid black;
  aspect-ratio:1;
}
div ul li:nth-child(1) {
  background-color: red;
  z-index: 1;
}
div ul li:nth-child(2) {
  background-color: blue;
  transform: translateX(var(--offset)) scale(var(--scaling));
}
div ul li:nth-child(3) {
  background-color: green;
  transform: translateX(calc(var(--offset) * -1)) scale(var(--scaling));
}
<div>
  <ul>
    <li>Card 1</li>
    <li>Card 2</li>
    <li>Card 3</li>
  </ul>
</div>
<p>At less than 1024px resolution wide, padding is not correctly calculated (width of the Card 1 changes, offset of Cards 2 and 3 too.)</p>
<p>The padding (lightgrey zone) should <strong>NOT</strong> be visible at the left / right side under 1024 pixels </p>
<p> Current viewport width: <span></span> </p>

  • Related