Skip to content

Understanding useObjectTransform

The useObjectTransform property controls whether the ObjectFit container preserves or discards the original object's transform (position, scale, rotation, skew) when the object is added.

The Problem It Solves

When you load an image or create a Fabric.js object, it may already carry transform properties: a position on the canvas, a rotation angle, a non-uniform scale, or a skew. When you wrap that object in an ObjectFit container, the library needs to decide what to do with those pre-existing transforms.

There are two valid behaviors:

  1. Preserve transforms — The ObjectFit container inherits the object's position/rotation/scale. The container appears where the object was, as if you swapped the object for a fitted version in place.

  2. Discard transforms — The ObjectFit container starts with a clean identity transform (position 0,0, no rotation, no scale). You control placement yourself.

How It Works

Default behavior

The constructor defaults useObjectTransform to true:

ts
const container = new ObjectFit(img, {
  width: 400,
  height: 400,
  mode: "cover",
  // useObjectTransform defaults to true
});

With useObjectTransform: true, the library:

  1. Captures the object's current transform via qrDecompose() on its transform matrix (position, scale, rotation, skew, origin).
  2. Normalizes the position to left/top origin using Fabric.js's translateToGivenOrigin(). This is critical because Fabric.js v7 defaults to center/center origin, while the library uses left/top internally. Without this conversion, the container would appear at the wrong position.
  3. Resets the object to an identity transform internally.
  4. Computes the fit (cover, contain, etc.) on the clean object.
  5. Re-applies the normalized transform to the ObjectFit container itself.

The result: the container appears at the same position, rotation, and scale as the original object, regardless of which originX/originY the object used.

Disabling it

ts
const container = new ObjectFit(img, {
  width: 400,
  height: 400,
  mode: "cover",
  useObjectTransform: false,
});

With useObjectTransform: false, the captured transform is stored but not applied. The container starts at position (0, 0) with no rotation or scale. You position it yourself:

ts
container.set({ left: 100, top: 50, angle: 15 });

When to Use Each

Use true (default) when:

  • Replacing an existing object on the canvas. You have an image at position (200, 150) rotated 30 degrees, and you want to wrap it in an ObjectFit container that lands in the same spot.

  • Round-trip serialization. When deserializing from JSON, the object may carry saved transforms that should be restored.

  • Swapping content. Using the object setter (which respects the instance's useObjectTransform property) to replace one image with another while keeping the container's position.

ts
// The container stays where it is, just the content changes
container.object = newImage;
container.recompute();

Use false when:

  • Building a fresh layout. You are placing containers at specific canvas positions yourself and don't want the source image's transforms to interfere.

  • Loading images from URLs. Freshly loaded images typically have identity transforms, so useObjectTransform has no visible effect. But setting it to false makes the intent explicit and avoids surprises if the image somehow carries transforms.

  • Creating galleries or grids. When you control the exact position of each container.

ts
const container = new ObjectFit(img, {
  width: 200,
  height: 200,
  mode: "contain",
  useObjectTransform: false,
});

container.set({ left: column * 220, top: row * 220 });
canvas.add(container);

The setObject Method vs. the object Setter

There is a subtle difference in defaults:

MethodDefault useObjectTransform
new ObjectFit(img, { ... })true (from constructor)
container.object = imgUses the instance's useObjectTransform property (default true)
container.setObject(img)false
container.setObject(img, true)true (explicit)

The setObject() method defaults to false because it is typically called when programmatically replacing content, where you usually want the container to stay put. The object setter delegates to setObject with the instance's useObjectTransform value, preserving the behavior chosen at construction time.

ts
// These behave differently:

// Uses instance property (default: true) — container may move
container.object = newImage;

// Explicit false — container stays in place
container.setObject(newImage);

// Explicit true — container inherits newImage's transforms
container.setObject(newImage, true);

Origin Normalization

Fabric.js v7 changed the default originX/originY from "left"/"top" to "center"/"center". This means that an object with left: 100, top: 100 in Fabric v7 has its center at (100, 100), not its top-left corner.

The library internally uses originX: "left", originY: "top" for all layout calculations. When useObjectTransform captures an object's position, it automatically converts the coordinates from the object's origin to left/top origin using Fabric.js's translateToGivenOrigin().

For example, a 200x100 object with originX: "center" at left: 150 is converted to left: 50 (since center at 150 minus half-width 100 equals top-left at 50).

This conversion is transparent — you don't need to worry about origin settings when using useObjectTransform.

What Transforms Are Captured

When useObjectTransform is active, the following properties are captured from the original object:

  • left, top — position (normalized to left/top origin for the container)
  • originX, originY — transform origin (stored for restoration via detachObject)
  • scaleX, scaleY — scale factors
  • angle — rotation in degrees
  • skewX, skewY — skew factors

These are extracted using Fabric.js's util.qrDecompose() on the object's full transform matrix, ensuring that nested group transforms are correctly accounted for.

Internal Details

The ObjectFit class maintains two private properties for transform tracking:

  • _loadedObjectInitialTransform — Permanently stores the original transform until the object is replaced. Used as a reference for the initial state.

  • _loadedObjectTransform — Temporary storage that is consumed during recompute(). When useObjectTransform is true, this is populated from _loadedObjectInitialTransform. After recompute applies these values to the container, they are cleared.

The restorePreviousObjectTransform parameter in setObject() controls whether the previous object's initial transform is restored when it is detached from the container. This defaults to true, so swapping objects puts the old one back where it was.

Demos and the Default

The demo examples rely on the default useObjectTransform: true. Since freshly loaded images typically carry identity transforms, the origin normalization is a no-op and the containers appear exactly where placed.

If you are building a fresh layout and want to make it explicit that you are positioning containers yourself, you can pass useObjectTransform: false. But in most cases, the default true works correctly thanks to automatic origin normalization.