script: comp.width and comp.height resize the frame without moving the layers (#1942)

The setters go through `comp.settings`, which defaults to the Composition Settings dialog's
centre anchor, so `comp.width = 800` on a 400-wide comp shifted every unparented layer — and
all of its position keys — by 200 px. A script that resized a precomp then found its pixel
coordinates had moved under it.

After Effects' width and height setters only change the frame; moving the contents is what
"resize from centre" scripts do for themselves. The setters now pass `anchor: 0` (top left).
The dialog, which is where the anchor belongs, is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Anubhav-Rai
2026-10-23 02:48:28 +05:30
co-authored by Claude Opus 5
parent 15b8a916c5
commit 88045641f3
2 changed files with 32 additions and 2 deletions
+5 -2
View File
@@ -1506,8 +1506,11 @@ function AVItem() {}
AVItem.prototype = Object.create(Item.prototype);
(function () {
function def(name, get, set) { Object.defineProperty(AVItem.prototype, name, { get: get, set: set }); }
def("width", function () { return this.__info().width; }, function (v) { this.__settings({ width: Math.round(__num(v, "width")) }); });
def("height", function () { return this.__info().height; }, function (v) { this.__settings({ height: Math.round(__num(v, "height")) }); });
// `anchor: 0` (top left): After Effects' width and height setters resize the frame and leave the
// layers at their coordinates. Re-anchoring the old frame in the new one belongs to the
// Composition Settings dialog, which is what `comp.settings` defaults to (#1942).
def("width", function () { return this.__info().width; }, function (v) { this.__settings({ width: Math.round(__num(v, "width")), anchor: 0 }); });
def("height", function () { return this.__info().height; }, function (v) { this.__settings({ height: Math.round(__num(v, "height")), anchor: 0 }); });
def("pixelAspect", function () { return this.__info().pixelAspect; }, function (v) { this.__settings({ pixelAspect: __num(v) }); });
def("duration", function () { return this.__info().duration; }, function (v) { this.__settings({ duration: __num(v, "duration") }); });
def("frameRate", function () { return this.__info().frameRate; }, function (v) { this.__settings({ frameRate: __num(v, "frameRate") }); });
+27
View File
@@ -1623,3 +1623,30 @@ fn set_value_rejects_an_array_of_the_wrong_dimension() {
let (_, c) = comp_named(&s, "Dims");
assert_eq!(layer(c, "s").props.prop("transform/position").unwrap().value, KV::Vec3([120.0, 34.0, 0.0]));
}
/// #1942: the scripting width and height setters resize the frame only. Re-anchoring the old frame
/// in the new one, which moves every unparented layer, belongs to the Composition Settings dialog.
#[test]
fn setting_comp_width_and_height_leaves_the_layers_where_they_are() {
let mut s = session();
let o = ok(
&mut s,
r#"
var c = app.project.items.addComp("w", 400, 200, 1, 1, 25);
var l = c.layers.addSolid([1, 1, 1], "s", 50, 50, 1, 1);
var pos = l.property("ADBE Transform Group").property("ADBE Position");
pos.setValue([100, 100]);
var k = c.layers.addSolid([1, 1, 1], "k", 50, 50, 1, 1);
var kpos = k.property("ADBE Transform Group").property("ADBE Position");
kpos.setValueAtTime(0, [10, 20]);
kpos.setValueAtTime(1, [30, 40]);
var before = "" + pos.value;
c.width = 800;
c.height = 400;
[before, "" + pos.value, "" + kpos.keyValue(1), "" + kpos.keyValue(2), c.width, c.height]
"#,
);
assert_eq!(o.result, json!(["100,100,0", "100,100,0", "10,20,0", "30,40,0", 800, 400]));
let (_, c) = comp_named(&s, "w");
assert_eq!(layer(c, "s").props.prop("transform/position").unwrap().value, KV::Vec3([100.0, 100.0, 0.0]));
}