Created
June 11, 2025 20:50
-
-
Save nathanshipley/fb7ff367364f12923952cf8510558e70 to your computer and use it in GitHub Desktop.
Position Keyframes to Text Converter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// After Effects Script: Position Keyframes to Text Converter | |
// This script converts position keyframes from selected layers to text format | |
(function() { | |
// Check if a composition is active | |
var comp = app.project.activeItem; | |
if (!comp || !(comp instanceof CompItem)) { | |
alert("Please select a composition first."); | |
return; | |
} | |
// Check if any layers are selected | |
var selectedLayers = comp.selectedLayers; | |
if (selectedLayers.length === 0) { | |
alert("Please select at least one layer."); | |
return; | |
} | |
// Store the output text | |
var outputText = ""; | |
// Begin undo group - this will allow undoing all operations with one undo | |
app.beginUndoGroup("Extract Position Keyframes to Text"); | |
try { | |
// Process each selected layer | |
for (var i = 0; i < selectedLayers.length; i++) { | |
var layer = selectedLayers[i]; | |
var position = layer.property("Transform").property("Position"); | |
// Check if position property exists | |
if (!position) { | |
continue; | |
} | |
// Store original expression state | |
var originalExpression = position.expression; | |
var hadExpression = (originalExpression && originalExpression !== ""); | |
// Add a dummy expression if no expression exists | |
if (!hadExpression) { | |
position.expression = "value"; | |
} | |
// Convert expression to keyframes | |
position.selected = true; | |
app.executeCommand(app.findMenuCommandId("Convert Expression to Keyframes")); | |
// Now extract the keyframe data | |
var keyframeData = extractKeyframeData(layer, comp); | |
outputText += keyframeData; | |
} | |
} finally { | |
// End undo group | |
app.endUndoGroup(); | |
} | |
// Create a window to display the results | |
showResultsWindow(outputText); | |
function extractKeyframeData(layer, comp) { | |
var position = layer.property("Transform").property("Position"); | |
var data = ""; | |
// Header information | |
data += "Adobe After Effects 8.0 Keyframe Data\n\n"; | |
data += "\tUnits Per Second\t" + Math.round(comp.frameRate) + "\n"; | |
// Get source dimensions (using layer dimensions as approximation) | |
var sourceWidth = layer.width || 100; | |
var sourceHeight = layer.height || 100; | |
data += "\tSource Width\t" + Math.round(sourceWidth) + "\n"; | |
data += "\tSource Height\t" + Math.round(sourceHeight) + "\n"; | |
data += "\tSource Pixel Aspect Ratio\t1\n"; | |
data += "\tComp Pixel Aspect Ratio\t1\n\n"; | |
data += "Transform\tPosition\n"; | |
data += "\tFrame\tX pixels\tY pixels\tZ pixels\t\n"; | |
// Get keyframe times and values | |
var numKeys = position.numKeys; | |
var fps = comp.frameRate; | |
var startTime = comp.workAreaStart; | |
var endTime = startTime + comp.workAreaDuration; | |
if (numKeys > 0) { | |
// If there are keyframes, use them | |
var firstKeyTime = position.keyTime(1); | |
var lastKeyTime = position.keyTime(numKeys); | |
// Sample from first key to last key | |
var currentTime = firstKeyTime; | |
var frameNum = 0; | |
while (currentTime <= lastKeyTime) { | |
var value = position.valueAtTime(currentTime, false); | |
var x = value[0]; | |
var y = value[1]; | |
var z = (value.length > 2) ? value[2] : 0; | |
data += "\t" + frameNum + "\t" + x + "\t" + y + "\t" + z + "\t\n"; | |
frameNum++; | |
currentTime = firstKeyTime + (frameNum / fps); | |
} | |
} else { | |
// If no keyframes, sample the entire work area | |
var totalFrames = Math.floor(comp.workAreaDuration * fps); | |
for (var frame = 0; frame <= totalFrames; frame++) { | |
var time = startTime + (frame / fps); | |
var value = position.valueAtTime(time, false); | |
var x = value[0]; | |
var y = value[1]; | |
var z = (value.length > 2) ? value[2] : 0; | |
data += "\t" + frame + "\t" + x + "\t" + y + "\t" + z + "\t\n"; | |
} | |
} | |
data += "\n\nEnd of Keyframe Data\n"; | |
return data; | |
} | |
function showResultsWindow(text) { | |
// Create UI window | |
var dialog = new Window("dialog", "Position Keyframe Data"); | |
dialog.orientation = "column"; | |
dialog.alignChildren = "fill"; | |
dialog.preferredSize.width = 600; | |
dialog.preferredSize.height = 500; | |
// Add instruction text | |
var instructions = dialog.add("statictext", undefined, "Copy the text below (Ctrl/Cmd+A to select all, Ctrl/Cmd+C to copy):"); | |
// Create a panel to hold the text (prevents edittext issues) | |
var textPanel = dialog.add("panel"); | |
textPanel.alignChildren = "fill"; | |
// Add text area with the keyframe data | |
var textArea = textPanel.add("edittext", undefined, text, { | |
multiline: true, | |
scrolling: true, | |
readonly: false | |
}); | |
textArea.preferredSize.height = 400; | |
// Add buttons | |
var buttonGroup = dialog.add("group"); | |
buttonGroup.alignment = "center"; | |
var copyInstructionBtn = buttonGroup.add("button", undefined, "Copy Instructions"); | |
var closeBtn = buttonGroup.add("button", undefined, "Close"); | |
// Button functionality | |
copyInstructionBtn.onClick = function() { | |
// Show copy instructions | |
alert("To copy all text:\n\n" + | |
"1. Click inside the text area\n" + | |
"2. Press Ctrl/Cmd + A to select all\n" + | |
"3. Press Ctrl/Cmd + C to copy\n\n" + | |
"After closing this window, you can press Ctrl/Cmd + Z to undo\n" + | |
"the keyframe baking and restore your original expressions."); | |
}; | |
closeBtn.onClick = function() { | |
dialog.close(); | |
}; | |
// Auto-select all text when window opens | |
textArea.active = true; | |
// Show the dialog | |
dialog.show(); | |
// Try to select all text after showing | |
textArea.textselection = [0, text.length]; | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment