jquery.path icon indicating copy to clipboard operation
jquery.path copied to clipboard

Rotate element along path in line with bezier angle?

Open techninja opened this issue 14 years ago • 9 comments

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?

techninja avatar May 11 '11 00:05 techninja

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 < [email protected]>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

weepy avatar May 11 '11 05:05 weepy

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 avatar May 11 '11 06:05 techninja

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.

techninja avatar May 11 '11 07:05 techninja

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 < [email protected]>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

weepy avatar May 11 '11 07:05 weepy

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

louisremi avatar May 11 '11 12:05 louisremi

@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 ;)

techninja avatar May 11 '11 18:05 techninja

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

weepy avatar May 11 '11 19:05 weepy

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" < [email protected]> 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 avatar May 11 '11 21:05 louisremi

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

louisremi avatar May 16 '11 15:05 louisremi