61

This issue appears to affect all WebKit-based browsers, including the iPhone.

First some background. The site I'm working on uses a JavaScript-based 'slider' animation.

I'm using -webkit-transform: translate3d to 'power' the actual animation. When using this method, as opposed to a JavaScript-based method, the text becomes all blurry once the content has been animated. This is especially noticeable on the iPhone.

A few workarounds I saw were to remove an relative positioning, which I did, and to add a rule for -webkit-font-smoothing: antialiased, which I also did. Neither change made the slightest difference.

The only way I could make this work properly without blurry text was to use regular JavaScript for the animation and bypass the translate3d altogether. I'd prefer to use translate3d because it performs much faster on WebKit-enabled devices, but for the life of me I cannot figure out why it is affecting the text in such a poor way.

Any suggestions or solutions would be greatly appreciated.

Adam Lear
  • 38,111
  • 12
  • 81
  • 101
Mike
  • 645
  • 1
  • 6
  • 8
  • 1
    Same problem here, browser makes blurry text in whole page when translate3d is applied. Did anyone found good solution? Demo: http://jsfiddle.net/DmitrySemenov/PtDVF/ – Dmitry Semenov Sep 02 '11 at 21:41
  • I modified the jsfiddle to replicate the zoom issue causing blurry text. http://jsfiddle.net/PtDVF/14/ The blurriness occurs in both PC, MAC, and Safari for iPhone. I have not tested android. – Brandon Hutchinson Mar 14 '12 at 00:06
  • Check out http://dropshado.ws/post/6142339613/resolving-anti-aliasing-on-webkit-hardware-accelerated – robertklep Aug 14 '11 at 10:42
  • I am still having this issue in 2023. theyrule.net scales text using transform translate3D and it is blurry in Safari. I don't want to do the hacks of adjusting scales to stay sharp, because the scaling is dynamic... – Korimako Jan 22 '23 at 03:21

19 Answers19

24

None of these seem to have worked for me but I've found a slightly dirty solution which seemed to do the trick:

top: 49.9%;
left: 49.9%;
-webkit-transform: translate(-50.1%, -50.1%);
transform: translate(-50.1%, -50.1%);
peter waldock
  • 289
  • 3
  • 7
  • 4
    Fugly solution for a fugly browser, none of the above worked, but this one did. For me Chrome/Webkit is the new IE, I need one stupid workaround after another. – RiZKiT May 09 '16 at 09:07
  • 2
    Using this trick for Y axis only did work for me while non other solutions worked: `translate(-50%, -50%)` -> `translate(-50%, -50.1%)`. Interestingly, increasing or decreasing `50.1` just a little does not work either... – JBE Jun 10 '16 at 18:24
  • 1
    This worked for me but everyone needs to experiment a bit, in my case I had to use 50.2 – jigzat Aug 23 '16 at 01:13
  • 6
    Look, this will always depends on the total width of your screen! There is no easy fix. If you resize the screen you will see that the effect changes... – Miguel Oct 17 '16 at 19:32
  • This is not an universal solution. Works when 50% doesn't, but in some cases it is bad, when 50% seems OK. – marcias Nov 14 '16 at 14:58
21

I had the exact same problem described in Ken Avila's post: CSS: transform: translate(-50%, -50%) makes texts blurry

The problem was of course that I used transform: translate(-50%, -50%) which made my centered content become blurry, but only in safari on osx.

It is not only the text that becomes blurry, but the entire content, including images. I read on: http://keithclark.co.uk/articles/gpu-text-rendering-in-webkit/ that the "blurryness" is due to that the element is rendered on a non-integer boundary.

I also discovered that I could avoid using transform translate on the horizontal part of my centering from this post: https://coderwall.com/p/quutdq/how-to-really-center-an-html-element-via-css-position-absolute-fixed -The only minus was that I had to introduce a wrapper.

I discovered that using transform: translateY(-50%) didn't create any "bluryness", maybe because my element has a set height and thus does not end up rendering on a non-integer boundary.

My solution therefore ended up like this:

.wrapper {
  position: fixed;
  left: 50%;
  top: 50%;
}
.centered {
  position: relative;
  left: -50%;
  -webkit-transform: translateY(-50%);
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
}
<div class="wrapper">
  <div class="centered">
    Content
  </div>
</div>
Community
  • 1
  • 1
