Skip to main content
Participant
December 5, 2022
Question

Auto-Resizing Shape Layer/Rectangle (But With Multiple Lines Of Text)

  • December 5, 2022
  • 3 replies
  • 3382 views

Hello!!

 

I've seen many tutorials on how to make a shape layer (usually a rectangle) auto-resize to fit a line of text in After Effects. However, is there an expression to make a shape layer respond to two or more lines of text, auto-resizing to fit whichever is the longest line of text?

 

If anyone knows an answer to this or a tutorial that I may have missed, that would be appreciated!

This topic has been closed for replies.

3 replies

Community Expert
December 6, 2022

You can use sourceRectAtTime() to determine the size of each text layer, add the combined height and look for the widest layer. Using index instead of layer names this expression will give you the Rectangle Size for 3 text layers:

 

// Rectangle Path Size
// source layers 
src1 = thisComp.layer(index - 3);
src2 = thisComp.layer(index - 2);
src3 = thisComp.layer(index - 1);

// text size
tBox1 = src1.sourceRectAtTime();
tBox2 = src2.sourceRectAtTime();
tBox3 = src3.sourceRectAtTime();

// max width
tWidth = Math.max(tBox1.width, tBox2.width, tBox3.width).toFixed(2);

//  add leading 
lyrSpace = 30;

// total height
srcHeight = Math.ceil(tBox1.height + tBox2.height + tBox3.height + lyrSpace);
tHeight = srcHeight + lyrSpace;

// box size
txtBase = [tWidth, tHeight];

// padding control
roundPad = content("Rectangle 1").content("Rectangle Path 1").roundness;
hPad = 20;
vPad = 40;


box = [(hPad + roundPad)] + [txtBase[0], txtBase[1]];
x = box[0];
y = box[1] + vPad;

[x, y]

 

The hPad, vPad, and lyrSpace can be sliders.

 

The next problem you have to solve is the text layer position. Stack the second and third text layers using this expression:

 

srcLyr = thisComp.layer(index - 1);
srcScl = srcLyr.scale * .01;
srcPos = srcLyr.position;
srcRec = srcLyr.sourceRectAtTime();
srcH = srcRec.height;
srcT = srcRec.top;
lyrT = thisLayer.sourceRectAtTime().top;
srcOfst = (srcH + srcT) * srcScl[1];
linSp = 20;

[srcPos[0], srcPos[1] + srcOfst - lyrT + linSp]

 

The linSP can be a slider.

 

Add this expression to the shape layer position:

 

srcLyr = thisComp.layer(index - 3);
srcLyr.position

 

You also need to write expressions for Rectangle/Position, Rectangle/Roundness. This post is getting kind of long and confusing, so I just shared a project file that contains all of the expressions and something extra. You can save it as an animation preset. I have not compensated for different paragraph justifications on the text layers so they all need to match. 

 

I hope this helps.

 

Look for a tutorial series on sourceRectAtTime() that is going to demonstrate how to automate all kinds of text, shape, and ordinary layers.

Known Participant
July 18, 2023

Hi Rick, 

 

This project file is almost working for me, but I has to much "stuff" I don't need. I hope you can help me out, because I don't see what I can delete without messing up the whole thing or to make it work better.

 

I just want a column with 1 to 3 lines of text. The lines are left aligned and the column must be centered to the middle of the composition. So, for example, if line 3 is the longest line, the column of text must be aligned to the center. The baseline of the lines is fixed, so I don't need any custom leading.

Can you please help me out?

 

See enclosed my AEP based on your AEP. 

Many thanks!

Community Expert
July 20, 2023

Ascenders and defenders will change the calculations slightly. I used code that I use to size and center a background layer with multiple lines. 

 

If you substitute the text layers .sourceRectAtTime.height with .text.sourceText.style.fontSize; you should be able to keep the top where it should be when the descenders are in the first line of the text. If you have descended characters (g, y, etc.) in all lines, the box will be centered. 

 

I don't have time to make it perfect for you now, but if you play with font size everywhere you are using sourceRectAtTime().width, you should be able to solve the problem. You'll still need to use SRaTime.width to keep the text centered.


Try this comp. I used the text size to calculate height. Characters do not fill the entire design box, so the center will be off just a bit. You could add a trim Slider to fine-tune the vertical placement, but I think this solution would work well for most work. 

 

Here's the position expression for the First text layer :

hCntr = thisComp.width/2;
vCntr = thisComp.height/2;

l1 = thisLayer;
l2 = thisComp.layer(index + 1);
l3 = thisComp.layer(index + 2);
lyr1 = l1.sourceRectAtTime().width;
lyr2 = l2.sourceRectAtTime().width;
lyr3 = l3.sourceRectAtTime().width;
w = Math.max(lyr1, lyr2, lyr3)/2;

vSpace = thisComp.layer("First Text").effect("Spacing")("Slider");
h1 = l1.text.sourceText.style.fontSize;
h2 = l2.text.sourceText.style.fontSize;
h3 = l3.text.sourceText.style.fontSize;
h = (h1 + h2 + h3 + vSpace + vSpace)/2 - h1;

[hCntr - w, vCntr - h]

I made a couple of other adjustments to the second and third layers' position properties:

p = thisComp.layer(index - 1).position;
yOfst = thisComp.layer("First Text").effect("Spacing")("Slider"); //space between layers
[p[0], p[1] + sourceRectAtTime().height + yOfst]
Mathias Moehl
Community Expert
Community Expert
December 5, 2022

Pins & Boxes is your friend 🙂

 

Pins & Boxes can create "Pins" at the corners or edges of any layers, and these pins stay attached to the layer if it is changed (say if the text of a text layer is replaced, the layer is moved, scaled,...) In addition, Pins & Boxes can create boxes around arbitrary collections of pins. These boxes resize such that they always includeexactly  all the pins that belong to that box.
Hence, you can simply add pins to the corners of your two text layers and then create a box around all those pins.

You can also parent the second text layer to a pin of the first one, it you want the second text to stay attached to a corner of the first text, for example.

Mathias Möhl - Developer of tools like BeatEdit and Automation Blocks for Premiere Pro and After Effects
Dan Ebberts
Community Expert
Community Expert
December 5, 2022

It seems like sourceRectAtTime().width should give you the width of the longest line, so that part should be the same as for a single line.