Layouts and dimensions
The API for editing extensions has multiple objects with dimensions and coordinates, including pages, elements, images, and crop boxes.
This topic explains how the geometry of these objects interact.

Units of measurement

Unless otherwise stated, all units of measurements are in pixels. These pixels are zoom-independent, meaning they don't change as you zoom in or out. This makes them easier to reason about, since you don't have to consider the design's zoom level.

Pages

Canva designs have one or more pages.
A page, also known as the canvas, is where users can add their design elements, such as images, videos, and text.
Pages have a width and a height, which Canva passes to an extension via the onReady callback:
1
canva.onReady(async (opts) => {
2
console.log(opts.page.width);
3
console.log(opts.page.width);
4
});
Copied!
Pages do not have a position.

Elements

A page of a design can have zero or more elements.
An element is a generic way of referring to the "ingredients" of a design, such as images, videos, and text. At the moment, editing extensions can only edit image-based elements, including JPG, PNG, and SVG files.
When a user opens an extension, Canva passes the element into the onReady callback:
1
canva.onReady(async (opts) => {
2
console.log(opts.element);
3
});
Copied!
The API represents elements as the CanvaElement type:
1
type CanvaElement<
2
T extends CanvaMedia | CanvaImageUrl | CanvaImageBlob | undefined
3
> = {
4
type: CanvaDataType.CANVA_ELEMENT;
5
data: T;
6
layout: CanvaElementLayout;
7
fixed: boolean;
8
};
Copied!
This is an object that encodes information about how the element appears on a page. It has a layout property, which defines the following properties:
  • Position - The X and Y coordinates of the element, relative to the top-left corner of the page that it exists on.
  • Dimensions - The width and height of the element.
  • Rotation - The rotation of the element, in degrees.
  • Crop box - The visible section of the element.
The API represents these properties as the CanvaElementLayout type:
1
type CanvaElementLayout = {
2
height: number;
3
width: number;
4
5
left: number;
6
top: number;
7
rotation: number;
8
9
cropBox: {
10
left: number;
11
top: number;
12
height: number;
13
width: number;
14
};
15
};
Copied!

iframes

Editing extensions run in an iframe. The layout of this iframe is the same as the layout of the element. If an extension or user changes the layout of an element, the layout of the iframe also changes. Practically, they're the same thing.

Images

When a user opens an extension, the element contains three CanvaImageUrl objects:
1
type CanvaImageUrl = {
2
type: CanvaDataType.CANVA_IMAGE_URL;
3
url: string;
4
imageType: string;
5
width: number;
6
height: number;
7
};
Copied!
Each of these objects point towards an image file of a different resolution (thumbnail, preview, and full).
Typically, extensions use the toCanvas method to download the user's image and convert it into an HTMLCanvasElement:
1
const canva = window.canva.init();
2
3
const {
4
imageHelpers: { toCanvas },
5
} = window.canva;
6
7
canva.onReady(async (opts) => {
8
const canvas = await toCanvas(opts.element, { quality: "preview" });
9
document.body.appendChild(canvas);
10
});
Copied!
This method uses the following CSS to ensure the HTMLCanvasElement is scaled to the same size as the element:
1
canvas.style.width = "100%";
2
canvas.style.height = "100%";
Copied!
Without this CSS, the user's image may stretch beyond the dimensions of the element or not reach the edges of the element. For example, since the default size of an HTMLCanvasElement is 300 × 150, this is what the happens without the CSS properties:
The aspect ratio of the element is always the same as the aspect ratio of the user's image. The only exception is if an extension changes the aspect ratio of the element without changing the aspect ratio of the image, which it shouldn't do.

Crop box

All elements have a crop box. This is a rectangle that defines the visible section of the element. By default, the crop box extends to the edges of the element, so the entire element is visible, but extensions and users can adjust the crop box.
A crop box is a combination of dimensions and position. The position of a crop box is relative to the top-left corner of an element.

Fixed elements

