Highlighted

Triggering a null object expression via inPoint of an attached child layer

New Here ,
Aug 01, 2020

Copy link to clipboard

Copied

I sure hope I'm barking up the wrong tree with my approach and that there's an easier way to do this.  All my current attempts are failing.  I've got a bunch of layers parented to a null object so that I can use the null object to scroll them up the screen.  I want to incrementally scroll as the composition time hits the inPoint of each child layer.

 

In the transform position expression of my null object I have the following: 

 

//basic example, select the index of either layer 4 or 5 depending on if the time is before the first one's inPoint or not
startIndex = (time<thisComp.layer(4).inPoint) ? 4 : 5; 
//use the inPoint of the currently selected layer based on the statement above
startTime = time - thisComp.layer(startIndex).inPoint;

//only scroll up
ease(startTime, 0,0.45, value, [value[0] ,value[1]-100]);

 

I can verify that during playback, the correct startIndex gets selected over time. I can also verify that startTime hits 0 at the inPoint of layer 4, then goes below 0 and hits zero again at the inPoint of layer 5. However, the value graph (and animation) only ever execute the Ease function for the inPoint of layer 5 (the default of the if statement). Any ideas?

 

Good thing I shave my head, else I'd have already pulled my hair out.

TIA,

Roland

Adobe Community Professional
Correct answer by Rick Gerard | Adobe Community Professional

The code that I provided works without regard to layer names. If I had 100 text layers that I wanted to move into place and sequence I would create all of them, set the out point of all layers to 30 frames, select all layers, apply the animation preset to all layers, Sequence the layers, move to the end of the timeline, set an out point for all layers, then adjust the position of the top layer to set the starting point. Returning to the in point of the timeline all layers will now animate in and push the first layer to the top. Here is the project file and a timelapse of the steps used to create it in less than 2 minutes:

20-0801 bump layers.gif

The problem that I see with your approach is that the search for the layer you should be using is recursive. It's not going to slow down much with four or five layers, but if you have 20 it's going to get pretty slow, and by the time you have 100 it's going to be really slow because the increase in the time it takes to run the routine and go through all of the layers is logarithmic.  I don't know the math for your system exactly but if it takes .005 seconds to look through 2 layers it could take .1 seconds to look through 4, then 2.5 seconds to look through 8, then 40 seconds to look through 16. It might not be that bad, but that's the problem with recursive expressions that have to loop through the If statement and check every variable on every frame before things move on to the next step.

 

The other thing I like about this preset is that the order the layers appear and stack on the screen depends on the layers in point, not on the stacking order in the timeline. If you get feedback from the client and you want layer 3 to appear above layer 8 instead of layer 4 then all you have to do is change the in point. This is would be especially helpful if you have background layers along with the text layers. 

TOPICS
Error or problem, Expressions

Views

98

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

Triggering a null object expression via inPoint of an attached child layer

New Here ,
Aug 01, 2020

Copy link to clipboard

Copied

I sure hope I'm barking up the wrong tree with my approach and that there's an easier way to do this.  All my current attempts are failing.  I've got a bunch of layers parented to a null object so that I can use the null object to scroll them up the screen.  I want to incrementally scroll as the composition time hits the inPoint of each child layer.

 

In the transform position expression of my null object I have the following: 

 

//basic example, select the index of either layer 4 or 5 depending on if the time is before the first one's inPoint or not
startIndex = (time<thisComp.layer(4).inPoint) ? 4 : 5; 
//use the inPoint of the currently selected layer based on the statement above
startTime = time - thisComp.layer(startIndex).inPoint;

//only scroll up
ease(startTime, 0,0.45, value, [value[0] ,value[1]-100]);

 

I can verify that during playback, the correct startIndex gets selected over time. I can also verify that startTime hits 0 at the inPoint of layer 4, then goes below 0 and hits zero again at the inPoint of layer 5. However, the value graph (and animation) only ever execute the Ease function for the inPoint of layer 5 (the default of the if statement). Any ideas?

 

Good thing I shave my head, else I'd have already pulled my hair out.

TIA,

Roland

Adobe Community Professional
Correct answer by Rick Gerard | Adobe Community Professional

The code that I provided works without regard to layer names. If I had 100 text layers that I wanted to move into place and sequence I would create all of them, set the out point of all layers to 30 frames, select all layers, apply the animation preset to all layers, Sequence the layers, move to the end of the timeline, set an out point for all layers, then adjust the position of the top layer to set the starting point. Returning to the in point of the timeline all layers will now animate in and push the first layer to the top. Here is the project file and a timelapse of the steps used to create it in less than 2 minutes:

20-0801 bump layers.gif

The problem that I see with your approach is that the search for the layer you should be using is recursive. It's not going to slow down much with four or five layers, but if you have 20 it's going to get pretty slow, and by the time you have 100 it's going to be really slow because the increase in the time it takes to run the routine and go through all of the layers is logarithmic.  I don't know the math for your system exactly but if it takes .005 seconds to look through 2 layers it could take .1 seconds to look through 4, then 2.5 seconds to look through 8, then 40 seconds to look through 16. It might not be that bad, but that's the problem with recursive expressions that have to loop through the If statement and check every variable on every frame before things move on to the next step.

 

