2016-08-12 20:47:46

你所不知道的AE技巧-脚本篇

AE的表达式并不能满足需求,当需要对DOM进行操作时,表达式就一点办法都没有了.对我个人而言,表达式与脚本的使用频率比例在5%:95%左右,我是把表达式当做胶水工具来用的.

对于进阶的AE使用者来说,学一些简单的脚本对你的帮助将非常大.基本上任何批量操作都可以由脚本实现,它在提高效率方面是最有用的.除此之外,有一大堆强大的特效,不用脚本根本没法做(特别是团神的一些教程)

当然,本文的脚本技巧对普通脚本开发者来说,用到的几率并不大

获得当前AE的路径

BridgeTalk.getAppPath("aftereffects-13.2")能够直接获得当前运行中的AE的文件路径,在很多方面都有用处

BridgeTalk对象是Adobe ESTK提供的全局对象,用来支持跨Adobe软件的通信功能,具体的源码在ESTK根目录中的src目录中

三引号

三引号类似与ES6标准的反引号, 不过没有模板的功能, 直接支持字符串换行, 经常用在界面的resource string这里

"""This is 1st line
This is 2nd line"""

命令行渲染

命令行渲染的流程中, 除了aerender.exe, /scripts/startup/commandLineRenderer.jsx也是必须存在的,千万不能删除.

装逼利器-隐藏的截图方法

app.activeViewer.views[0].saveBlittedImageToPng(comps.time,pngPath,1000,"what"s this? I don"t know");

如上方法可以对合成栏进行完整截图,可以截下3D图层的轮廓和灯光等不会被AE输出的导航信息

操作符重载

Adobe使用的JavaScript严格来说并不是JavaScript,它只是ECMA262的另一种实现.

一个典型的例子是,基于ES3的JSX支持操作符重载,而JavaScript的操作符重载还被扔在ES7那呢.

我在这里封装了一个操作符重载的函数. 注意,此函数第二个值rev是指是否是reversed.

注册操作符重载如下

var cout = new OperatorOverload(function (operand,rev){
            if(!rev)
                  $.writeln(operand);
            else
                  alert(operand);
      },"<<");

cout<<"这是输出到控制台的字符串";
"这是弹出窗口中的字符串"<<cout;

支持的操作符已在源码前几行列出

注册定时事件

app.scheduleTask (stringToCall, time, isLoop)是AE提供的最好的东西了,能够支持注册定时事件,虽然执行顺序非常非常迷.

选择循环执行时,函数返回一个id,可以通过app.cancelTask(id)取消这个事件

windowSpider

var win = Window.find(title,type);
if(win){
  for(var i in win) $.writeln(i)
}

可以通过以上脚本获取任一窗口型脚本的窗口,之后可以做很多事情:

  • 通过遍历或递归导出窗口的resource string
  • 在一个脚本中触发另一个脚本的任一事件来进行多脚本合作

windowSpider比较适合不需要响应式布局的界面的重构,需要注意的是,有些属性例如children,selection等千万不能爬取,否则会出错

在一个脚本中触发另一个脚本的任一事件

这里除了要用到上面的技巧之外,还要用另外一个模拟事件的方法:control.notify(event).

例如,control.notify("onClick")可以模拟点击,通过这种方法触发时,点击事件的默认参数event也可以拿到,这可比control.onClick()高到不知道哪里去了.

全局本地化函数

localize()提供全局本地化功能,例如

var hello = localize({
  en:"Hello world",
  ch:"你好"
})

hello // 英文版AE输出Hello world,中文版输出你好

确实提供了一些方便,但是它无法让用户切换语言了,完全跟着AE语言走,更好地办法还是自己管理语言的切换

模拟点击菜单

app.findMenuCommandId(str)输出菜单的字符串来获得菜单的id app.executeCommand(id)输入菜单的id来模拟点击

这个方法缺陷很大,原因在于它本身用来被模拟菜单,于是它就真的只能模拟菜单.

上面的意思是,你不能通过这个办法对不是当前合成的层来添加图层样式,因为理论上来说,非当前合成的层,连右键菜单都没有,更别提子菜单图层样式了,于是模拟点击就失败了

获取Error发生的行数

可以使用如下语句获得Error的详细信息,例如行数

try{

}catch(err){alert(err.line.toString()+"\r\n"+err.toString())}

获取当前行数

