I created this script (with the help of Perplexity...): {
function spaceOutSelectedKeyframesEvenly() {
var comp = app.project.activeItem;
if (!(comp && comp instanceof CompItem)) {
alert("Please select a composition.");
return;
}
var selectedLayers = comp.selectedLayers;
if (selectedLayers.length === 0) {
alert("Please select at least one layer.");
return;
}
// Find first property with selected keyframes
var targetProp = null;
for (var i = 0; i < selectedLayers.length; i++) {
var layer = selectedLayers[i];
var selectedProps = layer.selectedProperties;
for (var j = 0; j < selectedProps.length; j++) {
var prop = selectedProps[j];
if (prop.numKeys > 0 && prop.selectedKeys.length > 0) {
targetProp = prop;
break;
}
}
if (targetProp) break;
}
if (!targetProp) {
alert("Please select a property with selected keyframes.");
return;
}
var intervalFramesStr = prompt("Enter interval between keyframes in frames:", "5");
if (intervalFramesStr === null) return;
var intervalFrames = parseInt(intervalFramesStr, 10);
if (isNaN(intervalFrames) || intervalFrames <= 0) {
alert("Invalid interval entered. Please enter a positive integer.");
return;
}
app.beginUndoGroup("Space Out Selected Keyframes Evenly");
var frameDuration = 1 / comp.frameRate;
// Get selected keyframe indices sorted ascending
var selectedKeyIndices = targetProp.selectedKeys.slice(0).sort(function(a, b) { return a - b; });
// Save original keyframe data (time, value, easing, interpolation)
var keyData = [];
for (var i = 0; i < selectedKeyIndices.length; i++) {
var idx = selectedKeyIndices[i];
keyData.push({
originalTime: targetProp.keyTime(idx),
value: targetProp.keyValue(idx),
inInterp: targetProp.keyInInterpolationType(idx),
outInterp: targetProp.keyOutInterpolationType(idx),
temporalEaseIn: targetProp.keyInTemporalEase(idx),
temporalEaseOut: targetProp.keyOutTemporalEase(idx),
spatialInterp: (typeof targetProp.keySpatialInterpolationType === "function") ? targetProp.keySpatialInterpolationType(idx) : null,
roving: (typeof targetProp.keyRoving === "function") ? targetProp.keyRoving(idx) : false
});
}
// Calculate new times spaced evenly starting at the first selected keyframe's original time
var startTime = keyData[0].originalTime;
var newTimes = [];
for (var i = 0; i < keyData.length; i++) {
newTimes.push(startTime + i * intervalFrames * frameDuration);
}
// Remove selected keyframes from highest index to lowest to avoid reindexing issues
for (var i = selectedKeyIndices.length - 1; i >= 0; i--) {
targetProp.removeKey(selectedKeyIndices[i]);
}
// Add new keyframes at new evenly spaced times with original values and restore easing/interpolation
for (var i = 0; i < keyData.length; i++) {
targetProp.setValueAtTime(newTimes[i], keyData[i].value);
var newKeyIndex = targetProp.nearestKeyIndex(newTimes[i]);
if (newKeyIndex > 0) {
targetProp.setTemporalEaseAtKey(newKeyIndex, keyData[i].temporalEaseIn, keyData[i].temporalEaseOut);
targetProp.setInterpolationTypeAtKey(newKeyIndex, keyData[i].inInterp, keyData[i].outInterp);
if (keyData[i].spatialInterp !== null) {
targetProp.setSpatialInterpolationTypeAtKey(newKeyIndex, keyData[i].spatialInterp);
}
if (keyData[i].roving !== undefined) {
targetProp.setRovingAtKey(newKeyIndex, keyData[i].roving);
}
}
}
app.endUndoGroup();
}
spaceOutSelectedKeyframesEvenly();
}
... View more