204

I've been using CSS3 transform to rotate images and textboxes with borders in my website.

The problem is that the border look jagged in Chrome, like a (low-resolution) game without Anti-Aliasing. In IE, Opera and FF it looks much better because AA is used (which is still clearly visible but not that bad). I can't test Safari because I don't own a Mac.

The rotated photo and text itself look fine, it is only the border that looks jagged.

The CSS I use is this:

.rotate2deg {
    transform: rotate(2deg);
    -ms-transform: rotate(2deg); /* IE 9 */
    -webkit-transform: rotate(2deg); /* Safari and Chrome */
    -o-transform: rotate(2deg); /* Opera */
    -moz-transform: rotate(2deg); /* Firefox */
}

Is there any way I can fix this, e.g. by forcing Chrome to use AA?

Example below:

Jagged Edges example

dtech
  • 13,741
  • 11
  • 48
  • 73
  • For those reading it later: it should be fixed in Chrome as of version 15 (Nov 2011), but Safari introduced the exact same issue in 5.1 for Mac which is as of now not yet fixed – dtech Feb 08 '12 at 12:14
  • And they fixed it so well, that going back is impossible. We have cases where antialiasing is the last thing we want, but now Chrome/Chromium/Safari has no method to turn off antialiasing in transformed images although they are 1-bit images (eg. b/w gif). Blur is so cool, so cool, more blur is more cool, they say! Only way to ensure crisp edges is to convert all to svg paths or objects and add attribute shape-rendering="crispEdges". – Timo Kähkönen Oct 07 '12 at 20:09
  • For me the issue is with transparent borders used to create an arrow. This is in Chrome 40 on win and mac. None of the options here fix the issue. – Gurnard Mar 12 '15 at 09:34

13 Answers13

408

In case anyone's searching for this later on, a nice trick to get rid of those jagged edges on CSS transformations in Chrome is to add the CSS property -webkit-backface-visibility with a value of hidden. In my own tests, this has completely smoothed them out.

-webkit-backface-visibility: hidden;
starball
  • 20,030
  • 7
  • 43
  • 238
Neven
  • 4,112
  • 1
  • 15
  • 4
  • 7
    Lifesaver - this trick has allowed us to re-enable -webkit-transform on a number of sites that previously we were forced to turn transforms off because of anti-aliasing issues. Thanks! – Darren Nov 11 '11 at 11:47
  • any help on this: http://stackoverflow.com/questions/9235342/3d-css-transform-jagged-edges-in-firefox ? – abernier Feb 11 '12 at 12:06
  • Does anyone know how this compares to -webkit-transform-style: preserve-3d ? They seem to have identical results... – jstafford Mar 14 '12 at 06:24
  • 5
    This works in Chrome, but it makes them jagged again in iOS 6! – lazd May 06 '13 at 23:00
  • @jstafford: I don't get the smoothing with `-webkit-transform-style: preserve-3d` in Chrome... – lazd May 06 '13 at 23:02
  • 11
    @lazd to fix it in iOS add `padding: 1px; -webkit-background-clip: content-box;` – Rob Fletcher Jun 16 '13 at 13:51
  • Also seems to fix a problem I had with Gifs not rendering properly in chrome - quite bizarre! Thank you, I never would have found this. – ac_ Jun 24 '13 at 14:59
  • This removes jagged edge for me in iOS7 – Fisu Jan 28 '14 at 10:43
  • Removes the jagged edges for me on Android 4.3.3 Chrome. Thanks! – Ben Poulson Jul 09 '14 at 20:45
  • If you've an img as a child of the rotated image... jagged edges come back. https://code.google.com/p/chromium/issues/detail?id=429265 – jedierikb Oct 31 '14 at 19:01
  • This is also fixing jagged edges on a png with transparent background to which _no_ transform was applied. – JakeParis Dec 12 '14 at 19:05
  • Also fixes misplaced borders when centering something vertically with `top: 50%; transform: translateY(-50%);`. Perfect. – klh Jan 05 '15 at 10:29
  • Good to know. But the picture is a bit blured now :\ Better than the jagged of course :) – GBMan Nov 04 '15 at 14:59
  • Thank you very much. I googled, found this question, saw your answer, tried it, and it worked. It's beyond me why this works (yet), but it does. Wonder where this is documented. Thanks! – Bill Doughty Mar 09 '16 at 03:01
  • It fixes the issue with jagged edges, but it causes text and images to blur. – tehlivi Feb 06 '17 at 20:35
  • 2
    @RobFletcher added padding and background-clip which seem, per this thread, essential to a cross-browser and cross-os solution. This fixes my OSX/Chrome issue too so... I think a complete solution would be something like: `padding: 1px;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-background-clip:content-box;background-clip:content-box;` – Benjamin Luoma Jun 26 '17 at 19:01
  • this didn't work for me. But Lazd method of adding padding worked. But funny enough, when I use @BenjaminLuoma technique and removed the padding, it worked. Weird – Jay Smoke Apr 10 '19 at 09:56
