Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shaking images in Chrome when animating on odd positions

I'm trying to create some kind of masking animation where an image is revealed from its center (edit: "center" doesn't necessarily mean the center of the actual background image, but the center of the visible area! /edit). It's working fine so far, the only problem is that in Chrome (currently 24.0.1312.52m on Windows 7 x64) the revealed content is shaking.

Here's an jsfiddle example: http://jsfiddle.net/BaKTN/

So far I've found out that this can be fixed by not disabling background-repeat (http://jsfiddle.net/BaKTN/1/). Not sure what exactly happens internally, but this makes the image stay where it belongs. Unfortunately that's only half of the problem, shaking occours again when the outer container is placed on odd coordinates, which can for example happen when using percentage based positioning (at least internally the coordinates might be odd).

Here's another jsfiddle example showing this behaviour: http://jsfiddle.net/VbgXB/

edit: Using fixed positioning seems to help, even with more outer containers that would cause odd coordinates everything looks fine. However this workaround is ofcourse only conditionally useful, since it will messs up scrolling: http://jsfiddle.net/BaKTN/5/. So I'm still looking for another trick. /edit

Any ideas what is actually causing this problem and how to circumvent it without changing the containers coordinates? Might this have something to do with this bug: http://code.google.com/p/chromium/issues/detail?id=140038 ?

ps. please note that this is ment to become part of a larger animation (multiple tiles) which needs to stay CSS2 compatible!

like image 387
ndm Avatar asked Jan 11 '13 16:01

ndm


1 Answers

This is probably due to partial pixel values for the background-position. Rounding the values seems to fix it: http://jsfiddle.net/BaKTN/2/

(function($) {
  $.extend($.fx.step,{
    backgroundPosition: function(fx) {
      if (typeof fx.end == 'string') {
        var start = $.css(fx.elem,'backgroundPosition');
        start = toArray(start);
        fx.start = [start[0],start[2]];
        var end = toArray(fx.end);
        fx.end = [end[0],end[2]];
        fx.unit = [end[1],end[3]];
      }
      var nowPosX = [];

Relevant code

      nowPosX[0] = Math.round(((fx.end[0] - fx.start[0]) * fx.pos) + fx.start[0]) + fx.unit[0];
      nowPosX[1] = Math.round(((fx.end[1] - fx.start[1]) * fx.pos) + fx.start[1]) + fx.unit[1];

End Relevant code

      fx.elem.style.backgroundPosition = nowPosX[0]+' '+nowPosX[1];

      function toArray(strg){
        strg = strg.replace(/left|top/g,'0px');
        strg = strg.replace(/right|bottom/g,'100%');
        strg = strg.replace(/([0-9\.]+)(\s|\)|$)/g,"$1px$2");
        var res = strg.match(/(-?[0-9\.]+)(px|\%|em|pt)\s(-?[0-9\.]+)(px|\%|em|pt)/);
        return [parseFloat(res[1],10),res[2],parseFloat(res[3],10),res[4]];
      }
    }
  });
})(jQuery);

Edit — Converting your #container position to (whole) pixels instead of percentages will fix the second case, but it won't adjust its position when the window is resized: http://jsfiddle.net/VbgXB/1/

var $con = $("#container");
$con.css({
  left: Math.round($con.position().left),
  top: Math.round($con.position().top)
});
like image 200
Shmiddty Avatar answered Oct 22 '22 21:10

Shmiddty