It's common when creating After Effects templates (such as lower thirds) to set the size of a rectangle based on a text layer. This is so the box will grow to match the size of the layer, no matter the text content.
You do this by linking the size of the box to the width and height of the text layer, with the sourceRectAtTime()
method .
The problem
You can also watch our tutorial on dealing with descenders below:
Linking the box height to the text causes issues as letters with descenders ("y, g, j"
) or ascenders ("h, f, l"
) will cause the height to fluctuate as the copy changes.
Sometimes this is what the design requires, but in most cases it's preferable to set the rectangle height from the x-height rather than the height of the text layer.
X-height is the height of flat topped lowercase letters, such as "x"
, "u"
and "z"
.
Using x-height
Since the x-height is a consistent value across all letters, setting the height of the rectangle based on the x-height means the size won't change when the text contains descenders or ascenders.
Rather than hard-coding the x-height into the expression you can instead get the x-height from the text style
properties.
Getting the size
As of After Effects v17, you can now use expressions to access text style properties, such as the font, tracking and size.
The two style properties needed to calculate the height are:
fontSize
: used to calculate the x-height, which is thefontSize / 2
leading
: the space between baselines
You can destructure these two properties from the style
object:
js
const { fontSize, leading } = thisLayer.text.sourceText.style;
And then use them to get the total height:
js
const { fontSize, leading } = thisLayer.text.sourceText.style;const xHeight = fontSize / 2;const totalHeight = xHeight + leading * (numLines - 1);
Here we're using fontSize / 2
to get the height of the first line, and then the leading
to get the height of the remaining lines with leading * (numLines - 1)
.
You can use the textCount()
function from our library aeFunctions to get the number of lines.
Then you can get the layer's width with sourceRectAtTime()
. The complete expression for getting the descender-less size of the text layer is below.
js
const layer = thisLayer;const { fontSize, leading } = layer.text.sourceText.style;const { width } = layer.sourceRectAtTime();const xHeight = fontSize / 2;// Getting numLines is omitted for brevityconst height = xHeight + leading * (numLines - 1);// The descender-less text sizeconst textSize = [width, height];
You then can use this textSize
value to set the size of the box shape layer, while ignoring descenders and ascenders.
Getting the anchor point
You often need to set the anchor point dynamically as well, so the layer stays in a consistent position as you change the content.
This involves setting the anchor point to the corner of the text, offset by it's size, making sure to set the y
value from the text style
rather than sourceRectAtTime()
.
The anchor point origin for a text layer is at the baseline of the first line, so to set the anchor point to the top left you set each dimension to:
x
: the left value ofsourceRectAtTime
y
:-xHeight
, offsetting the default value by the x-height
For example, to center the anchor point to the text layer, ignoring descenders:
js
const { left } = layer.sourceRectAtTime();const topLeft = [left, -xHeight];topLeft + size / 2;
How we use it
To save writing out this code on each text (and box) layer, we've implemented these ideas in expression libraries aeFunctions and eBox.
Implementing these ideas in libraries avoids duplicating code.
On the text layer
The function layerRect()
handles getting the descender-less position value, for any corner (or center) of the layer.
js
// textLayer.transform.anchorPointlayerRect({ anchor: "center" }).position;
layerRect()
automatically gets the descender-less size and position when used on a Text layer.
Box Path
There are two parts to setting the box size (which we do as an expression on a Path
property):
- Getting the correct
width
andheight
of the text layer - Setting the value for the
Path
property
To get the size and position of the text layer without descenders, we use again uselayerRect()
. This gives us an object with size
and position
properties which we can use to create the rectangle.
js
const layer = thisComp.layer("TXT_Copy");const textRect = layerRect({ layer, anchor: "center" });
We then use the library eBox to create a rectangular path based on the textRect
object.
js
// Destructure functions from librariesconst { createBox } = footage("eBox.jsx").sourceData;const { layerRect } = footage("aefunctions.jsx").sourceData.getFunctions();// Padding around the textconst padding = 36;// Get the text layer size and positionconst textLayer = thisComp.layer("TXT_Copy");const textRect = layerRect({layer: textLayer,anchor: "center",});// Create the box pathconst myBox = createBox({size: add(textRect.size, [padding, padding]),position: textRect.position,anchor: "center",});myBox.getPath();
This expression creates a path that stays centered to the text layer, and matches it size, while ignoring descenders and ascenders automatically.
Blog