JavaScript Guide

Dan Ebberts

JavaScript is the glue that binds expressions together. If you want to get beyond creating expressions with the pick whip, you need to know how to use JavaScript. The After Effects expressions language is based on only a subset of JavaScript, however, which means there’s a lot about JavaScript that you don’t need to learn. To help you separate the vital from the unnecessary, this guide focuses on the core elements of JavaScript that are essential to master if you really want to understand expressions.

Variables

In multiline expressions, you sometimes need to save an intermediate result for later use in the expression. In such cases, you need to define a variable. A variable is nothing more than a temporary storage location that you can reference later by using the name you give it when you define it. You have a lot of latitude on what you can use as a variable name, but you must be careful not to use any of the words that are actually part of the expression language.

In expressions, you usually define a variable by assigning a value to it. You do this using the JavaScript assignment operator (=). For example, this statement creates the variable myRotation and assigns it the value of 180:

myRotation = 180;

Keep in mind that variables don’t always have to represent numbers. You can also use variables to represent objects, such as compositions, layers, cameras, and so on. For example, this code creates a variable and assigns its value to be the object Layer 1:

myLayer = thisComp.layer("Layer 1");

Close-up: Multiline Expressions and Semicolons

image

You may have noticed that the example statement ends with a semicolon. With multiline expressions, it is good practice to end each JavaScript statement with a semicolon. Although you can often get away with leaving them out, sometimes their absence creates a hard-to-diagnose error, which in turn generates a cryptic error message, so it’s better to include them.

However, you can always get away with leaving out the semicolon in a single-statement expression or in the last statement executed in a multistatement expression. A very common style, it’s the one used in these examples.

You could later use the variable myLayer as a shorthand reference for the object itself. For example, after the previous statement, you could later access Layer 1’s height using

H = myLayer.height;

Otherwise you’d have to type it out:

H = thisComp.layer("Layer 1").height;

You’ll see this technique used frequently in more complex expressions where there may be several references to the same object and this shorthand notation makes things more readable (and easier to type).

In addition to numbers and objects, variables can represent strings of characters, like this:

myString = "abcdefg";

Variables can also be used to store the logical values of true or false. These values are defined as the JavaScript keywords true and false. Here’s an example:

firstTime = true;

Note that logical values can also be calculated using comparison operators (covered later in the “Conditionals” section). For example, the resulting value of variable c in this expression would be false because the value of variable a is less than the value of variable b:

a = 100;
b = 200;
c = a > b;

Operators

One of the first things you need to learn about JavaScript is how to perform basic arithmetic. Arithmetic operators are pretty much the same in most programming languages, so if you’ve written any code at all, you’re ahead of the game.

The addition operator is +, subtraction is , multiplication is *, and division is /. If you want to create a Rotation expression that calculates the sum of the Rotation property of Layer 1 plus 180, for example, you use the addition operator:

thisComp.layer("Layer 1").transform.rotation + 180

In JavaScript, multiplication and division are evaluated before addition and subtraction, but you can use parentheses to change the default order of evaluation. Consider a couple of examples. This expression adds 60 to Layer 1’s Rotation value because multiplication is evaluated before addition:

thisComp.layer("Layer 1").transform.rotation + 15 * 4

Adding parentheses instructs After Effects to first add 15 to Layer 1’s Rotation value, and then multiply the entire result by 4:

(thisComp.layer("Layer 1").transform.rotation + 15) * 4

Advanced Operators

Beyond the basic arithmetic operators, you find the extremely useful modulus, increment, decrement, and shorthand operators. The modulus operator (%) returns only the remainder from a division operation. So where the result of 5/2 would be 2.5 (basic division), the result of 5%2 would be 0.5 (just the fractional part of 5 divided by 2). This turns out to be incredibly useful for making things cycle or repeat in expressions. In the following example, assume that Layer 1’s Rotation is keyframed to ramp from 0 to 360 degrees. The value of this expression then cyclically ramps (a total of 12 times) from 0 to 30:

thisComp.layer("Layer 1").transform.rotation % 30

The increment operator (++) increases a variable’s value by one; for example

a++;

provides a compact notation, but gives exactly the same result as

a = a + 1;

Decrement (--) decreases a variable’s value by one:

a--;

Notice that increment and decrement are unary operators. That is, they require only one operand, where the other arithmetic operators we’ve seen (+, , *, /, and %) each require two operands.

The shorthand operators will save you typing. For example, using the += operator

