Highlighted

Expression to animate position based on layer edges?

Community Beginner ,
Apr 18, 2020

Copy link to clipboard

Copied

Is there a way to animate the position of a layer based on its edges using an expression? Ideally this would work with or without a start keyframe and end keyframe already on the layer because it should start and finish the animation using the edges of the layer.

 

This is what the final output would look like. The keyframes would be 'hold' keyframes hence the 'jumps' in the movement.

 

giphy

 

Thanks!

Mason

Most Valuable Participant
Correct answer by Mylenium | Most Valuable Participant

You have to make up your mind and keep it simple. Why would you even bother whith mangling time when it could be as trivial as a fixed amount of pixels per frame which can always be easily calculated?! And there's no reason to make it even more complicated by parenting. You have everything you need: the comp width, the layer width (or that of the bounding box) and how long you want it to take to cross the screen. It only requires simple math:

mDur=2; //time in seconds
mComp=thisComp.width;
mLayer=thisLayer.width;

mStart=mComp+mLayer*0.5;
mEnd=-mLayer*0.5;

linear(time,inPoint,inPoint+mDur,mStart,mEnd)

 

Mylenium

TOPICS
Expressions

Views

95

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

Expression to animate position based on layer edges?

Community Beginner ,
Apr 18, 2020

Copy link to clipboard

Copied

Is there a way to animate the position of a layer based on its edges using an expression? Ideally this would work with or without a start keyframe and end keyframe already on the layer because it should start and finish the animation using the edges of the layer.

 

This is what the final output would look like. The keyframes would be 'hold' keyframes hence the 'jumps' in the movement.

 

giphy

 

Thanks!

Mason

Most Valuable Participant
Correct answer by Mylenium | Most Valuable Participant

You have to make up your mind and keep it simple. Why would you even bother whith mangling time when it could be as trivial as a fixed amount of pixels per frame which can always be easily calculated?! And there's no reason to make it even more complicated by parenting. You have everything you need: the comp width, the layer width (or that of the bounding box) and how long you want it to take to cross the screen. It only requires simple math:

mDur=2; //time in seconds
mComp=thisComp.width;
mLayer=thisLayer.width;

mStart=mComp+mLayer*0.5;
mEnd=-mLayer*0.5;

linear(time,inPoint,inPoint+mDur,mStart,mEnd)

 

Mylenium

TOPICS
Expressions

Views

96

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Apr 18, 2020 0
Most Valuable Participant ,
Apr 18, 2020

Copy link to clipboard

Copied

Easy enough to calculate if you know the bounding box size and that can easily be achieved with the sourceRectAtTime() function these days. Enough examples out there, so a little web search will be very enlightening.

 

Mylenium

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Apr 18, 2020 0
Community Beginner ,
Apr 18, 2020

Copy link to clipboard

Copied

Thanks! Currently, I've got this expression on a controller null. Just need the animation to be timed to the layer's width. Only problem is sourceRectAtTime() wouldn't help if I'm using non-Shape layers though. I would need to use thisComp.width, right? Where would you suggest I place that in this expression?

 

 

 

posterizeTime(3); value+time*-1000

 

 

 

 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Apr 18, 2020 0
Most Valuable Participant ,
Apr 18, 2020

Copy link to clipboard

Copied

You have to make up your mind and keep it simple. Why would you even bother whith mangling time when it could be as trivial as a fixed amount of pixels per frame which can always be easily calculated?! And there's no reason to make it even more complicated by parenting. You have everything you need: the comp width, the layer width (or that of the bounding box) and how long you want it to take to cross the screen. It only requires simple math:

mDur=2; //time in seconds
mComp=thisComp.width;
mLayer=thisLayer.width;

mStart=mComp+mLayer*0.5;
mEnd=-mLayer*0.5;

linear(time,inPoint,inPoint+mDur,mStart,mEnd)

 

Mylenium

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Apr 18, 2020 0
Community Beginner ,
Apr 18, 2020

Copy link to clipboard

Copied

Thank you! Is there a way to include posterizeTime? 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Apr 18, 2020 0
Most Valuable Participant ,
Apr 18, 2020

Copy link to clipboard

Copied

Posterizing the time is independent from the rest as it really just - erm - posterizes the time. Feel free to insert wherever you want before the linear() function. I also made an oopsie, as of course this would require to be a two-dimensional vector to get correct position So it should better read:

X=linear(time,inPoint,inPoint+mDur,mStart,mEnd);
Y=value[1];

[X,Y]

Mylenium 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Apr 18, 2020 1
Adobe Community Professional ,
Apr 18, 2020

Copy link to clipboard

Copied

If you want to fully automate this kind of a move you need to either write a very complex expression that can compensate for scale, anchor point, decide if the layer is a text layer, shape layer, or standard layer, compensate for left, right or centered paragraph alignment and if the layer is 3D the expression has to compensate for zoom and distance from the camera. All of that is doable but it gets very messy very quickly.

 

The other option is to write a simpler expression that counts on the Anchor Point being in the center of the layer, Rotation to be 0º,  and the scale to be 100%. As long as the layers are 2D you'll need two of them, one for shape or text layers and one for regular layers.

 

