В этом документе объясняются различия в синтаксисе языка выражений между движками выражений JavaScript и Legacy ExtendScript в After Effects 16.0.

Обращайтесь к этому документу, чтобы узнать, как улучшить выражения для движка выражений JavaScript, или при исправлении ошибок, которые возникают, когда выражения, написанные для предыдущих выпусков After Effects, не удается оценить в движке выражений JavaScript.

Язык выражений в After Effects основан на JavaScript, который представляет собой реализацию ECMAScript. Движок выражений JavaScript в After Effects 16.0 основан на ECMAScript 2018. Движок выражений Legacy ExtendScript основан на ECMAScript 3 (1999). (Adobe ExtendScript — это также язык, который используется для создания сценариев в After Effects и других приложениях Adobe.)

Вы можете следовать приведенным ниже примерам, а также рекомендациям по созданию выражений, которые работают в обоих движках выражений JavaScript и Legacy ExtendScript.

Основные различия между движками выражений JavaScript и Legacy ExtendScript:

Современный синтаксис JavaScript: улучшения, внесенные в язык выражений

В выражениях можно использовать синтаксис JavaScript из ECMAScript 2018

С момента выхода ECMAScript 3 в язык JavaScript было внесено много дополнений. Появились новые, более компактные и удобочитаемые методы для использования со строками, массивами и объектами. Также появились новые способы объявления переменных и функций, а также параметров по умолчанию, операторов spread и многого другого. Этот документ не охватывает такие изменения, так как это общие изменения языка JavaScript. Ресурсы для изучения многих синтаксических дополнений можно найти по следующим ссылкам:

Дополнительные рекомендуемые, углубленные ресурсы для изучения JavaScript:

Выражение .значение больше не требуется при ссылке на другие свойства из исходного текста

Для ссылки на другое значение свойства из свойства исходного текста, движок Legacy ExtendScript требует добавить выражение .значение в конце данного свойства. По умолчанию движок выражений JavaScript показывает значение свойства, если только не был явно использован еще один атрибут, такой как .propertyIndex или .name.

Например:

thisComp.layer("Solid 1").transform.position
 
// In the Legacy engine, this evaluates to "[object Property]" when applied to a Source Text property.
// In the JavaScript engine, this evaluates to the value of the Position property of layer "Solid 1" when applied to a Source Text property.

Заморозка значений свойств с помощью posterizeTime(0)

В After Effects 16.0 выражение posterizeTime(0) замораживает значение свойства в композиции в момент времени 0. Это относится к обоим движкам выражений JavaScript и Legacy ExtendScript.

Примечание.

Этот подход не является обратно совместимым и может привести к неожиданным результатам в версиях After Effects до 16.0.

Несовместимый устаревший синтаксис

Почти весь синтаксис выражений для движка выражений Legacy ExtendScript совместим с движком выражений JavaScript. Однако есть устаревший синтаксис, который несовместим с движком выражений JavaScript. Иногда это вызвано изменениями синтаксиса в современном варианте JavaScript. В других случаях устаревший синтаксис был удален. Ниже приведены примеры нерабочего и рабочего синтаксисов.

Большую часть этих различий в синтаксисе можно исправить, создавая сценарии приложений, которые переписывают выражения.

Отличается синтаксис if...else

В общем случае рекомендуется всегда писать операторы if...else с разрывами строк и скобками в соответствии с рекомендациями MDN. Движок Legacy ExtendScript был терпим к свободному синтаксису в операторах if...else, однако движок JavaScript требует строгого соблюдения синтаксиса. При использовании движка JavaScript выражения if...else с неверным синтаксисом не обрабатываются.

Не допускается завершать условный оператор без оператора else.

Если выражение завершается оператором if без оператора else, движок JavaScript не обрабатывает такое выражение, выдавая сообщение об ошибке «В выражении используется неопределенное значение (возможно, оно является подписью массива вне диапазона)». Движок Legacy ExtendScript вычисляет следующее выражение равным 100, если параметр time больше 1 секунды. В противном случае оно вычисляется равным 50:

