9
votes

I'm scaling a div up with the transform property, but I want to keep its children (which have 1px width or height) the same size. I counter-scaled them by .5, with the expected result that an element of 1px scaled by 2, and then .5, should end up back at 1px, but they wind up a blurry 2px.

Here's the box before scaling it:

.container {
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: #EEE;
    position: absolute;
}

.outline {
    position: absolute;
    background: #1899ef;
    z-index: 999999;
    opacity: 1 !important;
}

.outlineBottom, .outlineTop {
  width: 100%;
  height: 1px;
}

.outlineLeft, .outlineRight {
    height: 100%;
    width: 1px;
}

.outlineRight {
    right: 0px;
}

.outlineBottom {
    bottom: 0px;
}
<div class="container">
    <div class="outline outlineTop"></div>
    <div class="outline outlineRight"></div>
    <div class="outline outlineBottom"></div>
    <div class="outline outlineLeft"></div>
</div>

As you can see, the elements at the edges are a clear, dark 1px blue. Here's what the box looks like after scaling, though:

.container {
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: #EEE;
    position: absolute;
    transform: scale(2);
}

.outline {
    position: absolute;
    background: #1899ef;
    z-index: 999999;
    opacity: 1 !important;
    transform: scale(.5);
}

.outlineBottom, .outlineTop {
  width: 100%;
  height: 1px;
  transform: scale(1,.5);
}

.outlineLeft, .outlineRight {
    height: 100%;
    width: 1px;
    transform: scale(.5,1);
}

.outlineRight {
    right: 0px;
}

.outlineBottom {
    bottom: 0px;
}
<div class="container">
    <div class="outline outlineTop"></div>
    <div class="outline outlineRight"></div>
    <div class="outline outlineBottom"></div>
    <div class="outline outlineLeft"></div>
</div>

And here's a post-scaled render from Chrome 41.0.2272.89 Mac, which is what I'm running.

Adding transform-3d(0, 0, 0) didn't appear to help. A solution was found using the zoom property, but since zoom isn't well supported I'd like to avoid that. Adding filter: blur(0px); didn't appear to have any effect either.

It was posited in chat that perhaps the children are first scaled to .5 and then doubled in size, causing them to be scaled down to .5px and then back up from there. Is there any way to ensure the order that they're rendered in causes them to first be scaled up to 2px and then halved? Against my better judgement, I tried forcing the render order with JS, but unsurprisingly, that didn't have any effect (though, interestingly, the bottom element did maintain its original color).

Failing that, are there any other solutions floating around out there? I can't be the only one who's run into this problem.

1
Any chance you can show a before / after? It is unclear what the issue isAdjit
Before / after scaling? Not sure I understand what you're asking.Elliot Bonneville
Yes, before and after the scaling. I don't really see any issue with the box that you currently have - but that's probably because I am unclear of the desired effectAdjit
@Adjit Hopefully that should clear things up for you.Elliot Bonneville
I'm glad you found the solution finally, and it was so close (and yet so far :P) from the tests yesterdayAlvaro Montoro

1 Answers

11
votes

It is to do with the default transform-origin on the scaled elements. It defaults to 50% 50% for any element being transformed, but this has issues when scaling down 1px values as it has to centre the scale on a half pixel and the rendering of the elements has issues from here on out. You can see it working here with the transform-origin moved to the relevant extremes for each item.

A bit of playing about shows that this same blurring happens on scaled elements for any dimension where the scaling ends up halving a pixel.

body {
    padding: 1em;
}

.container {
    width: 200px;
    height: 200px;
    margin: 100px;
    background-color: #EEE;
    position: absolute;
    transform: scale(2);
}

.outline {
    position: absolute;
    background: #1899ef;
    z-index: 999999;
    opacity: 1 !important;
}

.outlineBottom, .outlineTop {
    width: 100%;
    height: 1px;
    transform: scale(1, 0.5);
}

.outlineBottom {
    bottom: 0;
    transform-origin: 0 100%;
}

.outlineTop {
    transform-origin: 0 0;
}

.outlineLeft, .outlineRight {
    height: 100%;
    width: 1px;
    transform: scale(.5,1);
}

.outlineRight {
    right: 0px;
    transform-origin: 100% 0;
}

.outlineLeft {
    left: 0px;
    transform-origin: 0 0;
}
<div class="container">
    <div class="outline outlineTop"></div>
    <div class="outline outlineRight"></div>
    <div class="outline outlineBottom"></div>
    <div class="outline outlineLeft"></div>
</div>