Skip to main content
Participant
October 9, 2018
Question

Dynamic keyframe value using expression?

  • October 9, 2018
  • 3 replies
  • 29917 views

Hi, I'll try my best to explain myself here.

I currently have an animation of a Shape Layer using two keyframes controlling the width of the the shape from 0 px to 100 px.

Now what I want to do is to have the second keyframe value be controlled by an expression (so I can link it & make it "dynamic") WHILE retaining the animation curves (velocity) from my predefined keyframes. Is this at all possible? In my mind it seems pretty basic as I just want to change the value of a specific keyframe while retaining the timing and speed of the predefined keyframes.

All I've seen are solutions where they replace the whole animation with some default ease or linear curve. It's crucial for me here to retain the predefined animation.

I'm thinking a solution would be as simple as this in the expression window:

key(2).value = [x, y]; //where I then would be able to set x to sourceRectAtTime().width of a specific object

Thanks in advance!

This topic has been closed for replies.

3 replies

Inspiring
January 27, 2021

Hello,  sorry for being this late (again). But I had the same problem, and thanks to @chrish18591230  I gave it a different try that worked for me. Here it is :

 

prop = thisProperty;
firstValue = prop.key(1).value;
lastValue = prop.key(2).value;

newValue = 500;//this value is to be changed for a controller or whatever

linear(value, firstValue, lastValue, firstValue, newValue);

 

If you link the controller to the newValue, you will be able to have what you want. 

Inspiring
January 27, 2021

ok sorry, I wanted to move my reply, but I cannot delete my first post !....

Inspiring
October 21, 2019

My apologies for posting on a thread that's been inactive for a year, but I was facing this exact same problem in the middle of a big project with a short timeline. Now that the project is over and I've had some time to think about it without a deadline hanging over my head, I think I've solved the problem and wanted to add my solution here in case it helps anyone in the future.

 

 

//reference to the property you've animated
prop = thisProperty
//reference to the first keyframe of the property you've animated
firstKey = prop.key(1).time;
//reference to the last keyframe of the property you've animated
lastKey = prop.key(prop.numKeys).time;
//the total distance traveled between your two keyframes
distanceTotal = prop.valueAtTime(lastKey) - prop.valueAtTime(firstKey);
//the distanced that's already been traveled on any given frame
distanceCurrent = prop.valueAtTime(lastKey) - prop.valueAtTime(time);
//the percentage of the total animation that's been completed on a given frame
percentage = 1 - distanceCurrent/distanceTotal;
//reference to a slider on a control layer that is used to modify the value of our
//final keyframe
offset = thisComp.layer("controls").effect("property offset")("Slider");
//sets the value of the property to be it's current value plus a percentage of the value
//we set with our slider
value + offset*percentage;

 

 

In my example, I was modifying the X Position after having separated dimensions, but this could easily be adapted to any property by just changing what property is being referenced by pos. It works with both positive and negative numbers entered on the slider, allowing you to either increase or decrease the animation, and it handles animations with more than just two keyframes, distributing the offset correctly across the whole animation.

 

The only limitation is that the slider is used to offset the position of your final keyframe, not to set an absolute value for it, but I would think that for the purposes of adjusting templates, that's how you would want it to work anyway.

 

Hopefully this will be useful to someone!

 

*EDIT* I adjusted my expression to just reference the property it's on, so now you can just paste it onto any property you want to adjust and all you'd have to change is relinking offset to your slider control.

Community Expert
October 21, 2019

That might work but I think you have created a recursive expression. I am pretty sure that the greater the distance between keyframes the longer it is going to take to make the calculations. I have not checked but I suspect that if the time between keyframes is 10 frames and it takes 10 seconds to render, when you change the time between keyframes to 60 frames, the time to render may increase to something like 100 seconds, but if you change the time between keyframes to 120 frames the time to render may increase to 500 seconds.  Make sure you check that out before you start sending things to the render cue. Any time you add .time to your expression and ask it to look forward or backward you are asking for an exponential increase in the time it takes to make the calculations.  If the times get rediculous the best thing you can do is to try using the Keyframe Assistant to convert the expression to keyframes. 

 

You might want to check out this thread by Dan Ebberts. Check the last entry.

Inspiring
October 21, 2019

You are correct, I went ahead and checked this in my test comp and here's what I discovered: In a 4 minute long composition there was a 1 minute difference in render time between a version with keyframes 1 second apart and a version with keyframes 4 minutes apart. There was a 1 second render time difference between versions with keyframes 1 second apart and keyframes 10 seconds apart. So your mileage may vary depending on what you are trying to do.

 

For my purposes, I was working on making dynamically resizeable templates for lower third graphics that only had animation lengths of a few seconds and I barely noticed the impact to render time. If you tried to apply this to a 10 minute long animation you'd definitely notice the increase and in that situation someone far smarter than me could probably come up with a more elegant solution.

Mylenium
Legend
October 10, 2018

I'm thinking a solution would be as simple as this in the expression window:

No, it isn't. Keyframe values are read-only to begin with. Any expression can only overwrite/ modify the actual values, not persistently set them in any form. It's inherent in how AE evaluates property streams:

Property base value --> Keyframe value --> Expression result

Furthermore, retaining custom interpolation curves can only be done by referencing an existing curve using valueAtTime() and manipulating the actual time that is used for sampling the value. So if you wanted to reference your existing interpolation, it would still be something like

thisLayer.transform.position.valueAtTime(myTime)[0];

for the X position for instance. myTime could then be defined as your retimed original animation:

myTime=linear(time, key(1).time,key(2).time,newStartTime,newEndTime);

In any case, you need to adapt your thinking. It's more complex than just a single line bit of code.

Mylenium

Inspiring
January 27, 2021

Hello,  sorry for being this late (again). But I had the same problem, and thanks to @Just Some Knucklehead  I gave it a different try that worked for me. Here it is :

 

prop = thisProperty;
firstValue = prop.key(1).value;
lastValue = prop.key(2).value;

newValue = 500;//this value is to be changed for a controller or whatever

linear(value, firstValue, lastValue, firstValue, newValue);

 

If you link the controller to the newValue, you will be able to have what you want. 

DiogoMP
Participating Frequently
February 28, 2023

Right in the money!!!!