CSS transition effect makes image blurry / moves image 1px, in Chrome?
Asked Answered
W

14

173

I have some CSS that on hover, a CSS transition effect will moves a div.

The problem, as you can see in the example, is that the translate transition has the horrible side effect of making the image in the div move by 1px down/right (and possibly resize ever so slightly?) so that it appears out of place and out of focus...

The glitch seems to apply the whole time the hover effect is applied, and from a process of trial and error I can safely say only seems to occur when the translate transition moves the div (box shadow and opacity are also applied but make no difference to the error when removed).

The problem only seems to happen when the page has scrollbars. So the example with just one instance of the div is fine, but once more identical divs are added and the page therefore requires a scrollbar the problem strikes again...

Wordy answered 17/3, 2013 at 17:47 Comment(7)
I'm on Chrome 27 on OSX, and it's fine. I believe that when the content gets put into a layer it gets turned into a bitmap during the animation, and that on older versions/older graphics cards this doesn't look great. Try a newer version and see if it's fixed.Hickey
Everything fine on Chrome 25 OS X. BTW: I'd suggest a different approach for the background gradient texture than a 300KB image!Chillon
And thanks @Chillon - the background image was for the demonstration only, it's not the image in use on the actual site!Wordy
The problem arise when the animation is handled by the GPU, looks like the bitmap roundings are a little bit off. Is reproduced in Canary, but it works ok if you turn off GPU accelerationStreamer
You can try this solution each frame... https://mcmap.net/q/144668/-chrome-font-appears-blurryDisquiet
I found that this issue only happen if the browser has a zoom. you can reset with CTR+0 / CMD+0Graphomotor
If your issue is translateX/translateY centering and you know the element dimensions, you can always fallback to the classic negative margin of half of the size of the centered element. and if your element is just 1px at size just don't translate it doesn't make sense if you are already 50%-ing the 1px el :) .Datestamp
C
276

2020 update

  • If you have issues with blurry images, be sure to check answers from below as well, especially the image-rendering CSS property.
  • For best practice accessibility and SEO wise you could replace the background image with an <img> tag using object-fit CSS property.

Original answer

Try this in your CSS:

.your-class-name {
    /* ... */
    -webkit-backface-visibility: hidden;
    -webkit-transform: translateZ(0) scale(1, 1);
}

What this does is it makes the division to behave "more 2D".

  • Backface is drawn as a default to allow flipping things with rotate and such. There's no need to that if you only move left, right, up, down, scale or rotate (counter-)clockwise.
  • Translate Z-axis to always have a zero value.
  • Chrome now handles backface-visibility and transform without the -webkit- prefix. I currently don't know how this affects other browsers rendering (FF, IE), so use the non-prefixed versions with caution.
