2

There have been questions about the absolute timing precision of CSS transitions and about removing jitter for inexpensive simultaneous transitions. However, the answers didn't give me a clear idea of the relative accuracy of the animation timings (e.g. if two simultaneous animations are "in-phase"), especially when the transitions get expensive.

The effect is most obvious when working with images, like in this fiddle, where the image and container are moving in opposite directions simultaneously trying to keep the image in the same absolute position, but the asynchrony is causing jitter:

/* CSS */
#container {
  position:absolute;
  width:200px;
  height:200px;
  left:200px;
  overflow:hidden;
  background-position:-200px -150px;

  -webkit-backface-visiblity:hidden;
  -webkit-transition:all 2s ease-in-out;
  -moz-transition:all 2s ease-in-out;
  -o-transition:all 2s ease-in-out;
  transition:all 2s ease-in-out;
}

/* JS */
$(function() {
  $('#container').css('left', 0).css('background-position', '0 -150px');
});

Curiously, the jitter is consistently to the right of neutral, which means that the image animation phases are a tad ahead of the container's. It's kind of hard to see, but comparing the offset frames to the stationary final frame, the direction bias is visible.

enter image description here

Is there any way to make sure each step of both transitions are rendered simultaneously?

TylerH
  • 20,799
  • 66
  • 75
  • 101
concat
  • 3,107
  • 16
  • 30

1 Answers1

1

I think what you are seeing is referred to as Jank.

It happens because of the CSS properties you are trying to animate. Both of these 2 CSS properties i.e. left & background-position trigger paint & compositing operations. Additionally, left property triggers layout as well.

Have a read on the subject of High Performance Animations and also take a look at which CSS properties trigger which operation over at CSS Triggers.

As a solution, you might want to animate translateX instead of left property. The result will be a little better but we would still have background-position to deal with which would keep triggering the heavy operation of re-painting.

I think the best solution, in my humble opinion and I could be completely wrong in approaching it, is to have an img tag inside your #container element, provide the image as its src and remove all the background related properties from your CSS.

And then move it as well using the same translate mentioned above. This way, hopefully, you will get the smoothest of results.

Take a look at this updated fiddle or the snippet below.

Snippet:

$(document).ready(function() {
  $('#container').css({
    '-webkit-transform': 'translateX(0)',
    '-moz-transform': 'translateX(0)',
    '-o-transform': 'translateX(0)',
    'transform': 'translateX(0)'
  });
  $('#container > img').css({
    '-webkit-transform': 'translate(0px, -150px)',
    '-moz-transform': 'translate(0px, -150px)',
    '-o-transform': 'translate(0px, -150px)',
    'transform': 'translate(0px, -150px)'
  });
});
#container {
  position: absolute;
  width: 200px;
  height: 200px;
  left: 0px;
  overflow: hidden;
  -webkit-transform: translateX(200px);
  -moz-transform: translateX(200px);
  -o-transform: translateX(200px);
  transform: translateX(200px);
  -webkit-transition: all 2s ease-in-out;
  -moz-transition: all 2s ease-in-out;
  -o-transition: all 2s ease-in-out;
  transition: all 2s ease-in-out;
}
#container > img {
  -webkit-transform: translate(-200px, -150px);
  -moz-transform: translate(-200px, -150px);
  -o-transform: translate(-200px, -150px);
  transform: translate(-200px, -150px);
  -webkit-transition: all 2s ease-in-out;
  -moz-transition: all 2s ease-in-out;
  -o-transition: all 2s ease-in-out;
  transition: all 2s ease-in-out;
}
body,
html {
  margin: 0;
  width: 100%;
  height: 100%;
}
span {
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<span id="container">
  <img src="https://images.unsplash.com/photo-1448975750337-b0290d621d6d?crop=entropy&dpr=2&fit=crop&fm=jpg&h=775&ixjsv=2.1.0&ixlib=rb-0.3.5&q=50&w=1450" />
</span>

P.S. As a side note, I am a big fan of GSAP (a JavaScript animation suite of tools). Here is another example using TweenMax (one of the tools from GSAP) which animates x property (a shorthand for translateX within the GSAP world and which also takes care of all the browser prefixes for you behind the scenes) in a more intuitive way using .fromTo() method.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Tahir Ahmed
  • 5,687
  • 2
  • 17
  • 28