var x = 50;
if ( time > 1 ) {
    x = 100
}
// The "else" here is implied but not stated.

Для движка JavaScript необходимо явно указать, является ли часть else условного оператора последним оператором в данном выражении:

var x = 50;
if ( time > 1 ) {
    x = 100;
} else {
    x;
}

Это выражение правильно обрабатывается и движком JavaScript, и движком Legacy ExtendScript.

if и else не могут находиться в одной строке без скобок

Оператор if...else в одной строке без скобок вычисляется движком Legacy ExtendScript, но движок JavaScript выдает сообщение об ошибке, такое как «Ошибка синтаксиса: неожиданный токен else» или «В выражении используется неопределенное значение (возможно, оно является подписью массива вне диапазона)». Сообщение об ошибке зависит контекста и типа свойства.

Движок Legacy ExtendScript вычисляет следующее выражение равным 100, если параметр time больше 1 секунды. В противном случае оно вычисляется равным 50:

if ( time > 1 ) 100 else 50;

В операторах if...else необходимо использовать разрывы строк и скобки, чтобы они обрабатывались движком JavaScript. Для простых случаев вместо этого можно использовать трехместный оператор. С движком JavaScript можно использовать любой вариант следующего синтаксиса:

// Solution A: adding a line break before "else" will allow both engines to evaluate correctly.
 
if ( time > 1 ) 100
else 50;
 
// Solution B: adding correct bracketing will also allow both engines to evaluate correctly.
 
if ( time > 1 ) { 100 } else { 50 };
 
// Solution C: Use a ternary operator in place of the if...else statement, which also evaluates correctly in both engines.
 
time > 1 ? 100 : 50;

Все указанные выше решения правильно обрабатывается и движком JavaScript, и движком Legacy ExtendScript.

Выражения не могут заканчиваться объявлением функции

Если выражение заканчивается объявлением функции, движок JavaScript не вычисляет такое выражение, а выдает сообщение об ошибке «Вместо объекта типа «Число», «Массив» или «Свойство» обнаружен объект другого типа». В случае движка JavaScript последний вычисляемый элемент должен возвращать значение, а не объявлять его.

Следующий пример работает для движка Legacy, но не для движка JavaScript:

timesTen( value );  // The Legacy engine evaluates this line, even though the function is declared below.
 
function timesTen ( val ) {
    return val * 10
}

Если в последней строке вызывается функция (вместо объявления), такое выражение правильно обрабатывается обоими движками:

function timesTen ( val ) {
    return val * 10
}
 
timesTen( value );  // The JavaScript engine needs the function call to happen below the declaration in order to return the correct value.

Сокращенный синтаксис this() не допускается, вместо него используйте thisLayer()

В движке Legacy ExtendScript было разрешено использовать this в качестве сокращения thisLayer. В движке JavaScript форма this ссылается на глобальный объект и вместо нее необходимо использовать thisLayer. Использование this в движке JavaScript обычно приводит к сообщению об ошибке «это не функция».

В следующем примере для движка Legacy ExtendScript формат  this используется для создания сокращенной ссылки на свойство «Положение текстового слоя» из свойства «Исходный текст»:

this(5)(2).value;

В движке JavaScript форму this необходимо заменить на thisLayer:

thisLayer(5)(2).value;

Использование thisLayer совместимо с обоими движками выражений.

Свойству исходного текста для доступа к символам из индекса-массива требуется выражение .значение

В случае движка выражений Legacy ExtendScript для доступа к символам текстового свойства можно использовать скобки, как для массива:

text.sourceText[0]      // Returns the first character of the Source Text property's text value.

В случае движка JavaScript для доступа к символам необходимо добавить выражение .значение:

text.sourceText.value[0]    // Returns the first character of the Source Text property's text value.

Этот синтаксис совместим с обоими движками.

Свойства и методы Snake case не допускаются

Устаревшие свойства и методы Snake Case (запись с подчеркиванием вместо camelCase) не поддерживаются механизмом JavaScript. Вместо них следует использовать версии camelCase поскольку они совместимы с обоими движками. Ниже приведен список устаревших выражений Snake Case и соответствующие им выражения camelCase.

Свойства Snake Case Свойства camelCase Методы Snake Case Методы camelCase

this_comp

this_layer

this_property

color_depth

has_parent

in_point

out_point

start_time

has_video

has_audio

audio_active

anchor_point

audio_levels

time_remap

casts_shadows

light_transmission

accepts_shadows

accepts_lights

frame_duration

shutter_angle

shutter_phase

num_layers

pixel_aspect

point_of_interest

depth_of_field

focus_distance

blur_level

cone_angle

cone_feather

shadow_darkness

shadow_diffusion

active_camera

thisComp

thisLayer

thisProperty

colorDepth

hasParent

inPoint

outPoint

startTime

hasVideo

hasAudio

audioActive

anchorPoint

audioLevels

timeRemap

castsShadows

lightTransmission

acceptsShadows

acceptsLights

frameDuration

shutterAngle

shutterPhase

numLayers

pixelAspect

pointOfInterest

depthOfField

focusDistance

blurLevel

coneAngle

coneFeather

shadowDarkness

shadowDiffusion

activeCamera

value_at_time()

velocity_at_time()

speed_at_time()

nearest_key()

posterize_time()

look_at()

seed_random()

gauss_random()

ease_in()

ease_out()

rgb_to_hsl()

hsl_to_rgb()

degrees_to_radians()

radians_to_degrees()

from_comp_to_surface()

to_comp_vec()

from_comp_vec()

to_world_vec()

from_world_vec()

to_comp()

from_comp()

to_world()

from_world()

temporal_wiggle()

loop_in_duration()

loop_out_duration()

loop_in()

loop_out()

valueAtTime()

velocityAtTime()

speedAtTime()

nearestKey()

posterizeTime()

lookAt()

seedRandom()

gaussRandom()

easeIn()

easeOut()

rgbToHsl()

hslToRgb()

degreesToRadians()

radiansToDegrees()

fromCompToSurface()

toCompVec()

fromCompVec()

toWorldVec()

fromWorldVec()

toComp()

fromComp()

toWorld()

fromWorld()

temporalWiggle()

loopInDuration()

loopOutDuration()

loopIn()

loopOut()

Использование eval() с выражениями в двоичной кодировке (.jsxbin)

Выражения, закодированные в двоичном формате ExtendScript (сохраненные как двоичный файл .jsxbin из ExtendScript ToolKit CC), не поддерживаются движком JavaScript.

Чтобы запутать выражение, используйте движок Legacy ExtendScript или другой метод запутывания, совместимый с ECMAScript 2018. Некоторые методы запутывания могут не поддерживаться обоими движками выражений.

Ограниченная поддержка объекта $. (доллар)

Для объекта $. (доллар) методы и свойства характерны для ExtendScript и в основном не поддерживаются движком JavaScript. В этой таблице перечислены неподдерживаемые и поддерживаемые виды использования объекта $. (доллар):

Не поддерживается $. Поддерживаемые объекты $.

$.fileName

$.hiResTimes

$.stack

$.evalFile()

$.list()

$.setenv()

$.getenv()

$.appEncoding

$.buildDate

$.decimalPoint

$.dictionary

$.error

$.flags

$.includePath

$.level

$.line

$.locale

$.localize

$.memCache

$.os

$.screens

$.strict

$.version

$.build

$.engineName (не поддерживается движком Legacy ExtendScript)

$.global

Не поддерживаются выражения ...reflect.properties, ...reflect.methods и toSource()

