Jump to content
GreenSock

danehansen

Bezier curves with progress built into points?

Recommended Posts

i am wondering if it is possible to use the BezierPlugin to create a curve that not only goes through a series of points, but will hit each of them at a defined amount of the way through the tween? using an array something like this:

 

var tracker1Positions=
    [
        {x:13.3,    y:19.5,    progress:0},
        {x:14.8,    y:19.0,    progress:0.1},
        {x:16.8,    y:18.3,    progress:0.7},
        {x:19.0,    y:17.9,    progress:0.75},
        {x:21.0,    y:13.3,    progress:1},
    ];

 

my end goal here, is that i have a video that will have a some dom elements following certain items in the video, and i think a bezier curve would make a much smoother way to simulate tracking than just straight tweens. thank you.

Link to comment
Share on other sites

So if you're dictating the progress at each spot, are you wanting the object to speed up and slow down suddenly at each point? In other words if the first segment is 10px long and progress is 0.5, but the second segment is 1000px long and progress is 1, can you see how it would go slow to begin with and then suddenly jump into high speed to cover the ground in the second segment? 

 

I'm guessing that's not really what you want...right?

 

To answer your question, though, you could technically use the BezierPlugin.bezierThrough() method, feed it your points, and then it'll calculate all the bezier data for you and spit back cubic (or quadratic if you prefer) data which you could then turn around and create individual bezier tweens for and control the timing however you please. You'd feed that bezier data into the tween and set the bezier type to "cubic. That way, you could just sequence a bunch of individual tweens with whatever durations you want.

 

In fact, you could map the durations to the progress values you mentioned above so that everything adds up to 1 (the first tween is 0.1 seconds long, the next is 0.6 seconds, then 0.05, then 0.25), drop those in a TimelineLite, and then set that TimelineLite's duration to however long you want the REAL animation to be and it would stretch everything accordingly. Or tween its time(). Lots of possibilities :)

  • Like 1
Link to comment
Share on other sites

i like your solution with the BezierPlugin.bezierThrough() method. i think this will do exactly what i want. but i have some further quesions. so if l pass all my points through that method like so:

 

    var tracker1Positions=
    [
        {x:13.3,    y:19.5,    progress:0},
        {x:34.2,    y:13.8,    progress:0.25},
        {x:54.4,    y:12.4,    progress:0.5},
        {x:63.6,    y:13.8,    progress:0.6},
        {x:75.0,    y:22.8,    progress:0.75},
        {x:78.6,    y:25.2,    progress:0.8},
        {x:84.7,    y:27.0,    progress:0.9},
        {x:89.0,    y:27.3,    progress:1},
    ];

 

        var bezierPoints=[];
        for(var i=0, len=tracker1Positions.length; i<len; i++)
        {
            bezierPoints.push({left:tracker1Positions.x,top:tracker1Positions.y});
        }
        var bezierSegments=BezierPlugin.bezierThrough(bezierPoints);

 

i get back an object of 7 cubic bezier objects. it is one less in length than that of my object, because it is a bezier of each in between, correct?

 

so then would i need to plug these 7 values into a TimelineLite, each with a TweenLite.fromTo call? also for what i am doing, these values need to be percentages, they aren't pixel values, so how does that factor in? i noticed taht calculating the bezierSegments with percentages breaks it and returns a bunch of NaNs so they are going through as numbers at that point. i am guessing that step would look something like this, but from the documentation online i am not fully sure how to finish this:

 

        $scope.timeline=new TimelineLite({paused:true});

        for(i=0, len=bezierSegments.length; i<len; i++)
        {
            $scope.timeline.insert(new TweenLite.fromTo($scope.tracker1, tracker1Positions[i+1].progress-tracker1Positions.progress, {bezier:{type:"cubic"}}, {bezier:{type:"cubic"},ease:Linear.easeNone}),tracker1Positions.progress);
        }

Link to comment
Share on other sites

  • 1 month later...

I'm sorry this slipped through the cracks. Do you still need help with this? 

 

Yes, if you feed in 8 points, that'll return 7 Bezier segments (draw a curve from the first to the last, through all the ones inbetween and you'll see that makes 7 segments). 

 

And yes, the values need to be numeric, not strings. It doesn't really matter what those numbers represent (percentages, pixels, ems, whatever) as long as the tween is just dealing with the raw numbers. 

 

As far as feeding those values back in to build a tween, you could just use the Bezier data you got back to create objects with the appropriate data for a type:"cubic" one. For example, if one of the segment you got back is {a:0, b:10, c:20, d:30} for the "x", your tween would look like {bezier:{type:"cubic", values:[{x:0}, {x:10}, {x:20}, {x:30}]}}

 

And if you've got "y" data, you can add that to the objects, like values:[{x:0, y:0}, {x:10, y:50}, {x:20, y:100}, {x:30, y:200}]

 

I hope that helps. 

Link to comment
Share on other sites

I'm trying to create a bezier curve that I can animate an icon over. I first tried creating the animated icon with the curve x, y coordinates like so:

 

TweenMax.to(icon, 3, {bezier:{type:'soft', curviness:1.25, values: [{x:350, y:95}, {x:200, y:495}, {x:610, y:580}], autoRotate:true}, ease:Power1.easeInOut});

 