The other thing I like about this preset is that the order the layers appear and stack on the screen depends on the layers in point, not on the stacking order in the timeline. If you get feedback from the client and you want layer 3 to appear above layer 8 instead of layer 4 then all you have to do is change the in point. This is would be especially helpful if you have background layers along with the text layers. 

TOPICS
Error or problem, Expressions

Views

99

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
Aug 01, 2020 0
Most Valuable Participant ,
Aug 01, 2020

Copy link to clipboard

Copied

Your code makes no sense. It's ease(time, startTime,startTime+0.45,...,...). Time is never absolute and you cannot eliminate the only component that actually causes the evaluation.

 

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...
Aug 01, 2020 0
New Here ,
Aug 01, 2020

Copy link to clipboard

Copied

Thanks for having a look.  Sorry, my variable names must be confusing you.  

 

My startTime variable holds the time I want the animation to start, which is the inPoint of the targeted layer. 

My understanding is that to trigger an expression based on the inPoint of a layer you would set the time of the ease to:

t = time - inPoint;

 

That's what I'm doing here, except that I've confusingly named my variable startTime, instead of t.  Either way, it doesn't work.  Also instead of using the inPoint of the current layer (a null object), I'm using the inPoint of a different layer (one of the children).  I can verify by outputting the value that I'm getting the correct inPoint time from the child layer, and the ease function runs just fine.  Problem is, it only runs once and I want it to run multiple times since over time it will hit multiple inPoints of other layers.  In the example above, it only ever runs the last one (layer 5) and skips layer 4. 

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...
Aug 01, 2020 0
New Here ,
Aug 01, 2020

Copy link to clipboard

Copied

For reference:

AESH2.pngAESH1.png

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...
Aug 01, 2020 0
Adobe Community Professional ,
Aug 01, 2020

Copy link to clipboard

Copied

If I understand this correctly you are animating position based on the in and out point of a bunch of other layers. That's going to get really complicated and recursive very quickly. My approach would be to use one expression for the position that looks at the position of the layer above (index - 1), adds the height of the current layer to the position plus and offset, and then relies on the in point to make the move happen. I have about 40 auto move presets that just rely on lin and out points of layers to animate position. All you do is put the layer in its hero position, set the in and out point, and then moves into the frame then moves out before the out point.

 

I don't have time to write it for you but if you are fairly good with expressions you should be able to figure it out. 

 

Edit. The previously posted expressions were a good starting point but this one will move a new text layer below the current text layer above it over 20 frames. I did have some time for fiddling around while waiting for feedback from a client.

 

 

// test for layer above
if (index -1 == 0)
	position
else {
	mstr = thisComp.layer(index - 1);
	scaleFactor = mstr.scale * .01;
	mstrX = mstr.sourceRectAtTime().width;
	mstrY = mstr.sourceRectAtTime().height;
	mstrSize = [mstrX *scaleFactor[0], mstrY * scaleFactor[1]];
	newPos = mstr.position - mstrSize;
	pad = 10 // distance between layers
	ofst = [mstr.position[0], newPos[1] - pad]
	startPosition = mstr.position;
	endPosition = ofst;
	startMove = time - inPoint;
	t = startMove/thisComp.frameDuration;
	moveFrames = 20 // number of frames for moveFrames
	yMove = linear(t, moveFrames, 0, mstr.position[1], ofst[1]);
	[ofst[0], yMove]
};

 

 

 combine that with a similar approach to opacity and you will have a new text layer appear behind the current text layer and move above it while maintaining position with the master text layer.

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...
Aug 01, 2020 0
Adobe Community Professional ,
Aug 01, 2020

Copy link to clipboard

Copied

Here's my final take on the expression + one for Opacity. I've saved it as an animation preset. It stacks text or shape layers on top of each other in the by the order of their in points and moves the next layer above the previous layer and always keeps the layers ordered by the first layers in point, not the layer order in the timeline. I've saved it as an animation preset. It was a good exercise.

// Apply to Position

// test for layer above 
if (index -1 == 0){
	startMove = time - inPoint;
	t = startMove/thisComp.frameDuration;
	ofst = thisLayer.sourceRectAtTime().height;
	moveFrames = 20 // number of frames for moveFrames
	yMove = linear(t, moveFrames, 0, position[1], position[1] - ofst);
	t = [value[0], yMove]
}
else {
	mstr = thisComp.layer(index - 1);
	scaleFactor = mstr.scale * .01;
	mstrX = mstr.sourceRectAtTime().width;
	mstrY = mstr.sourceRectAtTime().height;
	mstrSize = [mstrX *scaleFactor[0], mstrY * scaleFactor[1]];
	newPos = mstr.position - mstrSize;
	pad = 10 // distance between layers
	ofst = [mstr.position[0], newPos[1] - pad]
	startPosition = mstr.position;
	endPosition = ofst;
	startMove = time - inPoint;
	t = startMove/thisComp.frameDuration;
	moveFrames = 20 // number of frames for moveFrames
	yMove = linear(t, moveFrames, 0, mstr.position[1], ofst[1]);
	[ofst[0], yMove]
};

