weepy / jquery.path

Animatation for arcs and bezier curves with jQuery
http://weepy.github.com/jquery.path/
407 stars 79 forks source link

Rotate element along path in line with bezier angle? #3

Open techninja opened 13 years ago

techninja commented 13 years ago

jquery.path is perfect for my needs, but it's missing one little piece. What might be the best way to implement jquery rotate to change the angle of the element set to follow the bezier path, and to keep it's angle in line with the path?

http://code.google.com/p/jqueryrotate/

If you had an image of a car, animated to follow the contours of a hill, you'd want the image element to rotate as well, and there should be enough data to divine the proper angle (given the correct offset) inside the bezier animation function, I just can't seem to figure out what is what. Any ideas?

weepy commented 13 years ago

hrm its actually quite hard. in theory you need to find the 1st derivate of the path to get the gradient at any one point and then calculate the angle from that.

in practice you need to find how far the object moved in the last frame or two and then use that to calculate the gradient and hence the angle.

hope that helps!

On Wed, May 11, 2011 at 1:58 AM, techninja < reply@reply.github.com>wrote:

jquery.path is perfect for my needs, but it's missing one little piece. What might be the best way to implement jquery rotate to change the angle of the element set to follow the bezier path, and to keep it's angle in line with the path?

http://code.google.com/p/jqueryrotate/

If you had an image of a car, animated to follow the contours of a hill, you'd want the image element to rotate as well, and there should be enough data to divine the proper angle (given the correct offset) inside the bezier animation function, I just can't seem to figure out what is what. Any ideas?

Reply to this email directly or view it on GitHub: https://github.com/weepy/jquery.path/issues/3

techninja commented 13 years ago

Interesting stuff!

I actually did go ahead and attempt to implement something like this on the end during the FX stepping, but you can't rely on an angle taken from the immediately previous position because of rounding errors the angle bounces around like crazy. Even with average smoothing it only cut it down from a flickering mess to a drunken wobble. I'll attempt with three to 5 frames distance and see how well that works.

techninja commented 13 years ago

Yep, got it to to work! Unfortunately it's still pretty fidgety, and because it's based off of the frames, the offset depends on the speed of the animation (fairly slow in my case). The following modification of the $.fx.step.path function relies on the jquery rotate plugin mention in comment 1, and the angle finder function in a reachable scope.

var positions = [];  // Holds previous element positions
var frame_read_offset = 15; // Number of frames in the past to read from
var angle_offset = 180; // The number of degrees to add to the final angle
$.fx.step.path = function(fx){
    var css = fx.end.css(1 - fx.pos);

    // Grab current if unset
    if (positions.length == 0){
        positions.unshift({x:parseInt($(fx.elem).css('left')), y:parseInt($(fx.elem).css('top'))});
    }

    // Add next position
    positions.unshift({x:parseInt(css.left), y:parseInt(css.top)});

    // Rotate based on angle from current position and position so many frames ago!
    if (positions.length == frame_read_offset){
        $(fx.elem).rotate(angle(positions[0], positions.pop()) + angle_offset);
    }

    $(fx.elem)
    for(var i in css)
        fx.elem.style[i] = css[i];
}

...and the angle finder function:

function angle(center, p1) {
  var p0 = {x: center.x, y: center.y - Math.sqrt(Math.abs(p1.x - center.x) * Math.abs(p1.x - center.x)
  + Math.abs(p1.y - center.y) * Math.abs(p1.y - center.y))};
  return (2 * Math.atan2(p1.y - p0.y, p1.x - p0.x)) * 180 / Math.PI;
}

This completely falls apart at slow speed, and doesn't work at all until the position buffer fills up to the freame_read_offset, but other than that it seems to manage pretty well. If only We knew the math a bit better I'm quite sure you could plot an exact angle based on the more accurate calculation pre-rounding.

weepy commented 13 years ago

yes i imagine it's all turned into integers ? - is it possible to set the positions from the math rather than the css ?

shouldn't you empty the position buffer every time you take a reading ?

On Wed, May 11, 2011 at 8:00 AM, techninja < reply@reply.github.com>wrote:

Yep, got it to to work! Unfortunately it's still pretty fidgety, and because it's based off of the frames, the offset depends on the speed of the animation (fairly slow in my case). The following modification of the $.fx.step.path function relies on the jquery rotate plugin mention in comment 1, and the angle finder function in a reachable scope.

var positions = []; // Holds previous element positions var frame_read_offset = 15; // Number of frames in the past to read from var angle_offset = 180; // The number of degrees to add to the final angle $.fx.step.path = function(fx){ var css = fx.end.css(1 - fx.pos);

   // Grab current if unset
   if (positions.length == 0){
       positions.unshift({x:parseInt($(fx.elem).css('left')),

y:parseInt($(fx.elem).css('top'))}); }

   // Add next position
   positions.unshift({x:parseInt(css.left), y:parseInt(css.top)});

   // Rotate based on angle from current position and position so many

frames ago! if (positions.length == frame_read_offset){ $(fx.elem).rotate(angle(positions[0], positions.pop()) + angle_offset); }

   $(fx.elem)
   for(var i in css)
       fx.elem.style[i] = css[i];

}

...and the angle finder function:

function angle(center, p1) { var p0 = {x: center.x, y: center.y - Math.sqrt(Math.abs(p1.x - center.x) * Math.abs(p1.x - center.x)

  • Math.abs(p1.y - center.y) * Math.abs(p1.y - center.y))}; return (2 * Math.atan2(p1.y - p0.y, p1.x - p0.x)) * 180 / Math.PI; }

This completely falls apart at slow speed, and doesn't work at all until the position buffer fills up to the freame_read_offset, but other than that it seems to manage pretty well. If only We knew the math a bit better I'm quite sure you could plot an exact angle based on the more accurate calculation pre-rounding.

Reply to this email directly or view it on GitHub: https://github.com/weepy/jquery.path/issues/3#comment_1136042

louisremi commented 13 years ago

I've got a version of the plugin that does exactly that: https://github.com/louisremi/jquery-interval-bookmarklet/blob/gh-pages/jquery.path.js I should have taken some time to contribute back to jQuery.path You have to pass an additional true argument when building your bezier path. I hope that helps

techninja commented 13 years ago

@louisremi Dude. Duuuude! Far better than mine. By grabbing the values before they get integerized the calculations are smooth as butter. Though I'm missing the rotate cssHook, just replacing it with the $().rotate supplied by the rotate plugin and handing it the correct angle makes it work like a treat! Though I'd say instead of passing a boolean over, you pass the rotation angle offset (and then somehow get that over to the fx.step function ;)

weepy commented 13 years ago

sounds great - louisremi - is it ready just to merge in ?

louisremi commented 13 years ago

No, rotate is supposed to work as an option but the code has a bug: it only works with the option enabled. I have to investigate. Also, the rotate cssHooks is no longer maintained, it has been replaced by a transform hook. I'll give it a try this week. Regards, lr Le 11 mai 2011 21:22, "weepy" < reply@reply.github.com> a crit :

sounds great - louisremi - is it ready just to merge in ?

Reply to this email directly or view it on GitHub: https://github.com/weepy/jquery.path/issues/3#comment_1142444

louisremi commented 13 years ago

I've created a new branch that:

It's here: https://github.com/louisremi/jquery.path/tree/rotateOption I'll document it, add an example and issue a Pull Request asap