a += 6;

produces the same result as the less compact expression

a = a + 6;

In addition to +=, you can use –=, *=, /=, and %= operators to do exactly what you would expect.

Comments

Everyone needs reminders, and you can use comments to make notes to yourself about what an expression is doing at any given point. These can be very handy in more complex and lengthy expressions or when you’re writing an expression that you (or someone else) will need to understand in the future. You define the beginning of a comment with a double forward slash (//). Everything on that line to the right of the double slash becomes part of the comment and is ignored by JavaScript. Here’s an example:

a = 100; // this is a comment
// this entire line is another comment

You can also define a block of comments between the “bookends” of /* and */. For example:

/*
All of these lines are part of
one large comment block
and will be ignored by JavaScript.
*/

You won’t run into this longer form of commenting very often in expressions. (It’s much more common in After Effects scripts.)

Arrays

Arrays are a way of collecting multiple, related values within a single variable. For example, if you are working with an expression for the Source Text property of a text layer and you needed to reference abbreviations for days of the week, you can set up an array like this:

daysOfTheWeek = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];

The square brackets tell JavaScript that you’re creating an array. To access individual elements of an array, you append square brackets (enclosing the index of the element you want to retrieve) to the array name. For example, to retrieve the abbreviation for Wednesday, we use

daysOfTheWeek[3]

Notice that the index enclosed in the square brackets is 0-based, which means that the numbering starts at 0 for the first element of the array. To retrieve Sunday’s abbreviation you use

daysOfTheWeek[0]

Arrays have a built-in attribute (length) that tells you the number of elements in the array. In the example, the value of daysOfTheWeek.length is 7. Note that an array’s length is one greater than the index of the last element in the array (the index of “Sat” in the array is 6) to compensate for the array starting at 0.

Why should you care about arrays? In expressions, the values of multidimensional properties such as Position and Scale are stored (and accessed) as arrays. So, if you want to access just the X value of a Layer 1’s Position property, use the syntax

thisComp.layer("Layer 1").transform.position[0]

You can access the Y value like this:

thisComp.layer("Layer 1").transform.position[1]

For 3D layers, you access the Z value with

thisComp.layer("Layer 1").transform.position[2]

To create a Position expression that defines each of its X, Y, and Z components separately, do this:

x = 100;
y = 200;
z = 300;
[x, y, z]

Or, more concisely, like this:

[100, 200, 300]

Notice how the pieces are combined into an array for the final result by using the square brackets.

What if you want to specify just the x value in the expression, but use the pre-expression, keyframed values for y and z? Easy:

x = 100;
y = value[1];
z = value[2];
[x, y, z]

Or

[100, value[1], value[2]]

Note that I’ve strayed a little here; value is not part of JavaScript. It is actually an attribute added by Adobe that gives you the property’s pre-expression value at the current comp time.

Most of the multidimensional properties in After Effects are represented by arrays in a way that you would expect. For example, the three-dimensional Position value of a layer located at the center of a 640×480 composition is [320, 240, 0]. However, color is handled in a way that surprises a lot of people the first time they run into it. Color is represented as a four-dimensional value (for the red, green, blue, and alpha channels, in that order), but the values are normalized so that they are between 0 and 1.0. You might expect, for example, to see 50% gray at 100% alpha represented by [128, 128, 128, 255], but it would in fact be [0.5, 0.5, 0.5, 1.0].

It’s important to note that in JavaScript, strings are considered to be arrays of characters. As an example, suppose you had this expression for the Source Text of a text layer:

myString = "abcdefg";
myString[3]

The result would be the character “d”.

Take a look at one other little trick with arrays before moving on. You can define an empty array as

myArray = [];

You can then add elements at the end of the array like this:

myArray[myArray.length] = 100;

Remember that the length of an array is always one greater than the largest index of that array. So in this case, if myArray has no elements, its length is 0. You can add a new element at the next available index (which is 0) by using the length as the index. After the statement executes, the array’s length is 1. Note that you can extend the size of an array in this manner, but if you try to access an element beyond the end of the array, the expression will generate an error message. For example, this expression generates an error because the index of 1 is outside the current bounds of the array:

myArray = [];  // create a new array
myArray[myArray.length] = 100;  // add an element at
index 0
x = myArray[1]; // attempt to access element at index 1

Array Math

