4

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.

ChrisJ
  • 51
  • 1
  • 6
  • Ok, so if I remove the rotations, the position of the arrows update fine. So there seems to be an issue with combining a rotate with a top & left positioning (explains why clicking "Change!" still moved the circle - it wasn't rotated). Also found this GitHub exchange stating the same thing: https://github.com/batiste/sprite.js/issues/10 – ChrisJ Feb 28 '12 at 21:51
  • Pfft... Ok so I managed to get it to work in Safari, but I wish I hadn't. If I apply the rotate 1ms after the move on a timer, then it looks fine, but makes the code looks like a total mess when I'm actually just working round a bug in Safari. I think I'll just leave Safari out of it until they've fixed it (as it is in Chrome) - they get non rotated sprites like IE8! – ChrisJ Feb 28 '12 at 22:50

2 Answers2

3

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:...;
};
Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Hi Rob, thanks for your suggestions! I think some strange things happen with translate and rotate are applied - my arrows start going a bit mad (I think it's because they are rotated from an axis not at the centre of the div). Good advice though about using native JS, not jQuery, my refreshes are getting a bit short at 50ms. Also, any idea why my original code worked fine in FF, Chrome & IE but not Saf? Cheers! – ChrisJ Feb 28 '12 at 20:31
  • Ooh, really like your method of attaching vendor prefixes to the rotates :) – ChrisJ Feb 28 '12 at 20:36
  • @ChrisJ I suspect that Safari cannot keep up with that rate of updating. I haven't tested a lower rate, but you should easily be able to find out whether the bug is caused by a too small delay, or by a bug in Safari itself. Just increase the delay for your original code to find out. – Rob W Feb 28 '12 at 23:38
1

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().

Sebolains
  • 752
  • 2
  • 9
  • 16
  • Hi Sebolains. Thanks for your suggestions! I had thought about offset & position, but wasn't sure that they would do other than set the CSS top & left. Offset did work in Safari, to a degree. The movement goes a little wobbly and I think it's probably due to the calculations to position the item based on the requested values. And idea why Safari didn't like my original version when FF, Chrome & IE were happy with it? Thanks again! – ChrisJ Feb 28 '12 at 20:34