Now I am trying to create the bezier line itself by using the HTML drawing canvas api, using the exact coordinates and I'm not getting the same curve?? Is there a reason??

 

context.bezierCurveTo(350,95,200,495,610,580);
 

 

Link to comment
Share on other sites

Hi and welcome to the Greensock forums.

 

It's a tricky thing to accurate draw the exact same curve.

 

One reason could be the curviness factor which, as far as I know, is not considered in Canvas, as you can see in this codepen the results vary depending on the parameters:

See the Pen kjmDo by rhernando (@rhernando) on CodePen

 

Another could be the layout properties of the element, keep in mind that bezier through x and y is a transform function therefore it goes through the default transformation point of the element which is, unless you indicate something else, the centre of the element and usually the element is rendered considering the top left corner. For example if your icon is 100px width and height it's current top and left positions are 0, but the transform point is at 50px left and 50px top, so in this case you have to move the canvas 50px in each direction to make it fit the point where the element is being transformed.

 

You can use this codepen from the Greensock collection:

See the Pen CHwDx by GreenSock (@GreenSock) on CodePen

 

Fork it to meet your scenario and post back any other issue you might run into.

 

Best,

Rodrigo.

Link to comment
Share on other sites

I believe the problem is that you're using two fundamentally different types of Beziers - when you set the type to "soft", that tells BezierPlugin to interpret the values like this:

[control point, anchor, control point, anchor, control point, anchor...]

Notice that's a quadratic Bezier (one control point between the anchors) whereas the canvas bezierCurveTo() is a cubic Bezier (two control points). 

 

Luckily BezierPlugin is super flexible and can handle just about anything you throw at it (cubic, quadratic, or have it draw a line through anchors, whatever). 

 

If you want them to match, you'll probably be most limited by what canvas can do, so you could just use its bezierCurveTo() and then feed those same values to a bezier tween with type:"cubic" such that the values are in this order:

[anchor, control point 1, control point 2, anchor, control point 1, control point 2, anchor...]

Another option (if you want to have BezierPlugin draw a Bezier through a set of points for you) is to use the BezierPlugin.bezierThrough() method and feed it your points. It will spit back to you raw cubic Bezier data which you can then use however you please. See the docs for specifics. 

  • Like 3
Link to comment
Share on other sites

  • 2 weeks later...

ok so to do percentages for bezier, i would just need to tween numeric values on an object, then add an onUpdate function where i use those numeric values and add a "%" when appying that css to the object?

Link to comment
Share on other sites

I'm not entirely sure I understand your goal, but it sounds like you've got it right. Give it a shot and let us know if you run into any trouble. 

Link to comment
Share on other sites

  • 1 month later...

ok so i ended up going with linear tweens on the last project i originally asked about this for (oregongridiron.com) but i want this to work again for my latest project and still having trouble. if i run my points through the BezierPlugin.bezierThrough method, i get back an object with x and y values. each of those is an array that is the length of my points minus 1, and each value of that array is an object with a, b, ba, c, ca, d and da values. so my question is how can i take the segments of this object and make separate tweens with them with durations that i define?

 

example:

var points=
[
    {x:0, y:-50},
    {x:50, y:0},
    {x:0, y:50},
    {x:-50, y:0}
];
var bezierObject=BezierPlugin.bezierThrough(points);
 
//and here is where i dont fully understand how to finish. if i want to tween just from points[0] to points[1] with my own duration, i know where to find the start and finish x and y positions, but i dont know how to fit in the b, ba, c, ca, and da values to make this a curve:
TweenLite.fromTo(targ, 1, {x:bezierObject.x[0].a, y:bezierObject.y[0].a}, {x:bezierObject.x[0].d, y:bezierObject.y[0].d, ease:Linear.easeNone});

 

i suspect the solution is that i would need to build a custom ease to pass into each tween? if so, not positive how to do that but will be looking in the mean time.

Link to comment
Share on other sites

Each segment that's returned has "a", "b", "c", and "d" properties corresponding to the cubic Bezier data (a is the first anchor, b is the first control point, c is the second control point, and d is the second anchor), as described in the docs:

http://api.greensock.com/js/com/greensock/plugins/BezierPlugin.html

 

Here's a little demo that should clear things up for you:

http://codepen.io/GreenSock/pen/5cf4024a57bcafc98dbebddcf945f113

 

The green dot animates through all the points, whereas the red dot only animates along the first segment. 

 

Does that help?

Link to comment
Share on other sites

yes this is extremely helpful. my only question now is how does this fit in with a .fromTo function? or does passing in the segment on a .to function already encompass both the starting and ending values? thank you so much.

Link to comment
Share on other sites

Right, when you use the "cubic" mode for a Bezier, you're already providing the starting value directly in the path data. It isn't as if the tween has to take the object from wherever it currently is, and add extra path data to the beginning to get it to the first spot you're feeding in. A normal "thru" tween will do that for you, of course, because that's the most intuitive behavior, but "cubic" tweens shouldn't (it wouldn't make any sense). See what I mean? 

 

So there's no need to use a fromTo() for a "cubic" Bezier tween. Just a to() is fine. It won't hurt anything if you use a fromTo() - it's just meaningless and a bit wasteful. 

Link to comment
Share on other sites

its all in and functioning perfectly. thank you for your help. i will post a link here once it launches.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×