129

If you are using transition instead of transform, -webkit-backface-visibility: hidden; does not work. A jagged edge appears during animation for a transparent png file.

To solve it I used: outline: 1px solid transparent;

mhhorizon
  • 1,319
  • 1
  • 8
  • 5
27

Adding a 1px transparent border will trigger anti-aliasing

outline: 1px solid transparent;

Alternatively, add a 1px transparent box-shadow.

box-shadow: 0 0 1px rgba(255,255,255,0);
Callam
  • 949
  • 1
  • 14
  • 22
  • rgba(255, 255, 255, 0) is probably better – mjs Aug 14 '16 at 14:32
  • 4
    Adding the top section of CSS in your answer and the `outline: 1px solid transparent;` worked well for me. The other solutions above did not work well enough. – Timothy Zorn Nov 27 '16 at 00:09
  • `outline: 1px solid transparent;` trigger anti-aliasing also in Firefox 52 (that has the same troubles of Chrome) – Luca Detomi May 24 '17 at 08:32
19

Try 3d transform. This works like a charm!

/* Due to a bug in the anti-liasing*/
-webkit-transform-style: preserve-3d; 
-webkit-transform: rotateZ(2deg);
Zypherone
  • 191
  • 3
12

I've tried all the solutions here and didn't work in my case. But using

will-change: transform;

fixes the jagged issue.

doğukan
  • 23,073
  • 13
  • 57
  • 69
8

Chosen answer (nor any of the other answers) didn't work for me, but this did:

img {outline:1px solid transparent;}

chris
  • 577
  • 1
  • 9
  • 23
2

I've been having an issue with a CSS3 gradient with -45deg. The background slanted, was badly jagged similar to but worse than the original post. So I started playing with both the background-size. This would stretch out the jaggedness, but it was still there. Then in addition I read that other people are having issues too at 45deg increments so I adjusted from -45deg to -45.0001deg and my problem was solved.

In my CSS below, background-size was initially 30px and the deg for the background gradient was exactly -45deg, and all keyframes were 30px 0.

    @-webkit-keyframes progressStripeLTR {
        to {
            background-position: 60px 0;
        };
    }

    @-moz-keyframes progressStripeLTR {
        to {
            background-position: 60px 0;
        };
    }

    @-ms-keyframes progressStripeLTR {
        to {
            background-position: 60px 0;
        };
    }

    @-o-keyframes progressStripeLTR {
        to {
            background-position: 60px 0;
        };
    }

    @keyframes progressStripeLTR {
        to {
            background-position: 60px 0;
        };
    }

    @-webkit-keyframes progressStripeRTL {
        to {
            background-position: -60px 0;
        };
    }

    @-moz-keyframes progressStripeRTL {
        to {
            background-position: -60px 0;
        };
    }

    @-ms-keyframes progressStripeRTL {
        to {
            background-position: -60px 0;
        };
    }

    @-o-keyframes progressStripeRTL {
        to {
            background-position: -60px 0;
        };
    }

    @keyframes progressStripeRTL {
        to {
            background-position: -60px 0;
        };
    }

    .pro-bar-candy {
        width: 100%;
        height: 15px;

        -webkit-border-radius:  3px;
        -moz-border-radius:     3px;
        border-radius:          3px;

        background: rgb(187, 187, 187);
        background: -moz-linear-gradient(
                        -45.0001deg,
                        rgba(187, 187, 187, 1.00) 25%,
                        transparent 25%,
                        transparent 50%,
                        rgba(187, 187, 187, 1.00) 50%,
                        rgba(187, 187, 187, 1.00) 75%,
                        transparent 75%,
                        transparent
                    );
        background: -webkit-linear-gradient(
                        -45.0001deg,
                        rgba(187, 187, 187, 1.00) 25%,
                        transparent 25%,
                        transparent 50%,
                        rgba(187, 187, 187, 1.00) 50%,
                        rgba(187, 187, 187, 1.00) 75%,
                        transparent 75%,
                        transparent
                    );
        background: -o-linear-gradient(
                        -45.0001deg,
                        rgba(187, 187, 187, 1.00) 25%,
                        transparent 25%,
                        transparent 50%,
                        rgba(187, 187, 187, 1.00) 50%,
                        rgba(187, 187, 187, 1.00) 75%,
                        transparent 75%,
                        transparent
                    );
        background: -ms-linear-gradient(
                        -45.0001deg,
                        rgba(187, 187, 187, 1.00) 25%,
                        transparent 25%,
                        transparent 50%,
                        rgba(187, 187, 187, 1.00) 50%,
                        rgba(187, 187, 187, 1.00) 75%,
                        transparent 75%,
                        transparent
                    );
        background: linear-gradient(
                        -45.0001deg,
                        rgba(187, 187, 187, 1.00) 25%,
                        transparent 25%,
                        transparent 50%,
                        rgba(187, 187, 187, 1.00) 50%,
                        rgba(187, 187, 187, 1.00) 75%,
                        transparent 75%,
                        transparent
                    );
        background: -webkit-gradient(
                        linear,
                        right bottom,
                        right top,
                        color-stop(
                            25%,
                            rgba(187, 187, 187, 1.00)
                        ),
                        color-stop(
                            25%,
                            rgba(0, 0, 0, 0.00)
                        ),
                        color-stop(
                            50%,
                            rgba(0, 0, 0, 0.00)
                        ),
                        color-stop(
                            50%,
                            rgba(187, 187, 187, 1.00)
                        ),
                        color-stop(
                            75%,
                            rgba(187, 187, 187, 1.00)
                        ),
                        color-stop(
                            75%,
                            rgba(0, 0, 0, 0.00)
                        ),
                        color-stop(
                            rgba(0, 0, 0, 0.00)
                        )
                    );

        background-repeat: repeat-x;
        -webkit-background-size:    60px 60px;
        -moz-background-size:       60px 60px;
        -o-background-size:         60px 60px;
        background-size:            60px 60px;
        }

    .pro-bar-candy.candy-ltr {
        -webkit-animation:  progressStripeLTR .6s linear infinite;
        -moz-animation:     progressStripeLTR .6s linear infinite;
        -ms-animation:      progressStripeLTR .6s linear infinite;
        -o-animation:       progressStripeLTR .6s linear infinite;
        animation:          progressStripeLTR .6s linear infinite;
        }

    .pro-bar-candy.candy-rtl {
        -webkit-animation:  progressStripeRTL .6s linear infinite;
        -moz-animation:     progressStripeRTL .6s linear infinite;
        -ms-animation:      progressStripeRTL .6s linear infinite;
        -o-animation:       progressStripeRTL .6s linear infinite;
        animation:          progressStripeRTL .6s linear infinite;
        }
