Sean Anderson
Sean Anderson

Reputation: 29371

Inconsistent widths when transforming scale

Why are the two rectangles below different widths?

The first one is:

The second one is:

1 * 50 = 50 and 100 * .5 = 50, no?

body {
  margin: 10px 30px;  
}
#box {
  width: 439px;
  height: 200px;
  border: 1px solid black;
  box-sizing: border-box;
}
.line {
  height: 10px;
  transform-origin: left;
}
#example1 {
  width: 1%;
  transform: scaleX(50);
  background-color: red;
}
#example2 {
  width: 100%;
  transform: scaleX(.5);
  background-color: blue;
}
<div id='box'>
  <div id="example1" class="line"></div>
  <div id="example2" class="line"></div>
</div>

Upvotes: 2

Views: 319

Answers (1)

Harry
Harry

Reputation: 89780

At least in Chrome, this behavior seems to be because of how the pixel equivalent of the percentage value is calculated.

  • The size of #example1 is 1% of 437px+ which is exactly 4.37px but it seems like Chrome (and possibly other browsers) round this value to the nearest whole number. Because of this rounding, when you scale it up 50 times the width becomes only 200px.
  • When you increase the parent size to 459px, you can see that #example1 line becomes 250px wide because 4.57 gets rounded up to 5px.
  • The size of #example2 is 437px (100%) and when this is scaled down to 0.5, the width becomes 218.5px. Any value that is less than 0.5 seems to be getting rounded down whereas anything that is equal to or above 0.5 gets rounded up and so here the width becomes 219px.
  • Because of the above you could see a difference of 19px between the width of the elements. In the snippet I have added a few other lines with pixel values for reference. The green colored one is 218px wide, yellow is 200px, purple is 218.4px, chocolate is 219px and brown is 218.5px. You can see how the green and purple are same width while chocolate and brown are 1px wider.
  • If you set the container width as a multiple of 100 then this problem goes away. Refer the second block in the snippet. Note that it still has a small deviation because of the border setting as 1% of 498px is 5px (so 50 times is 250px) but .5 times 498px is 249px. You can see that it matches perfectly if you remove the border (or the `box-sizing) and make the parent container's size a multiple of 100.

+ 437px because even though we say 100%, the border-width on either side is excluded to box-sizing: border-box.

body {
  margin: 10px 30px;
}
#box {
  width: 439px;
  height: 200px;
  border: 1px solid black;
  box-sizing: border-box;
}
.line {
  height: 10px;
  transform-origin: left;
}
#example1 {
  width: 1%;
  transform: scaleX(50);
  background-color: red;
}
#example2 {
  width: 100%;
  transform: scaleX(.5);
  background-color: blue;
}
#example3 {
  width: 218px;
  background-color: green;
}
#example4 {
  width: 200px;
  background-color: yellow;
}
#example5 {
  width: 218.5px;
  background-color: brown;
}
#example6 {
  width: 219px;
  background-color: chocolate;
}
#example7 {
  width: 218.4px;
  background-color: purple;
}
#box2{
  width: 500px;
  height: 200px;
  border: 1px solid black;
  box-sizing: border-box;
}
<div id='box'>
  <div id="example1" class="line"></div>
  <div id="example2" class="line"></div>
  <div id="example3" class="line"></div>
  <div id="example4" class="line"></div>
  <div id="example5" class="line"></div>
  <div id="example6" class="line"></div>  
  <div id="example7" class="line"></div>
</div>

<div id='box2'>
  <div id="example1" class="line"></div>
  <div id="example2" class="line"></div>  
</div>


Contrary to what I had mentioned earlier (in comment and answer), the blur at the end of the scaled-down line is probably not because of layer differences because in both cases the lines will get only a rendering layer and no graphics layer. This blur is something that I am not able to explain right now.

You can read more about the layer creation, compositing and rendering in the below article:

Upvotes: 2

Related Questions