// Apply to Opacity

startMove = time - inPoint;
t = startMove/thisComp.frameDuration;
moveFrames = 20 // number of frames for moveFrames
fadeIn = linear(t, 0, moveFrames, 0 , 100);

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...
Aug 01, 2020 0
New Here ,
Aug 01, 2020

Copy link to clipboard

Copied

Rick,

 

I really appreciate you taking the time to whip that up for me.  I was able to make it work with the following code (for anybody else who may need something like this):

var timeSnapshot = time;
const moveIncrement = 200;
t = Math.round(timeSnapshot);

//ugly if/else to find the index we should be targeting
startIndex = (t > Math.round(thisComp.layer(9).inPoint)) ? 10 : (t > Math.round(thisComp.layer(8).inPoint)) ? 9 : (t > Math.round(thisComp.layer(7).inPoint)) ? 8 : (t > Math.round(thisComp.layer(6).inPoint)) ? 7 : (t > Math.round(thisComp.layer(5).inPoint)) ? 6 : (t > Math.round(thisComp.layer(4).inPoint)) ? 5 : 4;
targetTime = thisComp.layer(startIndex).inPoint;
startTime = time - targetTime;

//find the time to kick off the animation 
isFound = false;
for (f = timeToFrames(targetTime); f <= timeToFrames(); f++) {
    foundTime = framesToTime(f);
    if (startTime <= timeSnapshot) {
        isFound = true;
        break;
    }
}

if (isFound) {
    //get our new Y value via ease function
    ease(time - foundTime, 0, .25, value, [value[0], value[1]-moveIncrement]);
} else {
    //else return the old value
    value;
}

AESH3.png

 

As you can see, the animation kicks off at the inPoint of each layer in order of their inPoint.  I'll tuck this approach away for later, however, I'm not quite where I need to be.  I need the animation to be cumulative.  So that instead of the items popping up and then going away, they continue popping up.  Haven't quite figured it out since the transform value I'm applying always goes back to the original value when the animation kicks off again.  I'm guessing I need to somehow store the old value in a null object of some sort but my attempts at that haven't been fruitful as of yet.  For context, I'm animating text messages scrolling up the screen as they pop up.  I know I could keyframe this out but I'm looking for a more general approach since I'm going to have to do a lot of different compositions of this.

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...
Aug 01, 2020 0
New Here ,
Aug 01, 2020

Copy link to clipboard

Copied

Rick, looking through your code, seems like it may be what I need.  I was hoping to have a solution that I wouldn't have to apply to every layer and instead have centralized in a single spot (like a null obj); however, it seems like a small price to pay to have a working solution and to move forward.  I'm taking a break from the screen for a while but will test it out when I'm back and mark your reply as the answer if it suffices.  Either way, I very much appreciate you taking the time to think through that on my behalf. 

 

-R

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...
Aug 01, 2020 0
Adobe Community Professional ,
Aug 02, 2020

Copy link to clipboard

Copied

The code that I provided works without regard to layer names. If I had 100 text layers that I wanted to move into place and sequence I would create all of them, set the out point of all layers to 30 frames, select all layers, apply the animation preset to all layers, Sequence the layers, move to the end of the timeline, set an out point for all layers, then adjust the position of the top layer to set the starting point. Returning to the in point of the timeline all layers will now animate in and push the first layer to the top. Here is the project file and a timelapse of the steps used to create it in less than 2 minutes:

20-0801 bump layers.gif

The problem that I see with your approach is that the search for the layer you should be using is recursive. It's not going to slow down much with four or five layers, but if you have 20 it's going to get pretty slow, and by the time you have 100 it's going to be really slow because the increase in the time it takes to run the routine and go through all of the layers is logarithmic.  I don't know the math for your system exactly but if it takes .005 seconds to look through 2 layers it could take .1 seconds to look through 4, then 2.5 seconds to look through 8, then 40 seconds to look through 16. It might not be that bad, but that's the problem with recursive expressions that have to loop through the If statement and check every variable on every frame before things move on to the next step.

 

The other thing I like about this preset is that the order the layers appear and stack on the screen depends on the layers in point, not on the stacking order in the timeline. If you get feedback from the client and you want layer 3 to appear above layer 8 instead of layer 4 then all you have to do is change the in point. This is would be especially helpful if you have background layers along with the text layers. 

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...
Aug 02, 2020 1
New Here ,
Aug 02, 2020

Copy link to clipboard

Copied

Fantastic solution.  Thank you Rick!

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...
Aug 02, 2020 0