Countermarch answered 18/3, 2013 at 8:22 Comment(23)
Might not have explained anything but it explained enough to fix this problem for me.Allele
@Class Stacker - what's to explain? You just copy paste the code to your problematic element. Btw this works very nice!Strum
Worked like a charm for me, even in Firefox :OSelfabsorption
I can concur that this fixes a number of rendering issues even in Firefox (unprefixing, of course)... thanks rampapapa.Depside
Sometimes problems caused by float width like 300.12pxMechanistic
@Rantiev, can you modify this fiddle so that the problem can be reproduced? What version of Chrome are you using? jsfiddle.net/tf3xa/1Countermarch
@Countermarch Hi, Do you work in Chrome team? Recently i found that Chrome latest versions contains less bugs related to float width and etc. but they are present. I can't find an example now, but will provide a link, if i bump with such an issue.Mechanistic
@Rantiev, no I don't but I'm flattered :P Just trying to find a better solution for the problem.Countermarch
@user1672694 you are right. Chrome does some image rendering and filtering. Here's a fiddle which shows the left side of the cat a little more sharper after some time the animation has stopped. Sometimes you notice a slight nudge in the image as well.Countermarch
You may want to try SVG as well. Here is an example codepen.io/Izaias/pen/WvWxxvArboretum
Doesn't work with 'transform:translateY(-50%) translateZ(0)' and 'transform:translate3d(0, -50%, 0)'. Why?Unalterable
@FredK you can only have one transform property, right? This seems to work on my fiddle tests. .my-cat { -webkit-backface-visibility: hidden; transform: translateZ(0) translateY(-50%) scale(1, 1); } .my-cat:hover { transform: translateZ(0) translateY(-50%) scale(1.3, 1.3); }Countermarch
This worked for me, but I only used the backface-visibility portion. TranslateZ threw off my positioning. Thanks!Jeniferjeniffer
this works great but makes other elements such as paragraphs blurry. any ideas on how i fix it ?Ruffina
@Double like Rantiev mentioned, it's the sub-pixels that cause this blur. And I believe that's the reason why the pixel shift happens in the first place. Now you can try animation with font-size, that's looking good but is somewhat hard to handle.Countermarch
@Mechanistic "font-size animation" like what ? any links on the topic ?Ruffina
This answer needs to include the non -webkit- property versions as well. Chrome disables the properties here and ignores them as invalid. It will now only accept the non-webkit variants.Subtropics
@Subtropics I'd be happy to but... there's a possibility that it would make other browsers act weird, right? I don't have the time to test this now but if somebody can check that out then just ping me and I'll edit the non-vendor prefixed definitions there as well.Countermarch
i suggest this solution https://mcmap.net/q/144668/-chrome-font-appears-blurry im posting the link to avoid duplicationDisquiet
Can someone confirm if this still work, because whenever I add ` -webkit-backface-visibility` and -webkit-transform, I can't really see a change, and when I open chromes developer console. I see those 2 css properties are stroked through, as if they are overwritten, but they are not (empty css and html). It's as if chrome no longer accepts them.Pepsinate
@KevinM try without the -webkit- prefixes, these are now standard CSS.Countermarch
This do not solve the issue. It actually make the whole content more blurred than before...Mountainous
Do not use translateZ(0) if not necessary. It brokes DOM hierarchy model and isolates all childs from parentHiett
H
99

You need to apply 3d transform to the element, so it will get its own composite layer. For instance:

.element{
    -webkit-transform: translateZ(0);
    transform: translateZ(0);
}

or

.element{
    -webkit-transform: translate3d(0,0,0);
    transform: translate3d(0,0,0);
}

More about layer creation criteria you can read right here: Accelerated Rendering in Chrome


An explanation:

Examples (hover green box):

When you use any transition on your element it cause browser to recalculate styles, then re-layout your content even if transition property is visual (in my examples it is an opacity) and finaly paint an element:

screenshot

The issue here is re-layout of the content that can make an effect of "dancing" or "blinking" elements on the page while transition happens. If you will go to settings, check "Show composite layers" checkbox and then apply 3d transform to an element, you will see that it gets it's own layer which outlined with orange border.

screenshot

After element gets its own layer, browser just needs to composite layers on transition without re-layout or even paint operations so problem have to be solved:

screenshot

Henigman answered 6/10, 2013 at 17:52 Comment(4)
nice stuff! got a point cause of how detailed your answer was! which software you using for screen capturing / arrowing ?Hyperspace
Spot on mate!! Saved me a lot of hassle there.Dissatisfaction
This did the trick for me. At first I was using translateZ on the parent that I was animating, but the background-image sprites within were still blurry. I am using Velocity.js to scale yet another container within it and applied something like translateZ: 0.000001 (some infinitesimal #) and voila! Sharp background-images once again!Detribalize
Thanks mate. This worked on my problem. by the way, my problem is I have an element that is rotated 90degrees and has a fade-in transition using opacity. when triggering the transition the content of the element is moving 1px from left.Orella
C
49

Had the same problem with embeded youtube iframe (Translations were used for centering iframe element). None of the solutions above worked until tried reset css filters and magic happened.

Structure:

<div class="translate">
     <iframe/>
</div>

Style [before]

.translate {
  transform: translateX(-50%);
  -webkit-transform: translateX(-50%);
}

Style [after]

.translate {
  transform: translateX(-50%);
  -webkit-transform: translateX(-50%);
  filter: blur(0);
  -webkit-filter: blur(0);
}
Cullis answered 17/2, 2015 at 15:48 Comment(4)
filter: blur(0) did it for me!Roana
Unbelievable O_o WTF with the blur ? Why's it on by default?Scevour
This was the solution for me too. The accepted answer could work for people that aren't using other "translate" functions in their transform properties, but it wasn't working for me.Hepza
Shouldn't the -webkit- prefix come before? More InfoWisecrack
D
36

I recommended an experimental new attribute CSS I tested on latest browser and it's good:

image-rendering: optimizeSpeed;             /*                     */
image-rendering: -moz-crisp-edges;          /* Firefox             */
image-rendering: -o-crisp-edges;            /* Opera               */
image-rendering: -webkit-optimize-contrast; /* Chrome (and Safari) */
image-rendering: optimize-contrast;         /* CSS3 Proposed       */
-ms-interpolation-mode: nearest-neighbor;   /* IE8+                */

With this the browser will know the algorithm for rendering

Diesel answered 5/3, 2015 at 16:13 Comment(5)
This fixed my blurry rotated images while backface-visibility, blur(0), translateZ did not work for me. Thank you.Vito
Fixed images in some use cases, made it horribly worse in some others :-) Interesting in any case!Suppression
Digged in deeper: image-rendering: -webkit-optimize-contrast; solves the issue on Chrome. However, images on other browsers, e.g. Firefox, get rendered much, much worse with the rendering option set. Therefore, I only use the WebKit directive, which also works on the Blink engine. Thanks!Suppression
In some cases it causes the images to be noticeably jaggy. Can't seem to find a sweet-spot in between the blurrier result and this one ~sigh~Unsaddle
It isn't optimizeSpeed anymore, but pixalated -> see on the mozilla websiteSenghor
C
6