Выражения reflect.properties и reflect.methods не поддерживаются движком JavaScript. Эти методы характерны для ExtendScript и у них нет прямого эквивалента в JavaScript.

Метод toSource() в JavaScript устарел и не является частью какого-либо стандарта.

Чтобы просмотреть список доступных свойств и методов для любого заданного свойства After Effects, аналогичного тому, которое было предоставлено вышеупомянутыми методами, используйте следующее выражение для свойства «Исходный текст» и свяжите его с нужным свойством, например, используя инструмент «Лассо» вместо thisProperty в строке 1:

let obj = thisProperty;     // Replace "thisProperty" with a property-link to your desired property.
let props = [];
 
do {
    Object.getOwnPropertyNames(obj).forEach(prop => {
        if (props.indexOf(prop) === -1) {
            props.push(prop);
        }
    });
} while (obj = Object.getPrototypeOf(obj));
 
props.join("\n");           // Returns an array of strings listing the properties and methods available.

Приведенное выше выражение не совместимо с движком Legacy ExtendScript. В нем используются синтаксис и методы, недоступные в ECMAScript 3.

Требования к синтаксису для библиотек выражений .jsx и метода eval() с движком JavaScript

Когда выражения используются внутри библиотеки выражений .jsx или выражение вызывается внутри метода eval(), необходимо изменить определенный синтаксис:

Явный префикс thisLayer. или thisProperty. должен быть добавлен к любому собственному методу или атрибуту, который явно не вызывается для слоя или свойства. Такой префикс сообщает движку JavaScript, для какого объекта вызывается метод или атрибут.

Математические операции со значениями массива, такие как Position, необходимо вычислять, используя векторные математические функции или циклические функции, чтобы действовать на каждый элемент массива. Перегруженные математические операторы, такие как position + [100,100], не будут обрабатываться.

При использовании движка JavaScript выражения предварительно обрабатываются перед вычислением, чтобы сделать некоторые выражения с синтаксисом для Legacy ExtendScript читаемыми новым движком. Однако такие задачи предварительной обработки не выполняются при вычислении выражений из библиотеки функций выражений .jsx или вызове выражений внутри метода eval(). Указанные выше изменения синтаксиса в таких случаях необходимо вносить вручную. Все эти изменения синтаксиса обратно совместимы с устаревшим движком ExtendScript, поэтому выражение .jsx записанное так, чтобы работать с движком JavaScript, также будет работать с движком Legacy ExtendScript.

Совет по производительности: из-за отсутствия предварительной обработки в случае вызова сложных выражений из библиотеки .jsx с таким синтаксисом и движком JavaScript возможно повышение производительности по сравнению с вызовом такого же выражения непосредственно в свойстве.

Собственные методы с явным префиксом и атрибуты с thisLayer. или thisProperty.

В таблице ниже перечислены методы и атрибуты, для которых требуется префикс. Например, атрибут time необходимо записать в форме thisLayer.time, тогда как метод wiggle() — в форме thisProperty.wiggle()

Эти префиксы требуются, только если атрибут или метод не вызываются явно в другом слое или свойстве. Например, при вызове thisComp.layer(1).hasParent, добавляется thisLayer. не требуется, так как .hasParent уже явно вызывается в layer(1).