Adobe has implemented the ability to use standard arithmetic operators with arrays. This is not part of core JavaScript, but it is extremely useful. For example, if you want to move a layer 25 pixels to the right and 50 pixels down, you can do it with a Position expression:

value + [25, 50]

Without Adobe’s enhancement (which allows you to add two arrays directly), you have to use something like this:

[value[0] + 25, value[1] + 50]

You can also subtract arrays. For example

value – [25, 50]

moves a layer 25 pixels to the left and up 50 pixels.

With the multiplication operator (*) you can multiply all members of an array by a value. For example

[100, 200] * 6

results in [600, 1200], because the value 6 is multiplied through the array. Note that 6 * [100, 200] gives you the same result.

Division is handled a little differently. Unlike multiplication, the divisor (the number that you’re dividing by) must always be to the right of the array. So

[100, 200]/10

results in [10, 20] but

10/[100, 200]

generates an error. Using this array division operator, you can, for example, represent the center of a comp like this:

center = [thisComp.width, thisComp.height]/2;

Conditionals

Often you want an expression to come up with different results, depending on various conditions. JavaScript offers some nice built-in tools to help you. The simplest form of this is the basic if statement. An if statement consists of the keyword if, followed by a condition enclosed in parentheses, followed by the statement to be executed if the condition is true. If the condition is false, the statement does not execute. Consider a simple example: You want a layer to be visible for the first 4 seconds of the composition and invisible after that. You can accomplish this with an Opacity expression:

myOpacity = 100;
if (time > 4) myOpacity = 0;
myOpacity

The key component of this expression is the if statement on the second line, which says that if time is greater than 4 seconds, change the value of the variable myOpacity to 0. Prior to the comp’s 4-second mark, the condition part of the if statement evaluates to false and myOpacity does not get changed from its initial value of 100. Watch out for a common rookie trap, however. You might think you could just set the value of the layer’s Opacity to 100 and use an expression such as

if (time > 4) 0

You can’t. This syntax indeed sets Opacity to 0 when time is greater than 4 seconds, but it generates an error at any value of time less than 4 seconds. The reason for this is that an expression always has to evaluate to some number (or array) suitable for the property to which it is applied. In the if (time > 4) 0 example, when time is less than 4 seconds the 0 doesn’t get executed, meaning no statement that evaluates to a number has been executed. After Effects doesn’t know what to do in this case and generates an error message.

This leads us to a second, more useful form of the conditional construct known as if/else. In this version, the keyword else is added after the statement that gets executed if the condition is true. Then else is followed by a statement to be executed if the condition is false. Here’s a simple, compact example that is equivalent to the first example:

if (time < 4) 100 else 0

This conditional construct has five pieces. First is the keyword if. Next, in parentheses, is the condition to be tested. The condition is followed by code that gets executed if the condition is true. Then comes the keyword else, followed by the code to be executed if the condition is false.

Note that either section of code that gets executed based on the conditional can consist of multiple statements. In such a case, you need to use curly brackets to denote those sections of code. A more verbose version of the Opacity example, with multiple statements in each section, might look like this:

if (time < 4){
  myOpacity = 100;
  myOpacity
}else{
  myOpacity = 0;
  myOpacity
}

You can use the curly brackets even if there is only one statement in the section, as in this version:

if (time < 4){
myOpacity = 100;
}else{
myOpacity = 0;
}
myOpacity

The exact formatting of the conditional (as far as where to place the curly brackets) is very flexible, but I prefer the format used here.

You can string if/else conditionals together for more complex applications. Let’s say that you want Opacity to be 100 for the first 4 seconds, 50 for the next 4 seconds, and 0 after that. You can do it like this:

if (time < 4){
myOpacity = 100;
}else if (time < 8){
myOpacity = 50;
}else{
myOpacity = 0;
}
myOpacity

You can also nest conditionals (if/else statements within if/else statements).

Comparison Operators

JavaScript provides a nice set of comparison operators available for your conditional code, including

• Less than (<)

• Greater than (>)

• Less than or equal to (<=)

• Greater than or equal to (>=)

• Equality (==)

• Inequality (!=)

It’s worthwhile to point out that the equality operator (==) trips up a lot of beginners, and even experienced coders fall into the trap occasionally. It’s easy to mistakenly write

if (a = b)

when what you intended is

if (a == b)

Unfortunately, the first example does not generate an error. JavaScript just sets the value of variable a equal to the value of variable b and, unless the result happens to be 0, the expression proceeds as if the result of what you thought was going to be a comparison were true. These errors can be tricky to find later, so watch out for them when writing code.