记录错误时, 一般都有保存记录行行数的需求, 这个不是Error类型内置的了, 而是全局辅助类$的属性 $.line.

$.line确实很方便, 在node中并没有类似的原生方法, 自己部署会麻烦一些

利用反射获取AE的对象模型

var ref = app.reflect;
var xml = ref.toXML();

得到的就是ESTK支持显示的对象模型XML了

移植脚本至AI

不涉及对象模型操作的脚本(例如各种工具栏),可以移植至AI等其他Adobe软件中,但是需要在脚本最上面加一句#targetengine main

原因同样在于命名空间, 在之前说过,不同的命名空间具有不同的全局对象访问权限.AE脚本默认能访问所有,AE表达式默认只能访问基础对象和$,而AI的默认命名空间不能访问到Panel,因此Panel型脚本会全部出错.

除此之外, PS的命名空间更加特殊,它与AE表达式类似,一旦语句运行完毕后,这个命名空间就被垃圾回收机制给刷掉了.因此,除了Dialog型窗口能够阻塞引擎来保证其运行成功之外,需要用其他的办法来定时唤醒脚本,给PS一种这个脚本还没有运行完毕的错觉

绑定的福利

Layer.getRenderGUIDe(time,false),CS6-CC2014限定

可以获得层的hash值(不包括transform属性和material属性).这个办法对绑定特别有用,因为不再需要额外的标志来识别绑定的控制层了,直接计算其hash值即可

隐藏的JSON序列化

app.objToJson(),CS6-CC2014限定

隐藏的对象转JSON方法,但是有缺陷, 最好还是直接拖道格拉斯原汁原味的JSON2源码

插值错误

脚本从AE里面获取关键帧插值时, 其中influence有可能超过0-100的规定值,一旦应用这个值,就会抛出Problem级别的错误,必须在应用前限定influence的值

图片内嵌

脚本中可以存放一些资源的二进制资源,图片内嵌是经常用到的.实现也很简单,读任意资源的时候,加一句file.encoding = "BINARY";,即可读取到可以被直接使用的字符串值.

之后给得到的字符串加上双引号,扔给一个变量,再把它当成一个File扔给图片控件即可

快速开发界面

string resource型开发脚本界面,并不是很便利.一个主要的问题在于调试,不符合格式的,根本就没有详细的错误信息.

更好地办法是写一个Object,最后将其字符串化,这样,格式错误时,ESTK会直接提示哪里有问题

我的朋友阿木亮和水果硬糖实现了这样的功能,并将其写入了一个类jQuery的ScriptUI插件,详细信息在[这里(https://github.com/amlorg/javascript/blob/master/lib/ae/UIParser.jsx)

真正的单击与双击事件共存

ScriptUI中的单击事件和双击事件实际上是不共存的,也就是说,双击时一定会先触发单击事件.

我们可以通过调用定时方法,来手动进行单双击共存.

首先定义一个计数变量,在首次点击时计数+1,并同时注册一个500毫秒的定时事件,此事件检查计数是否为1,是的话则调用单击事件,否的话不做处理.接在第二次点击时触发双击操作,并将计数清零

脚本创建伪插件

expression control就是伪插件,意思是没有任何功能,只被表达式拾取数值的插件.伪插件用处很大,主要是对表达式控制的分组十分强大,很省空间.

伪插件之前还要安装,但是最近一个叫FlexEffector的脚本直接逆向了ffx预设,不需要安装就可以直接应用

200

上面介绍了一个app.findMenuCommandId(str)方法, 它可以输出菜单的字符串来获得菜单的id, 并提供给另外一个方法来实现模拟菜单. 其实这里还有一个坑:

AE相邻版本同一个菜单字符串的id相差200

因此, 为了做到让模拟菜单兼容全部AE版本, 必须做修正: 先读取当前AE的菜单字符串id, 取其前两位, 再与默认的菜单id的后两位拼接即可

10200

AE有个功能我经常用, Edit=>Purge=>All Memory & Disk Cache清除缓存, 利用app.executeCommand(id)确实可以模拟点击它, 但是它的id却是app.findMenuCommandId(str)找不到的.

因为之前的所有id全部都是4位, 而Edit=>Purge=>All Memory & Disk Cache比较新, 它的id是5位数的10200, 因此原有方法无法找到

快捷键运行任意脚本

本文链接:https://smallpath.me/post/ae-skill-script

-- EOF --