Njaal Gjerde
  • 653
  • 1
  • 6
  • 14
  • 2
    That first article explains the issue perfectly. In regards to only setting y because of the set height i managed to get around it with. function centerTarget(tar){ var y = Math.ceil(tar.height()/2), x = Math.ceil(tar.width()/2); tar.css('transform','translateX(' + x + 'px) translateY(' + y + 'px)'); } – Spaceman Sep 21 '15 at 06:09
  • 2
    Your sir are a legend! I just spent the last few hours trying god knows how many solutions and none worked until this! Thank you ever so much!!! – Brett Feb 07 '17 at 20:07
  • Kudos for thinking to add a wrapper. I can't think of any downside and seems to work. – zacharydl May 05 '17 at 22:16
21

As @Robert mentioned above, sometimes adding background helps, but not always.

So, for the example Dmitry added that's not the only thing you must do: except from the background, you must tell browser to explicitly use the proper anti-aliasing, so, there is a fixed Dmitry's example: http://jsfiddle.net/PtDVF/1/

You need to add these styles around (or for the) blocks where you need to fix the anti-aliasing:

background: #FFF; /* Or the actual color of your background/applied image */
-webkit-font-smoothing: subpixel-antialiased;
kizu
  • 42,604
  • 4
  • 68
  • 95
  • That works but what if I needed a transparent background? `background: transparent;` will lead to blurry typography again :-( – Timo Ernst Mar 29 '12 at 14:23
  • 1
    This has just saved my a$$! `background: rgba(0,0,0,0);`. Resolved a really nasty unwanted blur on iOS – ne1410s Nov 11 '16 at 09:25
  • Safari 11/High Sierra end of 2017 still affected, adding solid `background-color` (not `transparent` or rgba(0, 0, 0, 0)`) was the only solution helpful. – Volker E. Dec 07 '17 at 03:56
  • I also had same issue when i used translateZ, For me it worked when i used translateX and translateY together and not using translateZ, like this transform: translateX(-50%) translateY(-50%); works with transparent background as well – Pushpender Oct 16 '18 at 08:03
18

Elements you are translating must be divisible by two with even numbers.

It's important that whatever element you're trying to shift over by half is divisible by two for both it's width and height. Very similar to the responsive images, when things can be moved by 50% without splitting pixels.

A div with a width of: 503px and a height of 500px will cause blurring, no matter which way you move it or by how much when using translateX or Y. Using transform, it utilizes GPU graphics accelerator which should result is very crisp, smooth edges. It might also be a good idea to set box-sizing: border-box; to ensure calculated widths include padding and borders.

Be careful when using percentage widths. If it's relative to screen size, ever other screen pixel width will cause this blur.

factorypolaris
  • 2,757
  • 12
  • 15
  • 2
    I find that 3d translated images are still blurry even if their dimensions are divisible by 2. Really blurry. – Quentin Engles Aug 22 '17 at 18:55
  • @QuentinEngles - I have found with images in some cases whether they are translated or not. Two things, 1. If you are scaling the image down, ensure the width and height are both equally dividable. IE: 100px by 112px Image will look blurry if scaled to a width of 99px. as the height will not be a whole number. 2: High resolution monitors using windows scaling means you have to account for that translations as well. Keep image scaling to 100% or 50% to be safe. – factorypolaris Mar 04 '18 at 03:50
  • I noticed as long as box height is even number (e.g. height:400px) the blurriness for text goes away. – pref May 30 '20 at 00:39
16

I fixed this problem by adding a translate3d style to the element before any animation occurs.

-webkit-transform: translate3d(0,0,0); 
ronalchn
  • 12,225
  • 10
  • 51
  • 61
bartburkhardt
  • 7,498
  • 1
  • 18
  • 14
  • 1
    This did fix the subpixel rendering i had, but also introduced blurry text as a result of translate3D. It pushes it to the GPU which does render subpixels, but makes it blurry. It's better to fix the problem alltogether with a solution like @neil-taylor posted – Kars Barendrecht Nov 06 '19 at 10:49
6

This is the best solution translateX(calc(-50% + 0.5px))

Neil Taylor
  • 203
  • 3
  • 3
2

Probably the easiest solution:

transform: translate(-50%, -50%) scale(2); 
zoom:.5;

Scale-and-zoom takes care of rounding the pixel values to full numbers

1

None of these answers worked for me but using

display: inline-table;

did the trick

user3251328
  • 188
  • 1
  • 8
1

Neil Taylor's solution was the best, but it can still be improved:

transform: translateX(-50%) translateX(.5px)

This way it has 2 advantages:

  • It works in crappy browsers like IE11 (which doesn't support calc inside translate)
  • It only calculates at parse time, not every time the browser evaluates.
Kars Barendrecht
  • 549
  • 10
  • 23
0

I hope this is about centering the object into exact center of the screen. Blurring happens with the transform: translate(-50%,-50%);

so instead doing

position: absolute;
margin:0;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);

I tried injecting style in to element using javascript. (React.js)

const node = ReactDOM.findDOMNode(this);
var width = node.offsetWidth;
var height = node.offsetHeight;
var windowWidth = window.innerWidth;
var windowHeight = window.innerHeight;

style={
    left : (windowWidth/2) - (this.state.width/2) + 'px',
    right:  (windowWidth/2) - (this.state.width/2) + 'px',
    top: (windowHeight/2) - (this.state.height/2) + 'px',
    bottom: (windowHeight/2) - (this.state.height/2) + 'px'
}

inject style into element by javascript style

Eg.

export default class Dialog extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            width: '0px',
            height: '0px'
        };
    }

    setWidthhandHeight(node){
        if (this.state.width !== node.offsetWidth) {
            this.setState({
                width: node.offsetWidth,
                height: node.offsetHeight
            });
        }
    }

    componentDidMount() {
        this.setWidthhandHeight(node);
    }

    render() {
        const windowWidth = window.innerWidth;
        const windowHeight = window.innerHeight;
        return (
            <dialog className={this.props.className + " dialog"}
                    style={{
                        left : (windowWidth/2) - (this.state.width/2) + 'px',
                        right:  (windowWidth/2) - (this.state.width/2) + 'px',
                        top: (windowHeight/2) - (this.state.height/2) + 'px',
                        bottom: (windowHeight/2) - (this.state.height/2) + 'px'
                    }}>
                {this.props.title ? (
                    <header><span>{this.props.title}</span></header>
                    )
                    : null
                }
                {this.props.children}
            </dialog>
        );
    }
}
0

Reduce the vertical margin(margin-top or margin-bottom) or height of any element in the container on which transform translate property is being applied by just 1 pixel.

0

Resetting Chrome's zoom to 100% worked for me.

rich
  • 18,987
  • 11
  • 75
  • 101
  • lol, this would only solve it if you were zoomed in or out & that caused elements to be off pixel positioned. Which I don't think is the case here – bot19 Feb 05 '20 at 03:35
0

If the percentage is used for animation it still acceptable if we also consider it's performance, but when you're not using translate for animation it would different because the text will always blurry.

When using percentage it's possible of having fractional number like 0.5px and that the reason why the text was blurry. On my experiment using translate(0.5px, 0.5px) would make the text very blurry, so you should avoid fractional number between 0.3px <=> 0.7px while 0.2px and 0.8px still acceptable.

My suggestion is using JavaScript and turn the percentage into rounded pixel for animating the element, and using pixel instead of percentage if translate is being used for positioning the element.

The other alternative is using left and top to set the initial position with percentage then use transform: translate() with pixel for the target position and animate the transform only with transition-property: transform;

StefansArya
  • 2,802
  • 3
  • 24
  • 25
-1

For an alternative solution try:

-webkit-text-stroke: 0.35px
ANaimi
  • 6,266
  • 7
  • 32
  • 32
-1

Using zoom solved it for me.

I just added zoom: 1.04;

Black
  • 18,150
  • 39
  • 158
  • 271
-1

Solution found in my case was setting a height value of the first child element

DependencyHell
  • 1,027
  • 15
  • 22
-2

I use will-change on these types of issues, it usually solves scaling issues or rounding errors, such as seeing a 1px white line separating elements for example.

will-change: transform;

Read more here: https://developer.mozilla.org/en-US/docs/Web/CSS/will-change

-2

@kizu These answers and suggestions do not help. For your jsfiddle you need to add

-webkit-perspective-origin: 50% 50%; -webkit-perspective: 1400px;

to the parent element of the element you want to use translate3d on, otherwise translating the z-axis doesn't actually do anything. In this case you are applying it to the button you click, I think you meant to apply it to the content.

But anyway, adding those to activate z-axis change causes the blur to the button in your example.

I would love to find a solution to this as well, I fear there isn't one.

-8

Using the HTML5 doctype solved the issue for me.

<!doctype html>
<html>
<head>
<meta charset="UTF-8">