Just found another reason why an element goes blurry when being transformed. I was using transform: translate3d(-5.5px, -18px, 0); to re-position an element once it had been loaded in, however that element became blurry.

I tried all the suggestions above but it turned out that it was due to me using a decimal value for one of the translate values. Whole numbers don't cause the blur, and the further away I went from the whole number the worse the blur became.

i.e. 5.5px blurs the element the most, 5.1px the least.

Just thought I'd chuck this here in case it helps anybody.

Christianity answered 14/12, 2017 at 10:11 Comment(1)
Thanks, this was the issue in my case - I was using translateY(-50%) which must have been evaluating to a decimal pixel value.Suboceanic
E
5

I cheated problem using transition by steps, not smoothly

transition-timing-function: steps(10, end);

It is not a solving, it is a cheating and can not be applied everywhere.

I can't explain it, but it works for me. None of another answers helps me (OSX, Chrome 63, Non-Retina display).

https://jsfiddle.net/tuzae6a9/6/

Edmiston answered 12/1, 2018 at 19:15 Comment(1)
In your fiddle is shaking like Parkinson's, but in my case worked.Napolitano
L
2

Scaling to double and bringing down to half with zoom worked for me.

transform: scale(2);
zoom: 0.5;
Lepidolite answered 11/5, 2016 at 10:42 Comment(1)
this does seem to work in chrome for images. unfortunately it also modifies any html you put it around too.Adenaadenauer
U
2

Try filter: blur(0);

It worked for me

Unalterable answered 5/6, 2017 at 14:15 Comment(1)
Worked for me too, Chrome 63, 64, and Vivaldi 1.13Lagena
I
2

I've tried around 10 possibly solutions. Mixed them up and they still didn't work correctly. There was always 1px shake at the end.

I find solution by reducing transition time on filter.

This didn't work:

.elem {
  filter: blur(0);
  transition: filter 1.2s ease;
}
.elem:hover {
  filter: blur(7px);
}

Solution:

.elem {
  filter: blur(0);
  transition: filter .7s ease;
}
.elem:hover {
  filter: blur(7px);
}

Try this in fiddle:

.blur {
  border: none;
  outline: none;
  width: 100px; height: 100px;
  background: #f0f;
  margin: 30px;
  -webkit-filter: blur(10px);
  transition: all .7s ease-out;
  /* transition: all .2s ease-out; */
}
.blur:hover {
  -webkit-filter: blur(0);
}

.blur2 {
  border: none;
  outline: none;
  width: 100px; height: 100px;
  background: tomato;
  margin: 30px;
  -webkit-filter: blur(10px);
  transition: all .2s ease-out;
}
.blur2:hover {
  -webkit-filter: blur(0);
}
<div class="blur"></div>

<div class="blur2"></div>

I hope this helps someone.

Isomerous answered 21/3, 2018 at 11:51 Comment(0)
G
2

