7

If I scale an element using CSS scale() it becomes pixelated while transitioning. But it becomes normal again when transition is finished (refer to the screenshot 1). However it happens only in webkit browsers (tested in Chrome and Opera)

.foo {
  background: #4FC093;
  display: block;
  position: absolute;
  width: 20px;
  height: 20px;
  box-shadow: 0 0 10px inset;
  margin: 0 auto;
  border-radius: 50%;
  left: 50%;
  top: 50%;
  cursor: pointer;
  -webkit-transition: all 3s ease;
  -moz-transition: all 3s ease;
  -ms-transition: all 3s ease;
  transition: all 3s ease;
}

.foo:hover {
  -webkit-transform: scale(20);
  -moz-transform: scale(20);
  -ms-transform: scale(20);
  transform: scale(20);
}
<div class="foo"></div>

Screenshot 1 pixelated div with transition

A possible workaround

I have also tried using scale3d() with reversing the scale of this div, as suggested here
But it caused a jagged edge around the div in Google Chrome.

.foo {
  background: #4FC093;
  display: block;
  position: absolute;
  width: 400px;
  height: 400px;
  box-shadow: 0 0 200px inset;
  margin: 0 auto;
  border-radius: 50%;
  cursor: pointer;
  -webkit-transition: all 3s ease;
  -moz-transition: all 3s ease;
  -ms-transition: all 3s ease;
  transition: all 3s ease;
  -webkit-transform: scale3d(0.05, 0.05, 0.05);
  -moz-transform: scale3d(0.05, 0.05, 0.05);
  -ms-transform: scale3d(0.05, 0.05, 0.05);
  transform: scale3d(0.05, 0.05, 0.05);
}

.foo:hover {
  -webkit-transform: scale3d(1, 1, 1);
  -moz-transform: scale3d(1, 1, 1);
  -ms-transform: scale3d(1, 1, 1);
  transform: scale3d(1, 1, 1);
}
<div class="foo"></div>

I don't want the edges to be jagged. I have tried using -webkit-backface-visibility: hidden here but there's no luck. I know there is a property called -webkit-font-smoothing where we can set the font-smoothing as antialiased. Is there any way that we can set antialiased for a div?

Screenshot 2

using scale3d with reverse formula

Lastly, this is not a solution of my problem and I would like to avoid using this workaround as I'll have to go through and calculate the parameter values of scale3d() manually.

I am expecting the solution of first case here.

Community
  • 1
  • 1
aniskhan001
  • 7,981
  • 8
  • 36
  • 57
  • On Chrome 39 on OSX on a Retina Macbook Pro it's not pixelly, but the shadow is a series of rectangles. You can perhaps size the circle up a bit, say width:100px, height:100px, then start with an initial scale of 0.2, then scale it up to 5 (or whatever the values are), this seems to look better for me. This would then be only scaling it up a bit, which seems to resolve the errors. – Rich Bradshaw Aug 31 '14 at 08:56
  • Thanks for the info. I am using Chrome 37 64-bit on Windows. This is the updated fiddle of what you suggested: http://jsfiddle.net/aniskhan001/bk96d9vo/1/ .. I still see it's pixelated but a bit less from before. And this is just an example. I will have to grow up divs even bigger from tiny sizes in my project. – aniskhan001 Aug 31 '14 at 09:08
  • http://imgur.com/hb8RAHY is how this looks for me - not pixelated at all, but the inset shadow is really not good. Instead of an inset shadow, perhaps you could try a radial gradient? – Rich Bradshaw Aug 31 '14 at 09:10
  • Just tried it with Chrome 39 and for me, it's look better than what you have posted right now. http://imgur.com/f6vy3j6 I think it's depended on the GPU rendering. I'm using NVIDIA GTX 560. But, what about the other browsers like Chrome 37, Opera 23? – aniskhan001 Aug 31 '14 at 09:23
  • I think it's a bug with having an @2x screen rather than a GPU issue - might be wrong though. Have you tried a radial gradient? You might find that coincidentally that fixes this. I'd file two issues for chrome here - one for the pixelly version you had, and one for the issue with scaling an inset box shadow. – Rich Bradshaw Aug 31 '14 at 09:31
  • **optimizeQuality** on/off might have something to do with it. – Joop Eggen Sep 15 '14 at 13:04

3 Answers3

2

Yes, there is a fix, make the element big and scale to smaller at the initial state. Then on hover scale to 1, and now it's very smooth and not pixelated.

.foo {
    background: #4FC093;
    display: block;
    position: absolute;
    width: 200px;
    height: 200px;
    box-shadow: 0 0 10px inset;
    margin: 0 auto;
    border-radius: 50%;
    left: 50%;
    top: 50%;
    cursor: pointer;
    -webkit-transition: all 3s ease;
    -moz-transition: all 3s ease;
    -ms-transition: all 3s ease;
    transition: all 3s ease;

    -webkit-transform: scale(.20);
    -moz-transform: scale(.20);
    -ms-transform: scale(.20);
    transform: scale(.20);
}

