safariwebkitcss-transitionsflickertranslate3d

Opacity change during a transition flickers in Safari


I have a composited div (it has translate3d) with an opacity transition:

#bad {
    background-color: red;
    -webkit-transition: opacity .5s linear;
    -webkit-transform: translate3d(0, 0, 0);
}

If I change its opacity while transition is undergoing, it will flicker in Safari.
The flicker happens about once in three seconds and is akin to a white flash.

There is no such problem in Chrome.

Scroll up and down in this fiddle to see what I mean.

The problem does not seem to be limited to opacity—changing -webkit-transform while its transition is undergoing has a similar effect: once in a while the element is rendered in one of the transition's final states.

The problem goes away if I remove -webkit-transform but unfortunately that's not an option right now.
Can I fix this in Safari by some other means?


Solution

  • The problem is changing property values and adding animations need to happen at the same time.

    A race condition not present in OSX 10.5 was introduced when Core Animation was rewritten in C++. I learned this when my experiments with additive animation developed the same flicker. I found the workaround to be Core Animation's kCAFillModeBackwards. I also found the same workaround was effective for CSS Transitions by hacking up my own fork of WebKit, with emphasis on the hacking part. But conjecture doesn't help WebKit devs and I didn't want to annoy them any further. I do think the problem is with Core Animation, not WebKit. I'm guessing that they should use the same CFTimeInterval derived from a single call to CACurrentMediaTime throughout any given CATransaction.

    Unlike transitions, CSS animations do allow for fill modes. It might be difficult to reproduce transition behavior, but that is one option. In particular, the challenge would be replacing interrupted animations with new ones that begin where the previous left off. In other words, it's easy to animate from opacity of 0 to 1 or 1 to 0, but what happens if the user wants to start when current animated progress is at 0.577564? This might require manually modifying the @keyframes style rule, not an easy task.

    Then there is the question of the appropriate animation-fill-mode. Normally you wouldn't want to perform layout using forward filling animations, you'd use the CSS properties themselves. But in this case it might be simple enough to not set the underlying value, instead use only a forward filling CSS animation that gets replaced but never removed when finished. The alternative is setting the underlying value via element.style and at the same time adding a backwards filling CSS animation.

    Of course, the flicker also does not happen if WebKit doesn't use Core Animation. By far the easiest way to prevent the flicker in your case is to not enable hardware acceleration.

    Instead of:

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

    try:

    -webkit-transform: translate(100px, 100px);
    

    http://jsfiddle.net/z6ejt/9/