AE 脚本进阶:生成大量粒子扩散
AE 脚本进阶:生成大量粒子扩散
Utilities?
正文前面先说个技巧。脚本如何模块化?在前两个案例中,我们写了一些辅助函数(如清空子对象、随机数)。那么,如何在多个脚本中复用呢?
在早期的 js 中,没有模块的概念。事实上,如果有为 html 写 js 的经历,就会发现,几个 js 文件根本不需要设置依赖关系就能互相调用。这个也可以理解为,把所有代码 include 到了全局代码块。
但这个特性在 Adobe ExtendScript(AES)并不适用。直接把公共函数写在同目录其他文件中,符号并不会注入到全局命名空间被找到。AES 提供了一个模块化的方法:
#include "utils.js";
是的,你没看错,这是 js 不是 c……
这种语法就带来了一个问题,会让 prettier 之类的插件需要用的 parser 宕机。
需要注意,AE 执行脚本是有后效性的!一个符号只要通过运行加入了符号表,那么它会在生命周期里一直存在,即使从脚本代码中删掉。
Shape! Shape!
我们需要绘制大量的圆,而圆需要由 Shape 来承载。我们来看看 Shape 的描述:
可以由如下代码创建一个空图形对象。
comp.layers.addShape();
World of Properties
神说,要有圆。
下面我们研究属性。图层是由属性树(Property Tree)描述的,变换(transform)中的位置、旋转都是属性。我们来看看这个圆有哪些属性。
但是得把这个对应到代码上。我们来看看它的属性树长什么样。layer 本身就是一个 PropertyGroup,可以直接获取它的属性。但是我们不知道树的结构!(这个树跟 GUI 上看到的未必一样)
但是,好在我们可以遍历 group 的所有属性,于是有:
function debugProperties(obj) {
for (var i = 1; i <= obj.numProperties; i++) {
var prop = obj.property(i);
prop; // Break here
}
}
获取完 property 后可以断点查看它的具体数据,尤其是 name
和 matchNamed
域,这有助于我们直接获取该 property。简单测试一下可以得到“内容”的标识名为 ADBE Root Vectors Group
。
根据试探,我们可以代码描述出这样的层次……
function test() {
var layer = app.project.activeItem.layer(1);
var vectors = layer.property("ADBE Root Vectors Group");
var eclipse = vectors.property(1); // type: ADBE Vector Group; Name: 椭圆 1
var transform = eclipse.property("ADBE Vector Transform Group"); // 变换
debugProperties(transform);
var content = eclipse.property("ADBE Vectors Group"); // 内容
var path = content.property("ADBE Vector Shape - Ellipse"); // 椭圆路径 1
}
但是这样依然不够直观,太累了,不能一下子找到想要的属性。可以写个函数,把所有属性的名称和值转字符串。
function reprProperties(obj) {
var res = "";
for (var i = 1; i <= obj.numProperties; i++) {
var prop = obj.property(i);
res += prop.matchName + ": " + prop.name + " = " + prop.value.toString() + "\n";
}
return res;
}
比如椭圆形状的变换属性为:
ADBE Vector Anchor: 锚点 = 0,0
ADBE Vector Position: 位置 = -104.599982776641,14.4000132179264
ADBE Vector Scale: 比例 = 26,26
ADBE Vector Skew: 倾斜 = 0
ADBE Vector Skew Axis: 倾斜轴 = 0
ADBE Vector Rotation: 旋转 = 0
ADBE Vector Group Opacity: 不透明度 = 100
AES 不支持在控制台输出,但在 VSCode 调试时的 VARIABLES 区域可以复制该字符串。
这样还不够!能否一步到位生成树?
function reprPropertyTree(obj) {
var res = "";
function impl(pg, indent) {
for (var i = 0; i < indent; i++) res += " ";
var val =
pg.propertyValueType == PropertyValueType.NO_VALUE || !pg.value
? ""
: " = <" + pg.value.__proto__.constructor.name + "> " + pg.value.toString();
res += pg.matchName + " [" + pg.name + "]: " + pg.constructor.name + val + "\n";
for (var i = 1; i <= pg.numProperties; i++) {
var prop = pg.property(i);
impl(prop, indent + 1);
}
}
impl(obj, 0);
return res;
}
这里有几点需要说明。
- 判断是否有值,如果有值则输出值。值可以用
prop.value
获取,但如果没值却获取了,会 throw,并且提示你需要先用PropertyValueType.NO_VALUE
来判断是否有值。有值的情况下也可能是空值。 - 获取属性值的值和类型。在 ES3 没有
Object.getPrototypeOf
。而它可能是值类型,没有 constructor。但是可以用value.__proto__
来获取原型。于是有:pg.value.__proto__.constructor.name
。 - 获取属性的类型。这一定是 Object 类型,所以可以直接获取它的
constructor.name
。其实没啥必要,毕竟叶结点都是Property
,非叶结点和根结点都是PropertyGroup
。
对于这样的一个圆,可能有以下属性(大小和位置后来更改了,与图中不一样):
ADBE Vector Layer [形状图层 1]: ShapeLayer
ADBE Marker [标记]: Property = <Object> [object Object]
ADBE Root Vectors Group [内容]: PropertyGroup
ADBE Vector Group [椭圆 1]: PropertyGroup
ADBE Vector Blend Mode [混合模式]: Property = <Number> 1
ADBE Vectors Group [内容]: PropertyGroup
ADBE Vector Shape - Ellipse [椭圆路径 1]: PropertyGroup
ADBE Vector Shape Direction [形状方向]: Property = <Number> 1
ADBE Vector Ellipse Size [大小]: Property = <Array> 110.400002632141,110.400002632141
ADBE Vector Ellipse Position [位置]: Property = <Array> 0,0
ADBE Vector Graphic - Stroke [描边 1]: PropertyGroup
ADBE Vector Blend Mode [混合模式]: Property = <Number> 1
ADBE Vector Composite Order [合成]: Property = <Number> 1
ADBE Vector Stroke Color [颜色]: Property = <Array> 1,1,1,1
ADBE Vector Stroke Opacity [不透明度]: Property = <Number> 100
ADBE Vector Stroke Width [描边宽度]: Property = <Number> 10
ADBE Vector Stroke Line Cap [线段端点]: Property = <Number> 1
ADBE Vector Stroke Line Join [线段连接]: Property = <Number> 1
ADBE Vector Stroke Miter Limit [尖角限制]: Property = <Number> 4
ADBE Vector Stroke Dashes [虚线]: PropertyGroup
ADBE Vector Stroke Dash 1 [虚线]: Property = <Number> 10
ADBE Vector Stroke Gap 1 [间隙]: Property = <Number> 10
ADBE Vector Stroke Dash 2 [虚线 2]: Property = <Number> 10
ADBE Vector Stroke Gap 2 [间隙 2]: Property = <Number> 10
ADBE Vector Stroke Dash 3 [虚线 3]: Property = <Number> 10
ADBE Vector Stroke Gap 3 [间隙 3]: Property = <Number> 10
ADBE Vector Stroke Offset [偏移]: Property
ADBE Vector Stroke Taper [锥度]: PropertyGroup
ADBE Vector Taper Length Units [长度单位]: Property = <Number> 1
ADBE Vector Taper Start Length [起始长度]: Property
ADBE Vector Taper End Length [结束长度]: Property
ADBE Vector Taper StartWidthPx [起始长度]: Property
ADBE Vector Taper EndWidthPx [结束长度]: Property
ADBE Vector Taper Start Width [开始宽度]: Property
ADBE Vector Taper End Width [末端宽度]: Property
ADBE Vector Taper Start Ease [开始缓和]: Property
ADBE Vector Taper End Ease [结束缓和]: Property
ADBE Vector Stroke Wave [波形]: PropertyGroup
ADBE Vector Taper Wave Amount [数量]: Property
ADBE Vector Taper Wave Units [单位]: Property = <Number> 1
ADBE Vector Taper Wavelength [波长]: Property = <Number> 100
ADBE Vector Taper Wave Cycles [环形]: Property = <Number> 10
ADBE Vector Taper Wave Phase [相位]: Property
ADBE Vector Graphic - Fill [填充 1]: PropertyGroup
ADBE Vector Blend Mode [混合模式]: Property = <Number> 1
ADBE Vector Composite Order [合成]: Property = <Number> 1
ADBE Vector Fill Rule [填充规则]: Property = <Number> 1
ADBE Vector Fill Color [颜色]: Property = <Array> 0.58276235543045,0.75102203967525,0.95137257295496,1
ADBE Vector Fill Opacity [不透明度]: Property = <Number> 100
ADBE Vector Transform Group [变换]: PropertyGroup
ADBE Vector Anchor [锚点]: Property = <Array> 0,0
ADBE Vector Position [位置]: Property = <Array> -104.599982776641,14.4000132179264
ADBE Vector Scale [比例]: Property = <Array> 26,26
ADBE Vector Skew [倾斜]: Property
ADBE Vector Skew Axis [倾斜轴]: Property
ADBE Vector Rotation [旋转]: Property
ADBE Vector Group Opacity [不透明度]: Property = <Number> 100
ADBE Vector Materials Group [材质选项]: PropertyGroup
ADBE Vec3D Front RGB [正面颜色]: Property = <Array> 1,0,0,1
ADBE Vec3D Front Ambient [正面环境]: Property = <Number> 100
ADBE Vec3D Front Diffuse [正面漫射]: Property = <Number> 50
ADBE Vec3D Front Specular [正面镜面强度]: Property = <Number> 50
ADBE Vec3D Front Shininess [正面镜面反光度]: Property = <Number> 5
ADBE Vec3D Front Metal [正面金属质感]: Property = <Number> 100
ADBE Vec3D Front Reflection [正面反射强度]: Property
ADBE Vec3D Front Gloss [正面反射锐度]: Property = <Number> 100
ADBE Vec3D Front Fresnel [正面反射衰减]: Property
ADBE Vec3D Front Xparency [正面透明度]: Property
ADBE Vec3D Front XparRoll [正面透明度衰减]: Property
ADBE Vec3D Front IOR [正面折射率]: Property = <Number> 1
ADBE Vec3D Bevel RGB [斜面颜色]: Property = <Array> 1,0,0,1
ADBE Vec3D Bevel Ambient [斜面环境]: Property = <Number> 100
ADBE Vec3D Bevel Diffuse [斜面漫射]: Property = <Number> 50
ADBE Vec3D Bevel Specular [斜面镜面强度]: Property = <Number> 50
ADBE Vec3D Bevel Shininess [斜面镜面反光度]: Property = <Number> 5
ADBE Vec3D Bevel Metal [斜面金属质感]: Property = <Number> 100
ADBE Vec3D Bevel Reflection [斜面反射强度]: Property
ADBE Vec3D Bevel Gloss [斜面反射锐度]: Property = <Number> 100
ADBE Vec3D Bevel Fresnel [斜面反射衰减]: Property
ADBE Vec3D Bevel Xparency [斜面透明度]: Property
ADBE Vec3D Bevel XparRoll [斜面透明度衰减]: Property
ADBE Vec3D Bevel IOR [斜面折射率]: Property = <Number> 1
ADBE Vec3D Side RGB [侧面颜色]: Property = <Array> 1,0,0,1
ADBE Vec3D Side Ambient [侧面环境]: Property = <Number> 100
ADBE Vec3D Side Diffuse [侧面漫射]: Property = <Number> 50
ADBE Vec3D Side Specular [侧面镜面强度]: Property = <Number> 50
ADBE Vec3D Side Shininess [侧面镜面反光度]: Property = <Number> 5
ADBE Vec3D Side Metal [侧面金属质感]: Property = <Number> 100
ADBE Vec3D Side Reflection [侧面反射强度]: Property
ADBE Vec3D Side Gloss [侧面反射锐度]: Property = <Number> 100
ADBE Vec3D Side Fresnel [侧面反射衰减]: Property
ADBE Vec3D Side Xparency [侧面透明度]: Property
ADBE Vec3D Side XparRoll [侧面透明度衰减]: Property
ADBE Vec3D Side IOR [侧面折射率]: Property = <Number> 1
ADBE Vec3D Back RGB [背面颜色]: Property = <Array> 1,0,0,1
ADBE Vec3D Back Ambient [背面环境]: Property = <Number> 100
ADBE Vec3D Back Diffuse [背面漫射]: Property = <Number> 50
ADBE Vec3D Back Specular [背面镜面强度]: Property = <Number> 50
ADBE Vec3D Back Shininess [背面镜面反光度]: Property = <Number> 5
ADBE Vec3D Back Metal [背面金属质感]: Property = <Number> 100
ADBE Vec3D Back Reflection [背面反射强度]: Property
ADBE Vec3D Back Gloss [背面反射锐度]: Property = <Number> 100
ADBE Vec3D Back Fresnel [背面反射衰减]: Property
ADBE Vec3D Back Xparency [背面透明度]: Property
ADBE Vec3D Back XparRoll [背面透明度衰减]: Property
ADBE Vec3D Back IOR [背面折射率]: Property = <Number> 1
ADBE Mask Parade [蒙版]: PropertyGroup
ADBE Effect Parade [效果]: PropertyGroup
ADBE Transform Group [变换]: PropertyGroup
ADBE Anchor Point [锚点]: Property = <Array> 0,0,0
ADBE Position [位置]: Property = <Array> 960,540,0
ADBE Position_0 [X 位置]: Property
ADBE Position_1 [Y 位置]: Property
ADBE Position_2 [Z 位置]: Property
ADBE Scale [缩放]: Property = <Array> 203,203,203
ADBE Orientation [方向]: Property = <Array> 0,0,0
ADBE Rotate X [X 轴旋转]: Property
ADBE Rotate Y [Y 轴旋转]: Property
ADBE Rotate Z [Z 轴旋转]: Property
ADBE Opacity [不透明度]: Property = <Number> 100
ADBE Envir Appear in Reflect [在反射中显示]: Property = <Number> 1
ADBE Layer Styles [图层样式]: PropertyGroup
ADBE Blend Options Group [混合选项]: PropertyGroup
ADBE Global Angle2 [全局光角度]: Property = <Number> 120
ADBE Global Altitude2 [全局光高度]: Property = <Number> 30
ADBE Adv Blend Group [高级混合]: PropertyGroup
ADBE Layer Fill Opacity2 [填充不透明度]: Property = <Number> 100
ADBE R Channel Blend [红色]: Property = <Number> 1
ADBE G Channel Blend [绿色]: Property = <Number> 1
ADBE B Channel Blend [蓝色]: Property = <Number> 1
ADBE Blend Interior [混合内部样式为组]: Property
ADBE Blend Ranges [使用源的混合范围]: Property = <Number> 1
dropShadow/enabled [投影]: PropertyGroup
dropShadow/mode2 [混合模式]: Property = <Number> 5
dropShadow/color [颜色]: Property = <Array> 0,0,0,1
dropShadow/opacity [不透明度]: Property = <Number> 75
dropShadow/useGlobalAngle [使用全局光]: Property
dropShadow/localLightingAngle [角度]: Property = <Number> 120
dropShadow/distance [距离]: Property = <Number> 5
dropShadow/chokeMatte [扩展]: Property
dropShadow/blur [大小]: Property = <Number> 5
dropShadow/noise [杂色]: Property
dropShadow/layerConceals [图层镂空投影]: Property = <Number> 1
innerShadow/enabled [内阴影]: PropertyGroup
innerShadow/mode2 [混合模式]: Property = <Number> 5
innerShadow/color [颜色]: Property = <Array> 0,0,0,1
innerShadow/opacity [不透明度]: Property = <Number> 75
innerShadow/useGlobalAngle [使用全局光]: Property
innerShadow/localLightingAngle [角度]: Property = <Number> 120
innerShadow/distance [距离]: Property = <Number> 5
innerShadow/chokeMatte [阻塞]: Property
innerShadow/blur [大小]: Property = <Number> 5
innerShadow/noise [杂色]: Property
outerGlow/enabled [外发光]: PropertyGroup
outerGlow/mode2 [混合模式]: Property = <Number> 11
outerGlow/opacity [不透明度]: Property = <Number> 75
outerGlow/noise [杂色]: Property
outerGlow/AEColorChoice [颜色类型]: Property = <Number> 1
outerGlow/color [颜色]: Property = <Array> 1,1,0.74509803921569,1
outerGlow/gradient [颜色]: Property
outerGlow/gradientSmoothness [渐变平滑度]: Property = <Number> 100
outerGlow/glowTechnique [技术]: Property = <Number> 1
outerGlow/chokeMatte [扩展]: Property
outerGlow/blur [大小]: Property = <Number> 5
outerGlow/inputRange [范围]: Property = <Number> 50
outerGlow/shadingNoise [抖动]: Property
innerGlow/enabled [内发光]: PropertyGroup
innerGlow/mode2 [混合模式]: Property = <Number> 11
innerGlow/opacity [不透明度]: Property = <Number> 75
innerGlow/noise [杂色]: Property
innerGlow/AEColorChoice [颜色类型]: Property = <Number> 1
innerGlow/color [颜色]: Property = <Array> 1,1,0.74509803921569,1
innerGlow/gradient [颜色]: Property
innerGlow/gradientSmoothness [渐变平滑度]: Property = <Number> 100
innerGlow/glowTechnique [技术]: Property = <Number> 1
innerGlow/innerGlowSource [源]: Property = <Number> 1
innerGlow/chokeMatte [阻塞]: Property
innerGlow/blur [大小]: Property = <Number> 5
innerGlow/inputRange [范围]: Property = <Number> 50
innerGlow/shadingNoise [抖动]: Property
bevelEmboss/enabled [斜面和浮雕]: PropertyGroup
bevelEmboss/bevelStyle [样式]: Property = <Number> 2
bevelEmboss/bevelTechnique [技术]: Property = <Number> 1
bevelEmboss/strengthRatio [深度]: Property = <Number> 100
bevelEmboss/bevelDirection [方向]: Property = <Number> 1
bevelEmboss/blur [大小]: Property = <Number> 5
bevelEmboss/softness [柔化]: Property
bevelEmboss/useGlobalAngle [使用全局光]: Property
bevelEmboss/localLightingAngle [角度]: Property = <Number> 120
bevelEmboss/localLightingAltitude [高度]: Property = <Number> 30
bevelEmboss/highlightMode [高亮模式]: Property = <Number> 11
bevelEmboss/highlightColor [加亮颜色]: Property = <Array> 1,1,1,1
bevelEmboss/highlightOpacity [高光不透明度]: Property = <Number> 75
bevelEmboss/shadowMode [阴影模式]: Property = <Number> 5
bevelEmboss/shadowColor [阴影颜色]: Property = <Array> 0,0,0,1
bevelEmboss/shadowOpacity [阴影不透明度]: Property = <Number> 75
chromeFX/enabled [光泽]: PropertyGroup
chromeFX/mode2 [混合模式]: Property = <Number> 5
chromeFX/color [颜色]: Property = <Array> 0,0,0,1
chromeFX/opacity [不透明度]: Property = <Number> 50
chromeFX/localLightingAngle [角度]: Property = <Number> 19
chromeFX/distance [距离]: Property = <Number> 11
chromeFX/blur [大小]: Property = <Number> 14
chromeFX/invert [反转]: Property = <Number> 1
solidFill/enabled [颜色叠加]: PropertyGroup
solidFill/mode2 [混合模式]: Property = <Number> 1
solidFill/color [颜色]: Property = <Array> 1,0,0,1
solidFill/opacity [不透明度]: Property = <Number> 100
gradientFill/enabled [渐变叠加]: PropertyGroup
gradientFill/mode2 [混合模式]: Property = <Number> 1
gradientFill/opacity [不透明度]: Property = <Number> 100
gradientFill/gradient [颜色]: Property
gradientFill/gradientSmoothness [渐变平滑度]: Property = <Number> 100
gradientFill/angle [角度]: Property = <Number> 90
gradientFill/type [样式]: Property = <Number> 1
gradientFill/reverse [反向]: Property
gradientFill/align [与图层对齐]: Property = <Number> 1
gradientFill/scale [缩放]: Property = <Number> 100
gradientFill/offset [偏移]: Property = <Array> 0,0
patternFill/enabled [图案叠加]: PropertyGroup
patternFill/mode2 [混合模式]: Property = <Number> 1
patternFill/opacity [不透明度]: Property = <Number> 100
patternFill/align [与图层链接]: Property = <Number> 1
patternFill/scale [缩放]: Property = <Number> 100
patternFill/phase [偏移]: Property = <Array> 0,0
frameFX/enabled [描边]: PropertyGroup
frameFX/mode2 [混合模式]: Property = <Number> 1
frameFX/color [颜色]: Property = <Array> 1,0,0,1
frameFX/size [大小]: Property = <Number> 3
frameFX/opacity [不透明度]: Property = <Number> 100
frameFX/style [位置]: Property = <Number> 1
ADBE Extrsn Options Group [几何选项]: PropertyGroup
ADBE Bevel Styles [斜面样式]: Property = <Number> 1
ADBE Bevel Direction [斜面方向]: Property = <Number> 1
ADBE Bevel Depth [斜面深度]: Property = <Number> 2
ADBE Hole Bevel Depth [洞斜面深度]: Property = <Number> 100
ADBE Extrsn Depth [凸出深度]: Property
ADBE Material Options Group [材质选项]: PropertyGroup
ADBE Casts Shadows [投影]: Property
ADBE Light Transmission [透光率]: Property
ADBE Accepts Shadows [接受阴影]: Property = <Number> 1
ADBE Accepts Lights [接受灯光]: Property = <Number> 1
ADBE Appears in Reflections [在反射中显示]: Property = <Number> 1
ADBE Ambient Coefficient [环境]: Property = <Number> 100
ADBE Diffuse Coefficient [漫射]: Property = <Number> 50
ADBE Specular Coefficient [镜面强度]: Property = <Number> 50
ADBE Shininess Coefficient [镜面反光度]: Property = <Number> 5
ADBE Metal Coefficient [金属质感]: Property = <Number> 100
ADBE Reflection Coefficient [反射强度]: Property
ADBE Glossiness Coefficient [反射锐度]: Property = <Number> 100
ADBE Fresnel Coefficient [反射衰减]: Property
ADBE Transparency Coefficient [透明度]: Property
ADBE Transp Rolloff [透明度衰减]: Property
ADBE Index of Refraction [折射率]: Property = <Number> 1
ADBE Audio Group [音频]: PropertyGroup
ADBE Audio Levels [音频电平]: Property = <Array> 0,0
ADBE Layer Sets [集]: PropertyGroup
More Shapes!
如果从头构建属性,会十分麻烦,因此这里采用原型模式。将之前手动创建好的那个椭圆复制多份。由于每个粒子的生命周期是独立的,因此还是分图层比较好。事实上,单个图层里的属性是没法直接复制的,虽然手动创建完整的属性树并不麻烦,比如:
function test() {
var layer = app.project.activeItem.layer(1);
var vectors = layer.property("ADBE Root Vectors Group");
var eclipse2 = vectors.addProperty("ADBE Vector Group");
eclipse2
.property("ADBE Vectors Group")
.addProperty("ADBE Vector Shape - Ellipse");
eclipse2
.property("ADBE Vectors Group")
.addProperty("ADBE Vector Graphic - Fill");
return;
}
function initLayer(comp, layer) {
var theta = randf(0, Math.PI * 2);
const preRange = randf(0, 50);
const maxTime = 3;
const speed = 100;
var dieTime = randf(2, 3);
var center = [comp.width / 2, comp.height / 2];
var x0 = Math.cos(theta) * preRange,
y0 = Math.sin(theta) * preRange;
var x1 = Math.cos(theta) * speed * maxTime,
y1 = Math.sin(theta) * speed * maxTime;
layer.position.setValueAtTime(0, center + [x0, y0]);
layer.position.setValueAtTime(maxTime, center + [x1, y1]);
layer.inPoint = 0;
layer.outPoint = dieTime;
}
在圆多的情况下脚本会执行较长时间,且单帧渲染时间较长。
Electron Cloud
但是上面的太普通了,我们想要粒子模拟出电子云。
众所周知电子云可以用 这 3 个参数来描述。为简化模型,下面我们只考虑基态氢原子 。
可视化方案有很多,这里采用的是用点密度来表示概率密度。
我们小学二年级时已经学过,单位立体角概率
这也就说,任何方向的概率相同。
径向位置概率密度
现在来根据这个 PDF 概率密度函数来生成随机半径。
积分得 CDF
取随机数 ,令 ,则反解出 。行吧,解不了。
下面采取 Acceptance-Rejection 采样法。
设 为 PDF,定义域为 ,值域为 。随机生成 ,,若 ,则接受采用,否则拒绝并重新生成 。
根据函数图像可以设置 ,当然 和 设置稍大也可,影响不大。
下面就是生成随机点了,没什么好解释的。包括径向随机点和球面随机点。
function randomR() {
function f(r) {
const a = 0.529;
return (4 / (a * a * a)) * r * r * Math.exp((-2 * r) / a);
}
const m = 1.023;
do {
var x = randf(0, 3),
y = randf(0, m);
if (y <= f(x)) return x;
} while (true);
}
function randomPoint() {
var r = randomR();
var phi = randf(0, 2 * Math.PI);
var theta = Math.acos(randf(-1, 1));
return [r, theta, phi];
}
function spherical(r, theta, phi) {
return (
r *
[
Math.sin(theta) * Math.cos(phi),
Math.sin(theta) * Math.sin(phi),
Math.cos(theta),
]
);
}
最后的调用部分。这里将半径放大 40 倍,速度和到原点距离成线性关系,运动轨迹比较随意了。
function initLayer(comp, layer) {
const maxTime = 1.5;
var dieTime = randf(1.0, 1.5);
var center = [comp.width / 2, comp.height / 2];
var pos = randomPoint();
var r = pos[0],
theta = pos[1],
phi = pos[2];
var startPos = spherical(r * 40, theta, phi);
layer.position.setValueAtTime(0, center + startPos);
layer.position.setValueAtTime(maxTime, center + startPos * 16);
layer.inPoint = 0;
layer.outPoint = dieTime;
layer.position.setInterpolationTypeAtKey(1, KeyframeInterpolationType.BEZIER);
}
上面最后一行设置关键帧插值,开始帧使用二次插值。
运行效果如下。