Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

This document mainly serves as our internal resource for HootVID’s project expressions (as well as their function and current application) and general project structure. The document’s primary author is Shane Starnes.

...

  • Backend vs Frontend

  • Layer NamesCommon Expressions

  • Bodymovin

  • KBar

  • Common Expressions

...

“Backend” vs “Frontend”

Backend

...

R = sourceRectAtTime(sourceTime(time), true);
w = thisComp.width / R.width * 100;
h = thisComp.height / R.height * 100;
s = Math.min(w, h);
[s,s]

Info

The “100” refers to the percentage of the comp space to take up. Math.min tells AE “stop when either the width or the height touch the edge of the bounds”, basically constraining it by either height or width. Change to Math.max to constrain by the smaller of the two, causing most images to bleed over the edge. Used in select cases.

Auto-scale text to a specified height and width SCALE

maxW = 550;
maxH = 100;
x = sourceRectAtTime();
x2 = sourceRectAtTime(31);
w = x.width;
h = x2.height;
y = w/h > maxW/maxH ? maxW/w: maxH/h;
[100,100]*y;

...

Text WordWrap Source Text

var str = text.sourceText;
var truncateLength = 35;
if (str.length > truncateLength) {
  var lastChar = str.substr(truncateLength-4, 1);
  if (lastChar === " ") {
    str = str.substr(0, truncateLength - 4).trim() + "...";
  } else {
    str = str.substr(0, truncateLength - 3) + "...";
  }
} else {
  str = str;
}
text.sourceText = str;
var lines = str.split('\n');
var maxW = 20;
str = wordWrap(str, maxW);
var lineCount = str.split('\n').length;
function wordWrap(str, maxW) {
    var newLineStr = "\n";
    done = false;
    res = '';
    while (str.length > maxW) {                
        found = false;
              for (i = maxW - 1; i >= 0; i--) {
            if (testWhite(str.charAt(i))) {
                res = res + [str.slice(0, i), newLineStr].join('');
                str = str.slice(i + 1);
                found = true;
                break;
            }
        }
                if (!found) {
            res += [str.slice(0, maxW), newLineStr].join('');
            str = str.slice(maxW);
        }
    }
    return res + str;
}

function testWhite(x) {
    var white = new RegExp(/^\s$/);
    return white.test(x.charAt(0));
};
finalText = time < 0 ? lineCount : str
time > 30 ? finalText + "abcdefghijklmnopqrstuvwxyz|" : finalText

Info

The above expression is quite complicated and due to the iteration algorithm included, will cause major slowdowns in render speeds. We’ll cover how to correct this later. 

A few very important values are highlighted:

  • truncateLength = [number]: This number is the max number of characters added to the text before truncates, and adds “...” automatically. It will have no effect on the fields in the frontend and is near-impossible to represent accurately in the Lottie preview.

  • maxW = [number]: This number is the maximum number of characters per line.

  • finalText = time < 0 ? lineCount : str: We’re creating a variable called “finalText” and assigning it 2 different values, which change over time. If the current time is less than 0 (impossible to see, but still calculable), the sourceText is the number of lines in the final text. We use this value to adjust the layout of the comp based on the number of lines. It can be called to in any other property on any other layer.

    • We can then call to this value in any other property within our AE project. To assign it to a variable, just use:

[variableName] = parseInt([compName].[layerName].text.sourceText.valueAtTime(-1)

  • time > 30 ? finalText + ""abcdefghijklmnopqrstuvwxyz|" : finalText: We are assigning the sourceText a value based on the current time. If the time is greater than 30 (none of our compositions ever are), then add the entire alphabet to it plus a pipe character. Otherwise, display the finalText, which is the intended text to be displayed in the final render.

    • When doing layout with text, letters, and characters that descend beyond the baseline (descenders) can cause layout issues when using sourceRectAtTime(). By using sourceRectAtTime(31), we’re able to grab the sourceRectAtTime of text that includes descenders, and we’re able to make more accurate layout predictions and concessions for all possibilities of text.

...

The other, used in some other cases but is a bit slower:

if (condition) {
do this
}else{
do this instead
}

We can check a million things, but a good use case is checking the length of another layer’s sourceText to see if there’s even any text there at all. A use case that we use often is as follows on the Y Position property:

...

Place two keyframes for the in and out point of your animation

function expoOut(curT,t1,t2,val1,val2){
  if(curT <= t1)return val1;
  if(curT >= t2)return val2;
  dtEase = t2 - t1;
  dvEase = val2 - val1;
  tEase = (curT-t1)/dtEase;
  return val1 + dvEase*(1-Math.exp(-10*tEase));
}

if(numKeys > 1){
  t1 = key(1).time;
  t2 = key(2).time;

  v1 = 80;
  v2 = 400;

  expoOut(time,t1,t2,v1,v2);

}else value

Info

Variables v1 and v2 are the beginning and ending values. They can also be 2 or 3 dimension values. They can also be variables, driven by another condition in the comp.

...