Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
techninja opened this issue May 11, 2011 · 9 comments
Open

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

techninja opened this issue May 11, 2011 · 9 comments

Comments

@techninja
Copy link

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
Copy link
Owner

weepy commented May 11, 2011

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:
#3

@techninja
Copy link
Author

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
Copy link
Author

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
Copy link
Owner

weepy commented May 11, 2011

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:
#3 (comment)

@louisremi
Copy link

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
Copy link
Author

@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
Copy link
Owner

weepy commented May 11, 2011

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

@louisremi
Copy link

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:
#3 (comment)

@louisremi
Copy link

I've created a new branch that:

  • has the rotate option
  • is backward compatible with the demo page
  • conforms to jQuery Core coding style
  • depends on jquery.transform.light.js to perform the rotation

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants