35

I've got some elements that I'm moving across the page very slowly. Essentially, I'm decreasing the left margin of two images over a span of 40 seconds or so.

Visually, it's working beautifully. However, my processor jumps to about 50% usage during the animations. This isn't specific to any single browser either- it's the same way on Safari3 and Firefox3. If I have both browsers running the page, my CPU is screaming at about 95% usage.

I'm using jQuery 1.3. Both animations are happening concurrently. There's no Flash on the page. If I comment the code out (remove the animation) and refresh the page, my processor immediately returns to normal usage.

I'm hoping I don't have to resort to Flash, but even watching shows on Hulu.com doesn't spike my CPU like this.

Thoughts?

Jeremy Ricketts
  • 2,601
  • 4
  • 29
  • 28

7 Answers7

44

I know this is an oldish question and Tim provided a great answer, but I just thought I should post an update for anyone looking for a solution to this problem, since there's now a simpler way...

As of jQuery 1.4.3 you can set the interval jQuery's animate uses directly via the jQuery.fx.interval property. So you can simply do something like:

jQuery.fx.interval = 50;
Community
  • 1
  • 1
Alconja
  • 14,834
  • 3
  • 60
  • 61
  • 1
    this sets a global interval for all animations. du you happen to know how an interval can be set for only one specific animation? – Björn Jun 04 '12 at 23:38
  • 1
    @Marcel - Not that I'm aware of, I believe all animations share the same internal timer, but I could be wrong... – Alconja Jun 05 '12 at 03:15
  • 1
    24fps, the speed at which a lot of filming is done at, seems to represent a good starting point for playing with this number. So, 1000ms / 24fps = ~42 – Matt Parkins Nov 12 '12 at 15:46
  • 2
    The default is `13` (milliseconds) – bendytree Apr 23 '13 at 13:09
34

I think the way jQuery animate() works is that it uses a timer that periodically fires and invokes a function that updates the DOM to reflect the state of the animation. Typically animations are relatively short and they may cover a fair amount of screen real estate, so I suspect (without confirming) that the timer expires, and is reset, at a fairly high rate to generate a smooth animation. Since your animation takes a long time, you might be able to modify the animate function so that the rate at which the animation proceeds can be set via an option. In your case you'd only need to update every 250ms or so since you're covering about 3-4 pixels per second, roughly.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • 2
    Since the animation is already slow, I wonder if that would make the animation poke along in a jaggy sort of way. Trying to avoid the page looking like an old Atari game. haha. I dunno, do you think Flash would be better equipped for this? – Jeremy Ricketts Jan 19 '09 at 21:51
  • No. You would have to make the whole page flash. IIRC, you cant have it as a background element – StingyJack Jan 19 '09 at 21:52
  • This bit would be in the header area of the site. So I think putting a flash movie behind the top content might be an option actually. I don't know the first thing about actually creating a Flash movie though. I work with them a lot, but I don't author them. :-/ – Jeremy Ricketts Jan 19 '09 at 21:58
  • Accepting this one because of the detailed description of the nature of the problem and the suggested workaround. Thank you everyone though! First Stackedoverflow question was a wild success. I'll be back here to help others too. – Jeremy Ricketts Jan 19 '09 at 22:28
  • If you dont need it as a background, then Flash is an option. – StingyJack Jan 20 '09 at 12:52
  • Any idea *how* you'd change the animation refresh rate? – Marcus Downing Jul 11 '09 at 17:41
  • @Marcus -- dig into jQuery and find where the timer is set. Adjust the time until the next event in the animation queue is processed. – tvanfosson Jul 11 '09 at 22:13
20

People have mentioned slowing down jQuery's refresh rate. You can override the timer function in jQuery 1.4 with this file (jquery.animation-fix.js):

function now() {
    return (new Date).getTime();
}
jQuery.fx.prototype.custom = function( from, to, unit ) {
    this.startTime = now();
    this.start = from;
    this.end = to;
    this.unit = unit || this.unit || "px";
    this.now = this.start;
    this.pos = this.state = 0;

    var self = this;
    function t( gotoEnd ) {
        return self.step(gotoEnd);
    }

    t.elem = this.elem;

    if ( t() && jQuery.timers.push(t) && !jQuery.fx.prototype.timerId ) {
        //timerId = setInterval(jQuery.fx.tick, 13);
        jQuery.fx.prototype.timerId = setInterval(jQuery.fx.tick, 2050);
    }
}