If you stick with 2D layers is easy to set the start and end position like this:

 

 

strtPosition = width/2 + thisComp.width;
endPos = -wldth/2;

 

 

If you are using shape or text layers then the Paragraph Style needs to be Centered and you use sourceRectAtTime().width for width like this:

 

 

LyrWidth = sourceRectAtTime().width;
strtPosition = LyrWidth/2 + thisComp.width;
endPosition = -LyrWidth/2;

 

 

You can use the layer in point to start the move by just subtracting the in point from the current time.  You could either use a multiplier on LyrWidth to time the move or you could calculate the speed of the move by calculating the difference between the inPoint and outPOint of the layer. From our example, I think that calculating the time based on width might be what you are looking for. 

 

You then just need to decide how many jumps in time you want so you can calculate the multiplier. If the layer is very short then it's going to move very quickly, and if the layer is very long it is going to move slowly. If you set up the move so that 10% of the distance is covered for each jump in position then there are going to be 10 changes in position in a very short amount of time for a narrow layer and  10 jumps in position over a long amount of time for a wide layer. What you might want to do is consider adding an expression control slider or use the in and out points of the layer to adjust the time it takes to get from one side of the composition to the other. Personally, I think I would use the layer's in and out points to set the time of the move, then add a loop to the position value so that the distance was divided by 10 based on the length of the layer. The logic work like this:

 

The moveTime is the difference between the in point and the out point of the layer. Set the frequency of the move. Divide moveTime by the frequency to set up an interval. Set up a loop that uses the moveTime divided by the frequency to subtract the distance between the start and end position divided by the frequency from the start position and you have got it. 

 

I am not very good at writing loop functions so I won't post the complete expression now but I think this is what you need to get started on the loop to calculate the move:

 

 

strtPosition = width/2 + thisComp.width;
endPosition = -width/2;
freq = 10;
shift = (width + thisComp.width)/freq;
moveTime = outPoint - inPoint;
strtTime = time - inPoint;
// loop starts here to calculate newX

// end of loop defines newX
[newX, value[1]]

 

 

I write these kinds of expressions all the time to animate layers based only on their in and out point but I've never written one that uses a loop to make jumps in time.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Apr 18, 2020 1
Adobe Community Professional ,
Apr 18, 2020

Copy link to clipboard

Copied

Mylenium's solution gives you a smooth move starting at the inPoint of a layer over a time that you set.

 

I thought you wanted to simulate hold keyframes. I gave you the start of an expression a few hours ago. I just finished it. In this example the layer will jump from right to left covering the entire comp frame in the number of steps you define in the first line. Here's the expression:

 

 

freq = 10;// Set the number of steps
strtPosition = width/2 + thisComp.width;
movSeg = (width + thisComp.width)/freq;
strtTime = time - inPoint;
runTime = outPoint - inPoint;
timeFactor = runTime / freq;
positionMult = Math.ceil(strtTime / timeFactor);
newX = movSeg * positionMult;
[strtPosition - newX, value[1]]

 

 

Let me explain how it works. 

 

  • The first line sets the number of times you want to divide the motion across the frame
  • The second line adds the width half the width of a layer to the width of the comp to move the left edge of the layer to the right side of the comp frame
  • The third calculates the length of each segment of the move by adding the comp width to the layer width and then divides that distance by the number of steps (frequency)
  • The fourth line resets the time to zero on the in point of the layer
  • The fifth line calculates the total run time of the layer
  • The sixth line divides the time of the layer by the frequency
  • The seventh line is where the magic happens - the Start time is divided by the time factor and rounded up to the nearest whole number
  • The eighth line simply multiplies segment distance by the corrected time
  • The ninth line subtracts the value of the eighth line from the start position and leaves the Y position where it was

No loop accumulator required, just a simple rounding up multiplier.

 

To modify the expression for use with Shape and Text layers you just change the width property to take advantage of the sourceRectAtTime() method. I call the variable shapeWidth and the expression looks like this:

 

 

freq = 10;// Set the number of steps
shapeWidth = sourceRectAtTime().width;
strtPosition = shapeWidth/2 + thisComp.width;
movSeg = (shapeWidth + thisComp.width)/freq;
strtTime = time - inPoint;
runTime = outPoint - inPoint;
timeFactor = runTime / freq;
positionMult = Math.ceil(strtTime / timeFactor);
newX = movSeg * positionMult;
[strtPosition - newX, value[1]]

 

 

I have saved both as animation presets relying on an Expression Control Slider to set the number of steps. I think they may be useful. I will probably also do one for Rotation and maybe even scale. Here are the presets:

Jump Right to Left Text/Shape

Jump Right to Left Layer

Feel free to try, save, and share.

 

When they are done they will compensate for Scale and possibly Anchor Point.  Both of these will recreate what you show in your screenshot without resorting to any fiddling with posterize time. One of these days I will put the 200+ 2D and 3D auto move presets in a package and sell them. 

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Apr 18, 2020 0