Logical Operators

JavaScript’s logical operators are useful in creating more complex conditional constructs. They are

• And (&&)

• Or (||)

• Not (!)

Sometimes you need to combine more than one condition. For example, you might want to test for the condition where either variable a is true or variable b is true:

if (a || b)

To test for the condition where both variables are true, write

if (a && b)

A test for the condition where variable a is true but b is false looks like this:

if (a && !b)

Returning to a real-world example, say you want an Opacity expression that is 100 between times of 2 seconds and 4 seconds but is 0 otherwise. You can write it like this:

if ( (time > 2) && (time < 4)) 100 else 0

In English, from left to right, this expression says “if the comp time is greater than 2 seconds and the comp time is less than 4 seconds, the result is 100; otherwise the result is 0.”

Loops

Often, you need an expression to do the same thing multiple times. Rather than forcing you to create multiple copies of the code, JavaScript provides some looping mechanisms that enable you to execute the same section of code over and over, until some condition is met. The most common of these mechanisms is the for loop. The easiest way to understand how it works is through a simple example. Suppose that you have an expression that needs to know the x coordinate of the right-most layer in the composition. You could set up an expression to loop through all the layers in the comp:

xMax = 0;
for (i = 1; i <= thisComp.numLayers; i++){
  if (thisComp.layer(i).transform.position[0] > xMax)
{
   xMax = thisComp.layer(i).transform.position[0];
  }
}

As you can see, the for statement has three controlling parameters, separated by semicolons. The first parameter defines a controlling variable and sets its initial value. The second parameter is the conditional test—as long as this condition is true, the loop continues to execute. As long as the loop variable i is less than or equal to the total number of layers in the comp, the example keeps looping. The last parameter increments the control variable.

The example increments the control variable by one each time through the loop, but you can increment (or decrement if you need your control variable to go from high to low) by any amount you want. The net result of this expression is that the statements between the opening and closing curly brackets of the for statement are executed once for each layer in the composition. When the loop finally exits, the variable xMax contains the x coordinate of the right-most layer in the composition (or 0 if no layers are positioned to the right of [0, 0]).

JavaScript provides a couple of useful tools for loop control. One is continue, which causes a jump to the end of the loop (from where the loop then continues). You might use this in the previous example if you wanted the loop to skip the layer containing the expression. In that case, you could modify the expression to

xMax = 0;
for (i = 1; i <= thisComp.numLayers; i++){
  if (i == index) continue;
  if (thisComp.layer(i).transform.position[0] > xMax)
{
   xMax = thisComp.layer(i).transform.position[0];
  }
}

The new conditional statement causes the loop to jump to the end if the control variable, i, is equal to the layer index of the layer containing the expression. This might be useful if, for example, the layer with the expression was a text layer that you were using to display the result. In that case, you probably wouldn’t want the expression to include the text layer in its search.

The other useful control is the break statement. You use break to exit a loop immediately. This is handy for loops where you’re looking for something specific, and there’s no point in continuing to loop once you find it. For example, perhaps you want to know the layer index of the first layer in the layer stack that has a name beginning with the letter “X”. The expression would look like this:

myIndex = 0;
for (i = 1; i <= thisComp.numLayers; i++){
  if (thisComp.layer(i).name[0] == "X"){
    myIndex = i;
    break;
  }
}

As soon as the loop encounters a layer name beginning with “X” it will set the variable myIndex to that layer’s index and exit the loop. If no such layer is found, myIndex will remain 0.

Before you move on from looping, you should get acquainted with one more JavaScript looping mechanism: the while loop. A while loop doesn’t do as much of the work for you as a for loop does. A while loop just tests a condition at the top of the loop and if the condition is true, the loop is executed. To avoid an infinite loop, it’s up to you to make sure that something eventually happens to make the condition false.

You could rewrite the layer index example using while like this:

myIndex = 0;
i = 0;
while (i <= thisComp.numLayers){
  if (thisComp.layer(i).name[0] == "X"){
    myIndex = i;
    break;
  }
  i++;
}

As you can see, the for loop presents a more compact notation for accomplishing the same thing, but you should be aware that there are situations where it makes more sense to use while.

The Math Object

JavaScript provides a very handy tool to assist you with any complex math you need for your expressions. The Math object comes with a number of built-in methods, some of which you will use much more often than others. A few you will find especially useful are

Math.PI

