Static preset thumbnails

Use static images as thumbnails for an editing extension's presets.

To provide the best user experience, editing extensions that support presets should dynamically generate thumbnails. This isn't always possible though, since it sometimes takes too much time to generate thumbnails on-the-fly. In these cases, it's possible to use static images as thumbnails. These thumbnails can indicate the behavior of the presets.

This tutorial explains how to use static images as preset thumbnails.

Step 1: Set up an editing extension

If you haven't already, set up an editing extension that supports presets. The following snippet provides the boilerplate of an extension that's suitable for this tutorial:

const { imageHelpers } = window.canva;
const canva = window.canva.init();
const state = {
image: null,
canvas: null,
selectedPreset: null,
};
canva.onReady(async (opts) => {
// Download the user's image
state.image = await imageHelpers.fromElement(opts.element);
// Convert the user's image to an HTMLCanvasElement
state.canvas = await imageHelpers.toCanvas(state.image);
// Render the HTMLCanvasElement
document.body.appendChild(state.canvas);
// Keep track of the selected preset
state.selectedPreset = opts.presetId;
// Is a preset selected?
if (state.selectedPreset) {
await renderImage();
}
});
canva.onPresetsRequest(async (opts) => {
return [
{
id: preset.id,
label: preset.label,
image: opts.image,
},
];
});
canva.onPresetSelected(async (opts) => {
state.selectedPreset = opts.presetId;
await renderImage();
});
canva.onImageUpdate(async (opts) => {
// Keep track of the updated image
state.image = opts.image;
// Re-render the user's image
await renderImage();
});
canva.onSaveRequest(async () => {
return await imageHelpers.fromCanvas("image/jpeg", state.canvas);
});
async function renderImage() {
const img = await imageHelpers.toImageElement(state.image);
const context = state.canvas.getContext("2d");
if (state.selectedPreset === "invert") {
context.filter = "invert(100%)";
}
context.drawImage(img, 0, 0, state.canvas.width, state.canvas.height);
}

If anything about this code is unfamiliar, see Simple presets.

Step 2: Upload assets via the Developer Portal

In the Developer Portal, editing extensions have an Assets field. You can upload files to this field that the extension can download at runtime, such as images for your preset's thumbnails. This is more efficient than embedding the images in the source code as base64-encoded strings.

To learn how to upload and download assets, see Assets.

Preset thumbnails should be at least 256px × 256px.

Step 3: Use the assets as preset thumbnails

In the JavaScript file, create an assets property in the state object:

const state = {
image: null,
canvas: null,
assets: null,
selectedPreset: null,
};

Then, in the onReady callback, use the state.assets property to store the assets that Canva passes to the extension:

// Keep track of the available assets
state.assets = opts.assets;

This lets the extension access the assets at other points in the extension's lifecycle.

In the onPresetsRequest callback, use the fromUrl method to download an asset and use it as a thumbnail:

canva.onPresetsRequest(async () => {
return [
{
id: "invert",
label: "Invert",
image: await imageHelpers.fromUrl(state.assets["invert.png"]),
kind: "simple",
},
];
});

This code sample assumes a file named invert.png has been uploaded to the Developer Portal.

Example

const { imageHelpers } = window.canva;
const canva = window.canva.init();
const state = {
image: null,
canvas: null,
assets: null,
selectedPreset: null,
};
canva.onReady(async (opts) => {
// Download the user's image
state.image = await imageHelpers.fromElement(opts.element);
// Convert the user's image into an HTMLCanvasElement
state.canvas = await imageHelpers.toCanvas(state.image);
// Render the user's image
document.body.appendChild(state.canvas);
// Keep track of the available assets
state.assets = opts.assets;
// Keep track of the selected preset
state.selectedPreset = opts.presetId;
// Is a preset selected?
if (state.selectedPreset) {
// Re-render the image when a user selects a preset
await renderImage();
}
});
canva.onPresetsRequest(async () => {
return [
{
id: "invert",
label: "Invert",
image: await imageHelpers.fromUrl(state.assets["invert.png"]),
kind: "simple",
},
];
});
canva.onPresetSelected(async (opts) => {
// Keep track of the selected preset
state.selectedPreset = opts.presetId;
// Re-render the image when a user selects a preset
await renderImage();
});
canva.onImageUpdate(async (opts) => {
// Keep track of the updated image
state.image = opts.image;
// Re-render the user's image
await renderImage();
});
canva.onSaveRequest(async () => {
// Return the image to Canva
return await imageHelpers.fromCanvas("image/jpeg", state.canvas);
});
async function renderImage() {
const img = await imageHelpers.toImageElement(state.image);
const context = state.canvas.getContext("2d");
// Apply the selected preset
if (state.selectedPreset === "invert") {
context.filter = `invert(100%)`;
}
context.drawImage(img, 0, 0, state.canvas.width, state.canvas.height);
}