Expression
AfterEffectsのエクスプレッションについて
新旧どちらのエンジンを使用しているか確認する
AE2019 (version 16) から、エクスプレッションで使用するエンジンが新しくなった。新しくなったといっても古い方も選択できるようにはなっているので、切り替え可能。ユーザーが任意で選択出来るので、どちらが選択されているかによって、同じエクスプレッション内容でもエラーになる可能性がある。
エラーを回避するにはどちらが使用されているかを判断して、分岐させるなどして書き分ける必要がある。
$オブジェクトで使用できるものがかなり減ったのでそれへのアクセスの可否で判断できる。
After Effects User Guide | $.(ドル)オブジェクトの制限付きサポート
なんでもいいのだが、仮に$.version を使用するとして、アクセスした場合の結果は、
// 以前のExtendScript
$.version //4.5.6(文字列)
// Javascript
$.version // undefined
となるので、結果をBooleanに変換すれば判断できる。
// JavascriptエンジンのときTRUE、ExtendScriptエンジンのときFALSEのフラグにしたければ
var usingJSEngine = Boolean($.version);
// JavascriptエンジンのときFALSE、ExtendScriptエンジンのときTRUEのフラグにしたければ
var usingESEngine = !Boolean($.version);
てな感じにすればいい。簡単に書きたければ
var usingESEngine = !!$.version;
でよい。使う$オブジェクトは何でもいいが、ExtendScriptエンジンで使ったときの結果が、数値の0とか長さ0の文字列などの、型変換でFALSEになるものを使用すると正しい判定が出来ないので注意。まぁ$.versionでいいと思う。公式もそうやってるし。
エクスプレッションのエラーメッセージを好きな文章にする
try~catchでエラーを拾った後、catch内で
$.error = "\"カメラ 1\" を 1 ノードカメラにすることはできません。";
みたいに書くと、エラーの文章が変わる。ただし、新しいJavascriptエンジンを使用する場合、$.errorは使用できなくなっている。
After Effects User Guide | $.(ドル)オブジェクトの制限付きサポート
その場合、throwでエラー文を投げればいい。
throw "\"カメラ 1\" を 1 ノードカメラにすることはできません";
どっちにも対応するには使用しているエンジンを確認して分岐する。
try {
targetLayer.position[2];
} catch (err) {
if (usingLegacyEngine) {
$.error = "3Dレイヤーではない";
} else {
throw "3Dレイヤーではない";
}
}
エッセンシャルプロパティでタイムリマップの値を拾う時なんかおかしい
「ソーステキスト」をエッセンシャルプロパティにして、そのエッセンシャルプロパティにエクスプレッションを書いてタイムリマップを参照したとき、思った値が返ってこないことがある。
timeRemap
ではなく、
time
で拾うとうまくいく。エッセンシャルプロパティなので外から操作してるけど、コンポの中で操作している...という感覚を持て、ということか?
しかし、スライダーなどをエッセンシャルプロパティにしてタイムリマップを参照した時はなんら問題ない。ソーステキストのときだけ。よくわからん。
l = [対象のレイヤー];
l.timeRemap + l.StartTime
としたらなんかうまく拾えた。
時間の表記が正しくない
エクスプレッションのエンジンをJavascriptエンジンにして、テキストレイヤーのソーステキストに、
new Date()
というエクスプレッションを書く。すると通常、
Tue Nov 16 2021 15:23:23 GMT+0900 (日本標準時)
といった表示になる。
ところが、AE2021で同じエクスプレッションを書くと、
Mon Nov 15 2021 21:23:23 GMT-0900 (GMT-09:00)
と表示される。AE2021、AE2022で確認済み。
曜日も日付も時間も違うし...めちゃくちゃやないか!とお思いの人は、まぁ待ちなされ。コンピュータは「曜日、月、日、年、時、分、秒」をそれぞれのデータを持っているわけではないのですよ。捉え方としては「期待している値よりも18時間(540分)遅い」と見るのが正解。
「18」から連想出来る数字は?日本の標準時が世界標準時から「+9時間」を連想しますね。なんかその辺が怪しいですね。GMTの後ろも+と-で違ってますしね。
タイムゾーンオフセット値を取得できるメソッド、
[Date].getTimezoneOffset()
を実行すると、AE2020は -540 、AE2021は 540 が返ってくる。やはりタイムゾーンのプラスマイナスが逆転しておるようじゃ。
日本は協定世界時から9時間進んでいる(+09:00)なので、-540 が返ってくるのが正しい。(+09:00なのにオフセットはマイナスなのちょっとややこしい)
getTimezoneOffsetの詳細は以下ページにて。
Date.prototype.getTimezoneOffset() - JavaScript | MDN
で、実際運用上で日付オブジェクト使いたいところって、映像内にレンダリング開始時間を表示したいみたいな「文字列」として扱いたい場合と、日付や時間を「数値」で取得してそれを元に何らか後々の計算に使用したい場合ってところかしら?後者はほぼ無いと思うけど。
「数値」で欲しい場合は、「GMT~」の部分の表示がおかしいのは忘れて、日付と「時」部分だけ調整できれば良いでしょう。なので作成したDateオブジェクトから協定世界時(UTC)で時間を取得して9時間足せばいいでしょう。(日本の場合)
d = new Date(); //Dateオブジェクトを作成して
d.setHours(d.getUTCHours() + 9); // UTCから9時間進める
h = d.getHours(); // 「時」を数値で取得
2行目の d.getUTCHours() + 9 を直接使用せずに1回 setHours() してるのは、もし9時間を足した事によって24時を超えた場合、「26」とか「30」みたいな数字を取得するのではなくて、日付を繰り上げ、時間としては「2」を取得したいから。日付オブジェクトのまま扱うことで日付の繰り上げとかは、やってくれるから助かる。
一方、テキストレイヤーに表示するなど「文字列」で欲しい場合は、
d = new Date();
d.setHours(d.getUTCHours() + 9);
d.toLocaleString();
とすれば、結果は
2021/11/16 19:52:10
のように、日本で馴染みのあるフォーマットで表記される(使用している地域で表記が変わる)。
まぁこれでもいいけど、toLocaleString の引数でタイムゾーンを指定できるのを活かして、
d = new Date();
d.toLocaleString('ja-JP', {timeZone: 'Asia/tokyo'});
こうする。結果は先のものと同じ。これのほうが式としては簡潔であろう。
最後に、toLocaleString() の紹介。普段使わないからよく知らなかったけど、改めて調べてみるとめちゃくちゃ有能なやつだったことがわかった。ちょっと紹介して終わる。
Dateオブジェクトで使える、toLocaleString() は、日時データを人間が読める文字として表現することが出来るもの。
Date.prototype.toLocaleString() - JavaScript | MDN
上記リンク先に載ってる例のまま、toLocaleString() の引数に値を入れてみる。
d = new Date();
d.toLocaleString('ja-JP-u-ca-japanese');
これの結果は
R3/11/16 2:46:16
のようになる。Rって令和のRやろ...。日本の元号も対応してるんかい!!って感動しました。
さらに、第2引数に option を設定できて、表示方法を詳細に決めることが出来る。
以下に例として toLocaleString() とその結果だけ並べていく。(d = new Date(); は省略している。> で始まる行はテキストレイヤーに表示される文字)
d.toLocaleString('ja-JP-u-ca-japanese');
> R3/11/16 2:46:16
d.toLocaleString('ja-JP',{timeZone: 'Asia/tokyo', calendar: 'japanese', dateStyle: 'full', timeStyle: 'full'})
> 令和3年11月16日火曜日 21時20分51秒 日本標準時
d.toLocaleString('ja-JP',{timeZone: 'Asia/Tokyo', calendar: 'japanese', dateStyle: 'long', timeStyle: 'short', hour12: true})
> 令和3年11月16日 午後9:27
d.toLocaleString('ja-JP',{era: 'long', year: 'numeric', month: '2-digit', day: '2-digit', weekday: "short", hour: '2-digit', minute: '2-digit'})
> 西暦2021年11月16日(火) 03:36
ほんの一例だが、同じDateオブジェクトをこんな感じで表現の仕方を多様に設定できる。時間を2桁表示するために自分で0埋めしなくていいのかよ!曜日も自在かよ!と思いました。
他、option に設定できる内容は以下のページ参照。
Intl.DateTimeFormat() コンストラクター - JavaScript | MDN
一見難しいけど、上に書いた例を参考に書いてみたら出来ると思う。