Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CSS3 Transform causing text to flicker in Safari and Firefox Mac Yosemite

I'm having this weird issue on Safari and Firefox (Mac/Yosemite) that causes almost all of the text on the page to flicker when hovering over the transforming element.

Example gif: (Firefox, Yosemite)

enter image description here

.usp {
   //USP has an icon that is defined below
    opacity: .4;
    @include transition(all .3s ease-in-out);


    &:hover { 
        opacity: 1;
        @include transition(all .3s ease-in-out);


        .icon {
            @include transform(scale(1.1));
            @include transition(all 1.7s ease-in-out);
        }
    } // :hover
} 

.usp .icon {
    display: block;
    height: 75px;
    width: 75px;
    // Insert background-image sprite (removed from this example)
    @include transition(all .3s ease-in-out); 
    @include transform(scale(1.0));
}

I've tried the following things:

Add every possible combination of these styles to the body, the transforming element and/or his parent

-webkit-transform-style:preserve-3d;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
 backface-visibility: hidden;
-webkit-filter: opacity(.9999);
-webkit-font-smoothing: antialiased;
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-webkit-transform: translate3d(0, 0, 0);
 transform: translate3d(0, 0, 0);
-webkit-font-smoothing: subpixel-antialiased;
-webkit-text-stroke: 0.35px;

If (styles below) are applied to the body, the problem is fixed in Safari but not in Firefox as it is not a webkit browser.

-webkit-transform: translate3d(0, 0, 0);
-webkit-text-stroke: 0.35px;

I have literally no idea what causes this and how I can fix it!

like image 955
Rick Doesburg Avatar asked Jun 04 '15 07:06

Rick Doesburg


4 Answers

Alright!

After a week of testing, removing and adding CSS rules I finally found the solution that fixed my problem. I originally had this problem in both Firefox 39 and Safari 9 but Firefox mysteriously fixed itself with the latest update. Safari however, did not. The problem has to do with the 3D rendering of elements on the page. The element I tried to scale had to be transformed into 3D context, the flickering elements on the page switched between 2D and 3D as explained by @Woodrow-Barlow in the other answers.

By adding

-webkit-transform: translate3d(0, 0, 0);

to the flickering elements, and thus rendering them in 3D on page load, they no longer had to switch!

EDIT 1: For people who have this issue in other browsers, take a look at the CSS 'will-change' property: https://dev.opera.com/articles/css-will-change-property/

like image 162
Rick Doesburg Avatar answered Nov 09 '22 02:11

Rick Doesburg


Note to OP: you seem to be at least aware of most of these concepts, but I've included plenty of detail in this answer for anyone else who might have a similar problem.

In modern browsers running on a computer that has a dedicated GPU (processor for rendering graphics), the browser will sometimes shift the task of rendering the webpage from your CPU (the "normal" processor) to the GPU. The CPU and GPU each have their own strengths and weaknesses -- the nature of the GPU is that it can render 3-dimensional transformations very efficiently, but might not be able to render plain text as crisply as the CPU does.

Your hover effect is using a transformation that your browser has deemed appropriate for hardware-accelerated GPU rendering (most probably triggered by the scale(1.1) transformation), and so it has shifted rendering temporarily to the GPU while the hover animation/transition occurs. After the animation is done, the CPU takes over rendering again. Because of the different rendering strategies used by the different pieces of hardware, the text looks different (less crisp) while the GPU is in charge.

Unfortunately, we don't (yet) have explicit control of hardware acceleration via the CSS -- the browser sets that whenever it wants to. What we can do, however, is set some properties that we suspect will put the browser in hardware-accelerated GPU mode. The theory here is that we will keep the browser in GPU mode even when the animation isn't occurring, so that we don't see a "flicker".

This strategy has some downsides: users visiting your website will experience slightly increased RAM memory usage while your page is open, and mobile / laptop users will experience slightly increased battery drain. On devices that don't have a dedicated GPU, hardware acceleration won't be triggered (but then again, those devices wouldn't be seeing the "flicker" that you're seeing anyway).

It looks like you've attempted to do this already by adding the scale(1.0) property to the un-hovered element -- my best guess is that your browser(s) got "clever" and detected that this rule does nothing and ignored it. The most dependable way of triggering hardware-acceleration is typically a transformation on the Z-axis. Try changing your transformation to the following:

@include transform(scale(1.00001), translateZ(0.00001));

Rather than using the values "1" and "0", I'm using infinitesimally close values; hopefully this will prevent the browser from getting "clever" and ignoring the rule.

I'm assuming your Sass include there is doing vendor-prefixing. Double-check that the final output includes not only the -webkit-transform: and -moz-transform:, but that it also contains the un-prefixed transform: syntax. If you want to be certain (for the purposes of debugging), just type them out manually:

.usp .icon {
  transform: scale(1.00001), translateZ(0.00001);
  -webkit-transform: scale(1.00001), translateZ(0.00001);
  -moz-transform: scale(1.00001), translateZ(0.00001);
}

On my end, I didn't experience any flicker to start with on your fiddle (I suspect that my browser/OS/hardware configuration doesn't think that a 2-dimensional scale is appropriate for the GPU), so I'm unable to test this code. However, I have used similar techniques to solve similar problems quite often.

like image 33
Woodrow Barlow Avatar answered Nov 09 '22 01:11

Woodrow Barlow


Ahhh, but have you tried

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

http://jsfiddle.net/j04mayvb/4/

enter image description here

I honestly have no idea why this works, but I can see it stopping the flickering on your Fiddle in Safari.

like image 3
MattDiamant Avatar answered Nov 09 '22 03:11

MattDiamant


OK!

So, the problem i faced was in a custom popup where i had a wheel effect on the cross button using css transition. But that caused flicker issue in the popup.

After visiting various online portals, i got to know that transition property:

webkit-backface-visibility: hidden;

on the transitioned element really works and stops the flickering. But using this property blurred the whole component (popup) in my case and to stop that, i had to use another property on the root element of the component:

webkit-transform: translate3d(0, 0, 0);

but as i was using it in a custom popup which was already translated -50% in y-direction to keep it in centre, i was restricted not to use it.

After playing with css properties for days and trying out various solutions, i came to a conclusion that total height of the component in which transitioned element is used must be EVEN and in case of dynamic data, we need to adjust the margins and paddings in such a way that total height remains EVEN.

This resolved my issue. Just make sure total height remains EVEN and adjust margins and paddings accordingly.

Hope it helps anyone in need too. :)

like image 1
Avaneesh Tripathi Avatar answered Nov 09 '22 03:11

Avaneesh Tripathi