7

I've encountered this very annoying problem.

When you align an image with transform, translate percentage based it causes the image to blur slightly. This is only with percentage alignment

Consider this css:

img {
  display: block;
  height: auto;
  max-width: 100%;
  transform: translate(1%,1%); 
}

Tried solutions:

  1. translate3d fix
  2. perspective fix
  3. translateZ fix

Maybe somebody has an solution?

Updated: Js Fiddle I updated the js fiddle with an image to better see the difference. It is very noticeable in photography.

Example image:

enter image description here

Thanks!

rhfhgfgh
  • 81
  • 1
  • 5
  • Did you figure this one out? – user4584963 Apr 20 '17 at 16:27
  • @user4584963 unfortunately not. I know now its a retina ( or another high resolution ) screen specific problem. This causes it to be overlooked I think, not all users can see the difference because of the lower resolution. Do you see the problem too? – rhfhgfgh Apr 21 '17 at 17:32
  • Ya it's not always blurry but I can see as I change the width and/or height of the browser window the blurriness comes and goes. – user4584963 Apr 21 '17 at 22:10

4 Answers4

11

Try something like this translateX(calc(-50% + 0.5px))

Neil Taylor
  • 203
  • 3
  • 3
2

Using a percentage value with Transform: translate means it positions your element with sub-pixel accuracy. You can see the pixel-value offset of your second div in the jsfiddle when you query it in the console:

subpixel positioning

This means that your browser is forced to do some less than optimal anti-aliasing to position your image, and that's what's causing the blurriness. If you could position it to whole-pixel values instead the image would remain sharp.

I haven't found an elegant solution to this annoying problem, but with javascript (sorry, I know this is tagged as a css problem) it's conceivable you could measure the element's offset values, then remove the translate and measure the non-translated pixel offset values and then calculate the difference between the two offsets (difference in offset between pre and post translate). Then rather than putting translate(%, %) back onto your element you could round the differences you calculated to the nearest whole pixel value (ie: remove sub-pixel rendering) and then reapply those values as translate (px, px) instead. This would keep your image sharp. It's a less than optimal solution, but it's the best I've been able to come up with so far.

EDIT:

Here's a quick function I wrote that will do what I'm talking about above. Once again, I apologize that this is not a CSS solution, but I see no way to fix it with CSS. This is also not a great solution in that you lose the responsiveness of % values, and it will also overwrite any Transform attributes that aren't translateX or translateY (so maybe use a wrapper div if that's a problem). Somebody could probably solve that problem by doing this with a Transform matrix, but yeah...

[EDIT 2: updated function to account for any css transitions that may be assigned to element]

function snapTranslateXYValsToNearestPixel(element){
  var xTransPos = $(element).offset().left;
  var yTransPos = $(element).offset().top;
  // turn off any transitions (but save values first):
  var transitionVal = $(element).css('transition');
  $(element).css('transition', 'none');
  // turn off translate:
  $(element).css('transform', 'translateX(0) translateY(0)');
  var xPosDiff = xTransPos - $(element).offset().left;
  var yPosDiff = yTransPos - $(element).offset().top;
  var xPixelVal = Math.round(xPosDiff);
  var yPixelVal = Math.round(yPosDiff);
  var translateVal = 'translateX(' + xPixelVal + 'px) translateY(' + yPixelVal + 'px)';
  $(element).css('transform', translateVal);
  // reapply transition value (wait one tick for new css value to apply first):
  setTimeout(function() {
    $(element).css('transition', transitionVal);
  }, 1);
}

Again, not a totally ideal solution... BUT it WILL convert the translateX and translateY percentages of your element to whole pixel values and it will give you a nice crisp image.

Example usage:

snapTranslateXYValsToNearestPixel('.align-per');
RATKNUKKL
  • 116
  • 5
0

I just had the same problem with an overlay which moves to the middle of the window via CSS and the content determines the height and width. The overlay also has a maximum height and width. Besides, I got some CSS transitions on the overlay I didn't want to lose.

All CSS fixes described here did not work.

I got rid of the blur by using JavaScript to ensure that the overlay is always divisible by 2. I call this function when creating the overlay, as well as when resizing the window.

Theoretically, the overlay works without JS - but with JS it's sharper. And it do not need any timeouts to keep the transitions work.

fullPixelFix = function() {
  var overlay = $('#overlay');
  overlay.removeAttr('style');
  var dividable_width = Math.round(overlay.outerWidth() / 2) * 2;
  var dividable_height = Math.round(overlay.outerHeight() / 2) * 2;
  overlay.outerWidth(dividable_width).outerHeight(dividable_height);
  overlay.css(maxWidth: dividable_width, maxHeight: dividable_height);
}
Markus
  • 214
  • 3
  • 6
-1

I found this fix here : CSS: transform: translate(-50%, -50%) makes texts blurry

div.align-per {
  transform: translate(1%, 1%) translateZ(0) ;
  -webkit-transform: translateZ(0) scale(1.0, 1.0);
}

I think it's worth investigating for others browsers.

Community
  • 1
  • 1
317
  • 67
  • 5
  • @371 Thanks for your message. Unfortunately the ` -webkit-transform: translateZ(0) scale(1.0, 1.0);` resets the transform value. If you adjust it to: ` -webkit-transform: translateZ(0) translate(1%, 1%);` you will see the image stays blurred. – rhfhgfgh Mar 08 '17 at 11:23
  • I'm sorry, I can't tell if it's blurred and I can't see it or if it's not blurred anymore. Could you confirm that in this picture, the first one is not blurred and the other two are? http://i.imgur.com/E4pypZD.png – 317 Mar 09 '17 at 10:33
  • I can confirm that the middle one is more blurred. I know it is very suble, but in photography very noticeable. – rhfhgfgh Mar 09 '17 at 11:29
  • The problem is actually due to sub-pixel placement of the element. This css trick (along with other common ones like backface-visibility) unfortunately won't fix this. :/ – RATKNUKKL May 24 '17 at 22:28