Методы, требующие thisLayer. Атрибуты, требующие thisLayer. Методы, требующие thisProperty. Атрибуты, требующие thisProperty.
comp()
footage()
posterizeTime()
add()
sub()
mul()
div()
clamp()
length()
dot()
normalize()
cross()
lookAt()
timeToFrames()
framesToTime()
timeToTimecode()
timeToFeetAndFrames()
timeToNTSCTimecode()
timeToCurrentFormat()
seedRandom()
random()
gaussRandom()
noise()
degreesToRadians()
radiansToDegrees()
linear()
ease()
easeIn()
easeOut()
rgbToHsl()
hslToRgb()
hexToRgb()
mask()
sourceRectAtTime()
sourceTime()
sampleImage()
toComp()
fromComp()
toWorld()
fromWorld()
toCompVec()
fromCompVec()
toWorldVec()
fromWorldVec()
fromCompToSurface()
time
source
thisProject
colorDepth
transform
anchorPoint
position
scale
rotation
opacity
orientation
rotationX
rotationY
rotationZ
lightTransmission
castsShadows
acceptsShadows
acceptsLights
ambient
diffuse
specular
specularIntensity
shininess
specularShininess
metal
audioLevels
timeRemap
marker
name
width
height
index
parent
hasParent
inPoint
outPoint
startTime
hasVideo
hasAudio
active
enabled
audioActive
cameraOption
pointOfInterest
zoom
depthOfField
focusDistance
aperature
blurLevel
irisShape
irisRotation
irisRoundness
irisAspectRatio
irisDiffractionFringe
highlightGain
highlightThreshold
highlightSaturation
lightOption
intensity
color
coneAngle
coneFeather
shadowDarkness
shadowDiffusion
valueAtTime()
velocityAtTime()
speedAtTime()
wiggle()
temporalWiggle()
smooth()
loopIn()
loopOut()
loopInDuration()
loopOutDuration()
key()
nearestKey()
propertyGroup()
points()
inTangents()
outTangents()
isClosed()
pointsOnPath()
tangentOnPath()
normalOnPath()
createPath()

velocity
speed
numKeys
propertyIndex

Замена математических операторов векторными математическими функциями

Оба движка JavaScript и LegacyExtendScript разрешают перегрузку математических операторов для массивов при использовании такого синтаксиса, как position + [100,100], однако это не работает для выражений из библиотеки функций выражений .jsx или внутри метода eval().

Чтобы применить математические функции к свойствам массива, таким как Position, Scale и т. д., для сложения, вычитания, умножения и деления следует использовать эквиваленты векторных математических функций. Векторные математические функции также будут работать для обычных чисел, поэтому функция, которая может быть вызвана для свойств данных любого типа, должна использовать векторные математические функции.

Примечание.

Префикс thisLayer. необходимо использовать с векторными математическими функциями.

  • Сложение: thisLayer.add(vec1, vec2)
  • Вычитание: thisLayer.sub(vec1, vec2)
  • Умножение: thisLayer.mul(vec, amount)
  • Деление: thisLayer.div(vec, amount)

Ниже приведены примеры выражений с использованием стандартных математических функций и обновленных векторных математических функций. В векторных математических выражениях также используется соответствующий префикс thisLayer. или thisProperty., когда необходимо.

Чтобы найти разницу между wiggle() и значением свойства Position:

// Standard Math:
wiggle() - value;
 
 
// Vector Math:
thisLayer.sub( thisProperty.wiggle(), value );

Для интерполяции между двумя значениями, подобно linear(), но с расширенным диапазоном за пределами заданного минимума и максимума:

// Standard Math:
value1 + ( ( t - tMin ) / ( tMax - tMin ) ) * ( value2 - value1 );
 
 
// Vector Math:
thisLayer.add( value1, thisLayer.mul( thisLayer.div( thisLayer.sub( t, tMin ), thisLayer.sub( tMax, tMin ) ), thisLayer.sub( value2, value1 ) ) );

Циклический ввод и вывод свойства Position:

// Standard Math:
loopIn( "cycle" ) + loopOut( "cycle" ) - value;
 
 
// Vector Math:
thisLayer.sub( thisLayer.add( thisProperty.loopIn( "cycle" ), thisProperty.loopOut( "cycle" ) ), value );

Эта работа лицензируется в соответствии с лицензией Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported  На посты, размещаемые в Twitter™ и Facebook, условия Creative Commons не распространяются.

Правовые уведомления   |   Политика конфиденциальности в сети Интернет