Math.floor()

Math.round()

Math.min()

Math.max()

Math.sin()

Math.abs()

Math.cos()

Math.exp()

Math.log()

Math.acos()

Math.atan2()

The following sections take a closer look at each method.

Math.PI

Math.PI provides a convenient way to access the constant pi (3.14159...), which you need frequently when working with circular geometry and trigonometric functions such as sine and cosine.

Math.floor()

Math.floor() accepts one parameter and rounds it down to the nearest whole number. One simple application would be to calculate the current second from the time object:

Math.floor(time)

This expression produces a value of 0 for times between 0 and 1 second, a value of 1 for times between 1 and 2 seconds, and so on.

Another common use is to calculate which group a layer is in. For example, say you are going to distribute layers in an array of five columns and you want to calculate which row a particular layer is in based on its index. You could do it like this:

numCols = 5;
myRow = Math.floor((index – 1)/numCols)

For layers 1 through 5, myRow will be 0. For layers 6 through 10, myRow will be 1.

Math.round()

Math.round() accepts one parameter and rounds it (up or down) to the nearest whole number. Here’s an example that shows one way to calculate the current frame:

Math.round(time/thisComp.frameDuration);

Math.min()

Pass Math.min() two parameters, and it returns the smaller of the two. You might have an Opacity expression in which you want the Opacity to increase from 0% to 50% over the first 5 seconds of the comp and then to hold at 50%. You can do it like this:

myOpacity = 10 * Math.min(time, 5);

Math.max()

Pass Math.max() two parameters, and it returns the larger of the two. Suppose you have a calculation in which you want to hold the value at 0 until you get to the layer’s In point and then you want the value to be the time that has elapsed since the In point. You can do it with Math.max():

myTime = Math.max(0, time – inPoint);

Math.sin()

Math.sin() is useful for creating oscillating values. Its single parameter is an angle specified in radians, and it returns the sine value for that angle. If you vary the angle with time, the output is an oscillating sine wave. The output values will range from –1 to +1. This example creates a value that oscillates between –50 and +50 every 2 seconds.

freq = 2; // frequency
amp = 50; // amplitude
amp * Math.sin(freq*time*2*Math.PI)

If you apply this expression to a layer whose Opacity is set to 50%, the layer flashes (the Opacity oscillates between 0% and 100%) once a second.

Math.sin() is also helpful with certain trigonometry calculations.

Math.abs()

Math.abs() accepts one parameter and returns its absolute value. Parameters with a value of 0 or greater are returned unchanged. Parameters with values less than 0 are converted to positive numbers. For example, the absolute value of –10 would be 10.

Math.cos()

Math.cos() works just like Math.sin() except that the output value of Math.cos() is +1 when the angle parameter is 0. With Math.sin(), the output value is 0 when the angle parameter is 0. The result is that Math.cos() and Math.sin() both generate the same waveform, but they’re 90 degrees out of phase with each other.

Math.exp()

Math.exp() takes one parameter and returns the mathematical constant e raised to the power of the parameter. That sounds complicated, but it just means that you can use Math.exp() to generate exponential curves. You might want to do this to generate a natural looking decay. Here’s the pulsing Opacity expression you used with Math.sin() modified so that the pulses decay toward 0:

freq = 2; // frequency
amp = 50; // amplitude
decay = 1.5; // decay
amp * Math.sin(freq*time*2*Math.PI) / Math.exp(decay * time)

The variable decay determines how fast the pulses decay to 0.

Math.log()

Give Math.log() a number, and it returns the natural logarithm of that number. You probably won’t often need the natural logarithm of a number, but you can use it to calculate the base 10 logarithm, which is useful when working with audio levels in decibels. Here’s how you get a base 10 logarithm using Math.log():

myVal = 3;
base10Log = Math.log(myVal)/Math.log(10);

Math.acos()

Pass Math.acos() one parameter, the cosine of an angle, and it returns the arc cosine of that number, which will give you the angle in radians. This is pretty much the inverse of Math.cos(). You would use it when you know the cosine of an angle but want to know the angle itself.

Math.atan2()

Math.atan2() accepts two parameters and returns the arc tangent of the ratio of the first parameter divided by the second parameter. The result is an angle (in radians). You typically use Math.atan2() to calculate an angle when you know a Y value (first parameter) and an X value (second parameter). You may also find these parameters (Y and X) referred to as the rise and the run or the opposite side and the adjacent side.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset