Rotate element along path in line with bezier angle?
techninja opened this issue · 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?
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
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.
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.
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)
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 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 ;)
sounds great - louisremi - is it ready just to merge in ?
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)
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