.foo:hover {
    -webkit-transform: scale(1);
    -moz-transform: scale(1);
    -ms-transform: scale(1);
    transform: scale(1);
}
Mazhar Ahmed
  • 1,523
  • 2
  • 24
  • 41
  • Thanks for the answer. I have mentioned in my question that I would like to avoid using this method. Because I will have to change the values of scale() by calculating manually. But that's fine if the problem is fixed. Here is my updated fiddle which is closely similar to what I want to do finally: http://jsfiddle.net/bk96d9vo/2/ The only problem now is if I increase the size of those divs I face some positioning issue. They will not align as I expected. – aniskhan001 Sep 01 '14 at 02:44
  • Why don't you calculate the scale and always make it less than 1? It's not that hard at all. – Mazhar Ahmed Sep 01 '14 at 13:31
  • @aniskhan001 Absolutely position the elements. Then it doesn't matter how big they actually are – Zach Saucier Sep 01 '14 at 14:02
  • Okay, so this is the update: http://jsfiddle.net/bk96d9vo/3/ You can see the positioning issue. @ZachSaucier, setting those elements position to absolute make them gather in one place altogether. – aniskhan001 Sep 01 '14 at 14:26
  • @aniskhan001 That's why you'd position them with `right` and `left` like you always do with absolutely positioned elements – Zach Saucier Sep 01 '14 at 15:04
0

It will be a little more work, but you may want to consider not using scale.

If you're planning on resizing css shapes, you may be able to get away with just transitioning the attributes you used to draw the shapes and using a wrapper div for positioning.

Working Example

html:

<div class="row"> <!-- use a parent div to create rows -->
    <div class="wrap"> <!-- use a wrapper to position -->
        <div class="foo"></div>
    </div>
    <div class="wrap">
        <div class="foo"></div>
    </div>
    <div class="wrap">
        <div class="foo"></div>
    </div>
    <div class="wrap">
        <div class="foo"></div>
    </div>
    <div class="wrap">
        <div class="foo"></div>
    </div>
    <div class="wrap">
        <div class="foo"></div>
    </div>
</div>
<div class="row">
    <div class="wrap">
        <div class="foo"></div>
    </div>
    <div class="wrap">
        <div class="foo"></div>
    </div>
    <div class="wrap">
        <div class="foo"></div>
    </div>
</div>

css:

.wrap {
    position: relative;
    height:20px;
    width:20px;
    display: inline-block;
}
.foo {
    position: absolute;
    top: 190px;
    left: 190px;
    background: #4FC093;
    display: block;
    width: 20px;
    height: 20px;
    box-shadow: 0 0 10px inset;
    border-radius: 50%;
    cursor: pointer;
    transition:all 3s ease;
    z-index: 99;/*using an unnecessarily large z-index makes for a nicer transition in Chrome*/
}
.foo:hover {
    top: 0px;
    left: 0px;
    width: 400px;
    height: 400px;
    box-shadow: 0 0 200px inset;
    transition:all 3s ease;
    z-index: 1;
}
apaul
  • 16,092
  • 8
  • 47
  • 82
  • Thanks for your answer. But I want to align multiple `.foo`s like this demo: http://jsfiddle.net/bk96d9vo/2/ Also transition on `width` and `height` giving me a laggy effect, not sure why. – aniskhan001 Sep 12 '14 at 06:29
  • @aniskhan001 See update. If that isn't what you're after try this one: http://jsfiddle.net/aa6zjg5p/6/ – apaul Sep 15 '14 at 13:49
  • Thanks for taking time on this. The reason I prefer to use scale on an `absolute` div is not to change the actual width and height of the body/parent element. Which is showing on your example. I'm thinking that, it's a bug of webkit browsers yet to be fixed. – aniskhan001 Sep 15 '14 at 19:35
0

I think the only solution to this is to initially scale down the element and then scaling it to normal size how Mazhar Ahmed already answered.

If you don't want to calculate the size manually, you could just use CSS variables like this:

--scale:0.1;

.element{
  width: 40px * (1 / var(--scale));
  height: 40px * (1 / var(--scale));
  transform: scale(var(--scale));
}

.element:hover{
  transform:scale(1);
}

As you can see, you only define the scale factor in a variable. CSS does the rest and sets the width and height accordingly.
So in our case the element would be 400x400px, but it's scaled down, so it appears to be 40x40px as defined in our CSS.

Just a small addition for the lazybones (like me).

Tobias Glaus
  • 3,008
  • 3
  • 18
  • 37