For me, now in 2018. The only thing that fixed my problem (a white glitchy-flicker line running through an image on hover) was applying this to my link element holding the image element that has transform: scale(1.05)

a {
   -webkit-backface-visibility: hidden;
   backface-visibility: hidden;
   -webkit-transform: translateZ(0) scale(1.0, 1.0);
   transform: translateZ(0) scale(1.0, 1.0);
   -webkit-filter: blur(0);
   filter: blur(0);
}
a > .imageElement {
   transition: transform 3s ease-in-out;
}
Gutturalize answered 29/4, 2018 at 2:12 Comment(1)
Yes! 'blur(0)' fixes it for me in Chrome. Makes the image very slightly blurry on resize though but is less noticeable than the jump / resizeJahnke
B
2

None of this worked, what worked for me is scaling image down.

So depending on what size you want the image or what resoultion your image is, you can do something like this:

.ok {
      transform: perspective(100px) rotateY(0deg) scale(0.5);
      transition: transform 1s;
      object-fit:contain;
}
.ok:hover{
      transform: perspective(100px) rotateY(-10deg) scale(0.5);
}

/* Demo Preview Stuff */
.bad {
   max-width: 320px;
   object-fit:contain;
   transform: perspective(100px) rotateY(0deg);
   transition: transform 1s;
}
.bad:hover{
      transform: perspective(100px) rotateY(-10deg);
}

div {
     text-align: center;
     position: relative;
     display: flex;
}
h3{
    position: absolute;
    bottom: 30px;
    left: 0;
    right: 0;
}
     
.b {
    display: flex;
}
<center>
<h2>Hover on images</h2>
<div class="b">
<div>
  <img class="ok" src='https://www.howtogeek.com/wp-content/uploads/2018/10/preview-11.png'>
  <h3>Sharp</h3>
</div>

<div>
  <img class="bad" src='https://www.howtogeek.com/wp-content/uploads/2018/10/preview-11.png'>
  <h3>Blurry</h3>
</div>

</div>

</center>

The image should be scaled down, make sure you have a big image resoultion

Borneo answered 15/1, 2021 at 20:43 Comment(0)
A
1

I had a similar problem with blurry text but only the succeeding div was affected. For some reason the next div after the one that I was doing the transform in was blurry.

I tried everything that is recommended in this thread but nothing worked. For me rearranging my divs worked. I moved the div that blurres the following div to the end of parents div.

If someone know why just let me know.

#before
<header class="container">
      <div class="transformed div">
          <span class="transform wrapper">
            <span class="transformed"></span>
            <span class="transformed"></span>
          </span>
       </div>
       <div class="affected div">
       </div>
     </header>

#after
<header class="container">
   <div class="affected div">
   </div>
  <div class="transformed div">
      <span class="transform wrapper">
        <span class="transformed"></span>
        <span class="transformed"></span>
      </span>
   </div>

 </header>
Aldarcie answered 29/1, 2021 at 23:36 Comment(0)
M
0
filter: blur(0)
transition: filter .3s ease-out
transition-timing-function: steps(3, end) // add this string with steps equal duration

I was helped by setting the value of transition duration .3s equal transition timing steps .3s

Mydriatic answered 1/3, 2018 at 9:13 Comment(0)
G
0

The blurring occurred for me in Chrome only (Windows and Mac) when animating 'transform' in a keyframe animation. For me, the -webkit-optimize-contrast setting only partially helped. For best results I also had to use a "magic value" for scaleX (slightly larger than 1 instead of 1).

Here's the code that worked:

img {
  image-rendering: -webkit-optimize-contrast;
}

@keyframes scale-in-left {
  0% {
    transform: scaleX(0);
    opacity: 0;
  }
  100% {
    transform: scaleX(1.000001);
    opacity: 1;
  }
}

Here's the code that didn't work (caused blurry images in Chrome):

@keyframes scale-in-left {
  0% {
    transform: scaleX(0);
    opacity: 0;
  }
  100% {
    transform: scaleX(1);
    opacity: 1;
  }
}

In the end, the "working" code removed most of the blurring, but not all of it. Safari and Firefox were still clearer without any special settings.

Note also that just resizing the browser window cleared up the unwanted blurring, suggesting perhaps that something is causing Chrome to fail to execute a final render pass (?).

Grillparzer answered 22/10, 2021 at 0:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.