Some elements are said to be fixed.
When an element is fixed, it’s locked into a certain position and can’t be moved. This prevents extensions from changing the element’s layout.
Some examples of fixed elements include:
  • Backgrounds
  • Frames
  • Grids
To see a fixed element in action:
  1. 1.
    Open the Canva editor.
  2. 2.
    Select More > Background.
  3. 3.
    Add a background to the canvas.
  4. 4.
    Try moving the background.
You’ll notice that the background is "stuck" in place.
The CanvaElementLayout object has a fixed property that identifies if the element is fixed or not:
1
canva.onReady(async (opts) => {
2
console.log(opts.element.layout.fixed); // => true
3
});
Copied!
When an element is fixed, an extension can't change the size or position of the visible part of the element, relative to the page it's on. However, it can still update the layout of the element.
Updating the width and height of a fixed element's layout changes the size of the underlying iframe. Furthermore, by updating the cropBox, the app can control the section of the iframe that is displayed in the fixed element. This can be thought of as shifting the iframe around, behind the element, in order to control what is visible in the "window" of the element, which cannot be moved.

Example

This example demonstrates the behavior of a fixed element. It only works when running in a fixed element. A "real" extension should check if the element is fixed and handle layout changes differently based on that check.
1
const {
2
imageHelpers: { toCanvaImageBlob, toCanvas, toImageElement },
3
} = window.canva;
4
const canva = window.canva.init();
5
6
const state = {
7
element: null,
8
image: null,
9
canvas: null,
10
};
11
12
canva.onReady(async (opts) => {
13
if (!opts.element.fixed) {
14
throw new Error("This example is only intended to work on fixed elements");
15
}
16
17
state.element = opts.element;
18
state.image = await toCanvaImageBlob(opts.element, { quality: "preview" });
19
state.canvas = await toCanvas(state.image);
20
document.body.appendChild(state.canvas);
21
renderControls();
22
});
23
24
canva.onLayoutChange((opts) => {
25
if (opts.commit) {
26
state.element.layout = opts.layout;
27
}
28
});
29
30
canva.onControlsEvent((event) => {
31
switch (event.message.controlId) {
32
case "enlarge":
33
enlarge();
34
return;
35
case "shift_right":
36
shift("right");
37
return;
38
case "shift_left":
39
shift("left");
40
return;
41
}
42
});
43
44
canva.onImageUpdate(async (opts) => {
45
const img = await toImageElement(opts.image);
46
const context = state.canvas.getContext("2d");
47
context.drawImage(img, 0, 0, state.canvas.width, state.canvas.height);
48
});
49
50
canva.onSaveRequest(async () => {
51
return toCanvaImageBlob(state.element, {
52
quality: "full",
53
imageType: state.image.imageType,
54
});
55
});
56
57
function renderControls() {
58
const controls = [
59
canva.create("button", { id: "enlarge", label: "Enlarge" }),
60
canva.create("button", { id: "shift_right", label: "Shift Right" }),
61
canva.create("button", { id: "shift_left", label: "Shift Left" }),
62
];
63
canva.updateControlPanel(controls);
64
}
65
66
function enlarge() {
67
// Create a copy of the layout
68
const layout = { ...state.element.layout };
69
70
// Dimensions
71
layout.width += 50;
72
layout.height += 50;
73
74
// Position
75
layout.top -= 25;
76
layout.left -= 25;
77
78
// Crop box
79
layout.cropBox.left += 25;
80
layout.cropBox.top += 25;
81
layout.cropBox.width += 50;
82
layout.cropBox.height += 50;
83
84
// Update the layout
85
canva.updateLayout({ layout });
86
}
87
88
function shift(direction) {
89
// Create a copy of the layout
90
const layout = { ...state.element.layout };
91
92
if (direction === "right") {
93
layout.cropBox.left -= 50;
94
}
95
96
if (direction === "left") {
97
layout.cropBox.left += 50;
98
}
99
100
// Update the layout
101
canva.updateLayout({ layout });
102
}
Copied!
Last modified 27d ago