6

I'm using jQuery.crSpline to animate a graphic along a curved path. I'm pretty happy with the result.

However, the full canvas size is intentionally pretty wide - definitely larger than most screens - so the graphic will run out of viewport space pretty quickly and animate off the screen.

Instead, I'd like browser viewport to follow or centre on the image so that it stays 'in shot'.

How would I go about this with jQuery? Is scrollTop an option?

I've created a jsFiddle demo, based on the crSpline demo source, but with a wide minX setting.


NB: I did first attempt this with YUI3 and Loktar offered a canvas based solution, however I'm not using YUI & canvas any longer.

Community
  • 1
  • 1
Jon Hadley
  • 5,196
  • 8
  • 41
  • 65

2 Answers2

5

Is this what you had in mind? http://jsfiddle.net/gNdwD/33/. It seems a little choppy but its a rough first attempt.

It doesn't seem like crSpline exposes any coordinates on the animated element so we have to periodically observe them and adjust the viewport accordingly:

setInterval(function() {

    var mover = $('#mover'),
        posX = mover.position().left,
        posY = mover.position().top;

    $(window)
        .scrollLeft(posX - $(window).width() / 2)
        .scrollTop(posY - $(window).height() / 2);
}, 10);

I suspect the choppiness happens because our setInterval is out of sync with the $.animate on the mover. You can fix that by running two animations: one on the mover and one on the scrollTop and scrollLeft of a wrapper div. You can simultaneously apply two $.animates like this.

Community
  • 1
  • 1
Maros
  • 1,825
  • 4
  • 25
  • 56
  • Great stuff, exactly what I meant. Is there anyway I could sync the interval better, or integrate directly with the $.animate? Also, what kind of CPU hit will this have? – Jon Hadley Sep 18 '12 at 09:28
  • Instead of using `setInterval`, you can run a second `$.animate` like in this post: http://stackoverflow.com/a/4719034/659910. It can't be run on window so you might have to instead call it on a wrapper div with scrollbars. I don't think you'll have performance problems running this on desktop. – Maros Sep 18 '12 at 09:34
  • this technically works, but it causes serious eye cancer. it is also "bad" because the updates to the div position and the scrolling are not in sync which causes the annoying wiggle-effect. – kritzikratzi Sep 18 '12 at 17:43
  • 1
    My comment above explains how to fix that. Also another posted solution makes huge improvements to the flickering using `$.animate`'s step option. – Maros Sep 18 '12 at 17:53
3

There exists an option for step function in jQuery animate,which is run on every step of the animation.

See second version of function parameters here : http://api.jquery.com/animate/

.animate( properties, options )

propertiesA map of CSS properties that the animation will move toward.

optionsA map of additional options to pass to the method. Supported keys:

duration: A string or number determining how long the animation will run.
easing: A string indicating which easing function to use for the transition.
complete: A function to call once the animation is complete.
step: A function to be called after each step of the animation.
queue: A Boolean indicating whether to place the animation in the effects queue. If false, the animation will begin immediately. As of jQuery 1.7, the queue option can also accept a string, in which case the animation is added to the queue represented by that string.
specialEasing: A map of one or more of the CSS properties defined by the properties argument and their corresponding easing functions (added 1.4).

See this fiddle based on your code, which calls step function to adjust viewport :

http://jsfiddle.net/gNdwD/35/

$('<div id="mover" />')
        .appendTo($(document.body))
        .animate({ crSpline: spline },{
            duration: 20000,
            step: function() {       /* THE STEP FUNCTION TO ADJUST VIEWPORT */
              var mover = $('#mover'),               
              posX = mover.position().left;
              posY = mover.position().top;

              $(window)
              .scrollLeft(posX - $(window).width() / 2)
               .scrollTop(posY - $(window).height() / 2);
            } ,
            complete:function () {
                      // Re-run the demo with a new spline after we're done
                       window.setTimeout(function() {
                       DEMO.run();
                      }, 5000);
            }
        });
DhruvPathak
  • 42,059
  • 16
  • 116
  • 175
  • 1
    hm... somehow at first i thought i saw some strange behaviour with the step function, so i wrote my own answer (which i now delete because i can't see it anymore, not sure what i was doing before...). perfect, simple solution! +1 – kritzikratzi Sep 18 '12 at 22:06