John
  • 1
  • 13
  • 98
  • 177
Pegues
  • 1,693
  • 2
  • 21
  • 38
2

Adding the following on the div surrounding the element in question fixed this for me.

-webkit-transform-style: preserve-3d;

The jagged edges were appearing around the video window in my case.

chaser7016
  • 595
  • 2
  • 10
  • 28
1

Just thought that we'd throw in our solution too as we had the exact same problem on Chrome/Windows.

We tried the solution by @stevenWatkins above, but still had the "stepping".

Instead of

-webkit-backface-visibility: hidden;

We used:

-webkit-backface-visibility: initial;

For us this did the trick

1

You might be able to mask the jagging using blurred box-shadows. Using -webkit-box-shadow instead of box-shadow will make sure it doesn't affect non-webkit browsers. You might want to check Safari and the mobile webkit browsers though.

The result is somewhat better, but still a lot less good then with the other browsers:

with box shadow (underside)

dtech
  • 13,741
  • 11
  • 48
  • 73
0

For me it was the perspective CSS property that did the trick:

-webkit-perspective: 1000;

Completely illogical in my case as I use no 3d transitions, but works nonetheless.

Aron
  • 3,419
  • 3
  • 30
  • 42
0

For canvas in Chrome (Version 52)

All listed answers is about images. But my issue is about canvas in chrome (v.52) with transform rotate. They became jagged and all this methods can't help.

Solution that works for me:

  1. Make canvas larger on 1 px for each side => +2 px for width and height;
  2. Draw image with offset + 1px (in position 1,1 instead of 0,0) and fixed size (size of image should be 2px less than size of canvas)
  3. Apply required rotation

So important code blocks:

// Unfixed version
ctx.drawImage(img, 0, 0, 335, 218);
// Fixed version
ctx.drawImage(img, 1, 1, 335, 218);
/* This style should be applied for fixed version */
canvas {
  margin-left: -1px;
  margin-top:-1px;
}        
<!--Unfixed version-->
<canvas width="335" height="218"></canvas>
<!--Fixed version-->
<canvas width="337" height="220"></canvas>

Sample: https://jsfiddle.net/tLbxgusx/1/

Note: there is a lot of nested divs because it is simplified version from my project.


This issue is reproduced also for Firefox for me. There is no such issue on Safari and FF with retina.

And other founded solution is to place canvas into div of same size and apply following css to this div:

overflow: hidden;
box-shadow: 0 0 1px rgba(255,255,255,0);
// Or
//outline:1px solid transparent;

And rotation should be applied to this wrapping div. So listed solution is worked but with small modification.

And modified example for such solution is: https://jsfiddle.net/tLbxgusx/2/

Note: See style of div with class 'third'.

Community
  • 1
  • 1
Kiryl
  • 180
  • 13
0

For safari 16.2 any of mention solution doesn't work. But I tried rotation and it fix the issue.

 transform: "rotatex(360deg)"

In my case I have problem with video element.

Vít Zadina
  • 168
  • 8