So modify the line with this in it

jQuery.fx.prototype.timerId = setInterval(jQuery.fx.tick, 50);

Change the 50 to whatever interval you want. That is in Miliseconds (ms)

If you save this code in a different file, you can attach it like this:

<script src="/js/jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="/js/jquery.animation-fix.js" type="text/javascript"></script>
Tim
  • 201
  • 2
  • 2
  • I was just thinking, I wonder you could bundle this into a plugin that set setInterval on a per-animation basis. Like, you could add a parameter onto http://api.jquery.com/animate/ that changed the interval for that item. hmmm. – Jeremy Ricketts Jun 12 '10 at 20:51
  • Tim, this is exactly what I've been looking for! I love your work :) – David Meister Aug 28 '10 at 09:52
  • 11
    **Note to anyone using this solution**: This functionality is now [built in to jQuery, as of 1.4.3](http://stackoverflow.com/questions/459224/jquery-animate-and-browser-performance/4392685#4392685). (+1 for a great solution for pre-1.4.3 though). – Alconja Dec 08 '10 at 21:46
3

jQuery animate uses the javascript function 'setInterval' to update the obects every few milliseconds. Unfortunately the interval in jQuery is per default '13ms'. Thats 76 updates every second. Way to much for such slow and medium animations.

The 13ms are hardcoded into jQuery. So you can directly change this value in the jQuery.js, only. If you only have the slow clowds you can go up to 100ms. If you have some faster animations, too, you should set it to 50.

You can change the parameter for setInterval(); in the function custom. 'jQuery.fx.prototype { ... custom: function() { ... } ... }'

2

Animations involve looping operations, and those will really crunch the CPU no matter what.

I dont know how easy it is to do with jQuery, but what needs to happen is the animation needs to consume less cycles. Either you make the ani a little more jagged (the display isnt as smooth), the looping needs to be optimized, or reduce the work of the ani.

40 seconds? isnt that a bit long for an animation? I thought they are sposed to be a little more immediate than that.

StingyJack
  • 19,041
  • 10
  • 63
  • 122
  • 1
    The animation is a subtle movement of clouds at the top of a page. They start at a random position and slowly move to the left. – Jeremy Ricketts Jan 19 '09 at 21:48
2

I just watched the performance of animation under Scriptaculous, and found similar CPU spikes: roughly 50% for IE, slightly better performance (16-30%) for Firefox -- both on a DuoCore PC. Since both JQuery and Scriptaculous work by changing the underlying CSS, I think it's safe to say that any Javascript implementation is going to be computationally expensive.

You may well be stuck going with Flash.

rtperson
  • 11,632
  • 4
  • 29
  • 36
0

I really like the answer posted by Tim, although I needed to get this fix working in a Drupal 6 production environment.

I have jQuery 1.3.2 installed, through the use of jQuery update module, so there are a few differences between what I'm using and jQuery 1.4 that Tim's fix is designed for.

Following Tim's instructions got me 90% of the way there, I just had to put my thinking cap on for 10 minutes to come up with this code instead..

timer values of 25 - 33 seem to work a lot better than 13ms for medium speed animations such as fading background images.

var timerId;

function now() {
    return (new Date).getTime();
}

jQuery.fx.prototype.custom = function( from, to, unit ) {
    this.startTime = now();
    this.start = from;
    this.end = to;
    this.unit = unit || this.unit || "px";
    this.now = this.start;
    this.pos = this.state = 0;

    var self = this;
    function t( gotoEnd ) {
        return self.step(gotoEnd);
    }

    t.elem = this.elem;

        if ( t() && jQuery.timers.push(t) && !timerId ) {
    timerId = setInterval(function(){
        var timers = jQuery.timers;

        for ( var i = 0; i < timers.length; i++ )
            if ( !timers[i]() )
                timers.splice(i--, 1);

        if ( !timers.length ) {
            clearInterval( timerId );
            timerId = undefined;
        }
    }, 25);
}
};
David Meister
  • 3,941
  • 1
  • 26
  • 27