Opacity [0,5] [0,100]
Opacity [0,5] [100,0]
Scale [0,5] [100,100, 0,0]
Scale [0,5] [0,0, 100,100]
Scale [0,5,7] [0,0, 110,110, 100,100]
Rotation [0,5] [0,25]
Position [0,5] [0,0, 0,100]
Position [0,5] [0,0, 100,0]
Position [0,5,10] [0,0, 100,100, 0,0]
// AddKeyframeUI_v4.jsx
(function (thisObj) {
var settingsFile = null;
function readSettingsFile() {
if (!settingsFile) {
settingsFile = File.openDialog("Select AKUI_settings.txt", "*.txt");
}
if (!settingsFile || !settingsFile.exists) return null;
if (!settingsFile.open("r")) {
alert("Failed to open file.");
return null;
}
var lines = [];
while (!settingsFile.eof) {
var line = settingsFile.readln();
if (line.replace(/^\s+|\s+$/g, "") !== "") lines.push(line);
}
settingsFile.close();
return lines;
}
function parseSettingLine(line) {
var pattern = /(\w+) \[([\d,\s]+)\] \[([\d,\s]+)\]/;
var match = line.match(pattern);
if (!match) return null;
var prop = match[1];
var rawFrames = match[2].split(/,\s*/);
var frames = [];
for (var i = 0; i < rawFrames.length; i++) {
frames.push(parseFloat(rawFrames[i]));
}
var rawValues = match[3].split(/,\s*/);
var values = [];
for (var j = 0; j < rawValues.length; j++) {
values.push(parseFloat(rawValues[j]));
}
return { property: prop, frames: frames, values: values, raw: line };
}
function applyKeyframes(layer, setting, repeatCount) {
var comp = layer.containingComp;
var frameDuration = 1 / comp.frameRate;
var currentTime = comp.time;
if (!repeatCount || repeatCount < 1) repeatCount = 1;
var prop = layer.property(setting.property);
if (!prop) {
alert("Layer \"" + layer.name + "\" does not have \"" + setting.property + "\".");
return;
}
var baseKeyframes = [];
var is2D = (setting.property === "Position" || setting.property === "Scale" || setting.property === "Anchor Point");
for (var i = 0; i < setting.frames.length; i++) {
var frame = setting.frames[i];
var t = frame * frameDuration;
var v;
if (setting.property === "Position") {
var base = prop.value;
v = [
base[0] + setting.values[i * 2],
base[1] + setting.values[i * 2 + 1]
];
} else if (is2D) {
v = [
setting.values[i * 2],
setting.values[i * 2 + 1]
];
} else {
v = setting.values[i];
}
baseKeyframes.push({ frame: frame, value: v });
}
var keyframes = [];
var minFrame = Math.min.apply(null, setting.frames);
var maxFrame = Math.max.apply(null, setting.frames);
var chunkSpan = maxFrame - minFrame;
var offsetSpan = chunkSpan * 2;
for (var r = 0; r < repeatCount; r++) {
var offset = r * offsetSpan;
for (var k = 0; k < baseKeyframes.length; k++) {
var newTime = currentTime + ((baseKeyframes[k].frame + offset) * frameDuration);
keyframes.push({ time: newTime, value: baseKeyframes[k].value });
}
}
for (var j = 0; j < keyframes.length; j++) {
prop.setValueAtTime(keyframes[j].time, keyframes[j].value);
}
}
function buildUI(thisObj, settings) {
var win = (thisObj instanceof Panel)
? thisObj
: new Window("palette", "AddKeyframeUI v4", undefined, { resizeable: true });
win.orientation = "column";
win.alignChildren = ["fill", "top"];
var reloadBtn = win.add("button", undefined, "🔄 Reload Settings File");
reloadBtn.onClick = function () {
if (!settingsFile || !settingsFile.exists) {
alert("No file loaded or file missing.");
return;
}
var lines = readSettingsFile();
if (!lines) return;
var parsed = [];
for (var i = 0; i < lines.length; i++) {
var s = parseSettingLine(lines[i]);
if (s) parsed.push(s);
}
win.close();
buildUI(thisObj, parsed);
};
for (var i = 0; i < settings.length; i++) {
(function (setting) {
var group = win.add("group");
group.orientation = "row";
var text = group.add("statictext", undefined, setting.raw);
text.characters = 30;
var applyBtn = group.add("button", undefined, "apply");
applyBtn.preferredSize= [45,25];
applyBtn.onClick = function () {
var comp = app.project.activeItem;
if (!(comp && comp instanceof CompItem)) {
alert("Please open a composition.");
return;
}
var layers = comp.selectedLayers;
if (layers.length === 0) {
alert("Please select at least one layer.");
return;
}
app.beginUndoGroup("Apply Keyframes");
for (var j = 0; j < layers.length; j++) {
applyKeyframes(layers[j], setting, 1);
}
app.endUndoGroup();
};
var repeatInput = group.add("edittext", undefined, "");
repeatInput.characters = 4;
var repeatBtn = group.add("button", undefined, "repeat");
repeatBtn.preferredSize= [45,25];
repeatBtn.onClick = function () {
var repeatCount = parseInt(repeatInput.text, 10);
if (isNaN(repeatCount) || repeatCount < 1) {
alert("Please enter a valid repeat count.");
return;
}
var comp = app.project.activeItem;
if (!(comp && comp instanceof CompItem)) {
alert("Please open a composition.");
return;
}
var layers = comp.selectedLayers;
if (layers.length === 0) {
alert("Please select at least one layer.");
return;
}
app.beginUndoGroup("Repeat Keyframes");
for (var j = 0; j < layers.length; j++) {
applyKeyframes(layers[j], setting, repeatCount);
}
app.endUndoGroup();
};
})(settings[i]);
}
if (win instanceof Window) win.show();
else win.layout.layout(true);
return win;
}
var rawLines = readSettingsFile();
if (!rawLines) return;
var parsedSettings = [];
for (var i = 0; i < rawLines.length; i++) {
var setting = parseSettingLine(rawLines[i]);
if (setting) parsedSettings.push(setting);
}
buildUI(thisObj, parsedSettings);
})(this);