Safari: Absolutely positioned DIVs not moving when updated via DOM

I'm trying to animate some absolutely positioned DIVs on the page using JS to update the top and left CSS properties. Not a problem in Chrome, FF and even IE8 but try it in Safari 5 they just sit there... Weirdly if I resize the Safari window they will update their position...

You can see a demo of it here:

http://www.bikelanebrakes.com/tests/flyingThing1.html

The code that updates the DIVs is really simple, just a bit of jQuery (also updates the rotation, that works just fine in Safari):

$(this.elem).css({
 '-webkit-transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
 '-moz-transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
 'transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
 'left': Math.round(this.xPos) + 'px',
 'top': Math.round(this.yPos) + 'px'
});

I've added position:relative to the body... I added the 'px' and rounded down the numbers incase Safari didn't like the long floating point values... Nothing, they just sit there until the window is resized...

Any thoughts or help, much-o appreciated!

Thanks, Chris.


Solution 1:

Replace the top and left properties with the translate sub-property of the transform CSS property. For me, this solved the problem in Safari 5.

Also, do not use string arguments for setInterval.

Demo: http://jsbin.com/okovov/2/

Bird.prototype.draw = function() {
    var transform = 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)' +
                    ' translate('+ this.xPos + 'px,' + this.yPos + 'px)';
    $(this.elem).css({
          '-webkit-transform': transform,
          '-moz-transform': transform,
          'transform': transform
    });
};
// ...
var timer1 = setInterval(function(){bird1.animate();}, 50);
var timer2 = setInterval(function(){bird2.animate();}, 50);
var timer3 = setInterval(function(){bird3.animate();}, 50);

50 milliseconds is a very small delay. Consider optimizing your functions, to make the animation perform smoother, for example, by replacing jQuery methods with vanilla JavaScript:

Bird.prototype.draw = function() {
    this.elem.style.cssText = ['-webkit-', '-moz-', '', ''].join(
        'transform:rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)' +
        ' translate(' + this.xPos + 'px,' + this.yPos + 'px);'
    ); // Result: -webkit-transform: ...;-moz-transform:...;transform:...;
};

Solution 2:

I know this might not be exactly what you're looking for, but you could try using jQuery's .offset() to change their position, as opposed to manually changing their CSS attributes. Your code would look something like this:

$(this.elem).css({
 '-webkit-transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
 '-moz-transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)',
 'transform': 'rotate(' + (( this.angle * (180 / Math.PI) ) * -1) +'deg)'
})
.offset({
 top: Math.round(this.yPos),
 left: Math.round(this.xPos)
});

I hope that helps!

PS: if you're looking to set the relative position, use jQuery's .position().