Working with Design Editor
- 7-8 minutes to read
You may be already familiar with the IFrame API which allows for integrating the Customer's Canvas editor to a page. It exposes a JavaScript API that enables you to extend the functionality of the editor. However, it requires additional JavaScript programming which may be challenging.
To reduce the amount of work, you need to do to implement the typical scenarios, we have created a UI Framework. It helps you to define the behavior of the editor by modifying a JSON config + writing a few one-liner JavaScripts instead of diving into a full-fledged JS development.
In this tutorial, we will show you how you can use the UI Framework to create an editor with the following functionality:
- Edit product designs.
- Change a design, e.g. by selecting a product option.
- Set a background, e.g. by selecting a thumbnail in an image list.
- When the user changes a background, you don't want to lose the progress the user made.
- Change both canvas mockups and preview mockups.
- Render the design and use the result in other widgets.
Prerequisites
This tutorial assumes that you have already installed the UI Framework and have a general idea of parametrized configs and dynamic expressions.
You can use examples of this tutorial in the config constant in the sample.
DesignEditor Widget
As you might learn by using the demo, to manipulate the Customer's Canvas editor content, you need to configure the editor and use so-called "commands" in a widget config. Commands are the first-level elements of a widget's params, for example, for design-editor, this config looks as follows:
{
"widgets": [{
"type": "design-editor",
"name": "my-editor",
"params": {
"command1": { ... /* command1 params */ ... },
"command2": { ... /* command2 params */ ... },
// ... other commands.
}
}]
}
A command is triggered when the UI Framework detects changes in the config, i.e. if you refer to some option in one of its parameters. Other commands where no changes are detected are not triggered. If changes in several commands are detected, they are called in the same order as they go in the config.
Initializing the Editor
To load and initialize the editor, you must use the initial command. In this command, you specify a product design through the name of a state file, or an IDML/PSD file if you prefer to work with them directly. You can open the bbq-invitation.psd
design and enable a simple editor mode as follows:
{
"type": "design-editor",
"name": "my-editor",
"params": {
"initial": {
"productDefinition": {
"surfaces": [{
"printAreas": [{
"designFile": "bbq-invitation"
}]
}]
},
"editorConfig": {
"initialMode": "SimpleOnly"
}
}
}
}
Now, try to load the editor using this config. If everything is done correctly, you will see your design in the editor.
Let's take a look at other commands that you need to achieve the goal described above.
Replacing a Product Page
You may want to not only edit the template but also allow your users to change it, for example, by switching the orientation using the radio buttons in the Tool Panel.
To implement this, you need to prepare your templates for different product orientations. They should have a similar structure and the same elements with the same names. This will allow the editor to save the user changes when switching the templates.
Now, let's add an option button, which will switch the templates.
{
"type": "option",
"name": "orientation",
"title": "orientation",
"params": {
"type": "radio",
"title": "Orientation",
"values": [
{ "title": "vertical" },
{ "title": "horizontal" }
]
}
}
To change a template, you can use the setPrintArea command. This command is equivalent to IFrame API's setPrintAreas method. It takes the same input structure as you pass to the initial
command to describe a design (e.g. another state ID name or an IDML/PSD file name) and loads it to the current or specified page (surface) without updating other surfaces.
In the editor's config, you can refer to the value of this option through its name (orientation
in our case) as follows:
{
"type": "design-editor",
"name": "my-editor",
"params": {
"initial": { ... },
"setPrintArea": {
"data": {
"designFile": "{{'cards/' + $['orientation']._.title}}"
},
"options": {
"preserveUserChanges": true
}
}
}
}
This command has options
defining whether you want Customer's Canvas to merge new designs with the changes the user made or discard them. If you want to keep changes, then it runs some code under the hood that copies all new items to the new design, and if it detects items with the same names, it will copy their values to the appropriate items. You can refer to the description of options here.
The setPrintArea
command allows you to either change a single product page or replace the entire product in the editor. When you need to change a single page, use the surfaceIndex
parameter.
Note
The 0
index corresponds to the first page.
{
"type": "design-editor",
"name": "my-editor",
"params": {
"initial": { ... },
"setPrintArea": {
"data": {
"designFile": "{{'menu/' + $['weekday']._.title}}"
},
"surfaceIndex": 2,
"options": {
"preserveUserChanges": true
}
}
}
}
This way, you can reload the template and transfer all changes made by the user.
The setPrintArea
command transfers all layers, including the background. When you only need to change an image in the background, you can use the following command.
Changing a Background Image
Unlike the previous command, instead of changing the entire design, setBackground
receives a single image (JPEG, PDF, PNG) as an input. Under the hood, this command runs a code that finds a Background Container on a surface, removes the original image item from it, and replaces it with the specified image. It automatically enlarges it to fill the entire design.
{
"type": "design-editor",
"name": "my-editor",
"params": {
"initial": { ... },
"setBackground":
"url": "{{ $['bg']._.url }}"
}
}
}
The url
property can be either a URL to the image or a path in the Customer's Canvas gallery. In the latter case, use the public: prefix as explained in Populating designs with user info. You can also use the autoResize
or toTile
properties to make the background image fill the entire design.
{
"type": "design-editor",
"name": "my-editor",
"params": {
"initial": { ... },
"setBackground":
"url": "{{ 'public:' + 'themes/' + $['colors']._.props.themeName + '.pdf' }}",
"autoResize": true
}
}
}
Since this command only affects the background item, no other elements are affected and the user's progress remains intact.
Changing Mockups
In the UI Framework, you can define two mockup types:
- Canvas Mockups appear while editing a design. These mockups can be overlaying (so-called
Up
mockups) or represent the background (Down
mockups). - Preview Mockups appear when approving the design.
You enable these mockups, you can define a product in the initial
command as follows:
"widgets": [{
"type": "design-editor",
"name": "my-editor",
"params": {
"initial": {
"productDefinition": {
"surfaces": [
{
"stateId": "5fe423ee2510eb0cfd293042",
"mockup": {
"down": "608cf7c90a5afd7e9198106f"
},
"location": { "X": "500", "Y": "400" },
"previewMockups": [{
"down": "5fe339382510eb0cfd292f97"
}]
}
]
}
}
// other widgets
Here, we defined the design and mockup images by using their IDs in Asset Storage.
Now, let's look at how you can change these mockups based on the user's choice. First, define an option
widget with available mockups.
{
"type": "option",
"name": "mockups",
"title": "T-shirt color",
"params": {
"type": "radio",
"values": [
{
"title": "White",
"props": {
"mockup": "608cf7c90a5afd7e9198106f"
}
},
{
"title": "Red",
"props": {
"mockup": "1f42a938251ccb0cf3492f11"
}
}
]
}
}
After that, you can refer to this mockups
widget when defining the changeMockup command. This binding allows you to change mockups dynamically. When the user clicks the Red
option, an image with a red T-shirt appears. When they click White
again, the initial image will appear.
"widgets": [{
"type": "design-editor",
"name": "my-editor",
"params": {
"initial": { ... },
"changeMockup": {
"data": [{
"mockup": {
"down": "{{ $['mockups']._.props.mockup }}"
}
}]
}
// other widgets
Rendering Designs
UI Framework provides the following methods for rendering products loaded to the design-editor
widget:
- getHiResImages - renders print files.
- getProofImages - renders proof images in JPG and PNG formats.
- getProofPdf - renders proof images in PDF format.
Usually, you can call these methods from onActivate
and onLeave
handlers of steps
as well as from onClick
handlers of buttons, for example:
{
"type": "button",
"name": "finish-button",
"params": {
"text": "Finish",
"onClick": [
"{{#function main.showPreloader(true, 'Creating print files...')}}",
"{{#function $['my-editor'].getHiResImages(800,800)}}",
"{{#function main.showPreloader(false)}}",
"{{#function cart.submit()}}"
]
}
}
Here, we enable a preloader, render the product loaded to the my-editor
widget, disable the preloader when getHiResImages
finishes, and submit the order.
When you call these methods in onActivate
, you can initialize widgets defined in the step. For example, you can generate proof images when activating the approval step.
"steps": [
{
"name": "Editor",
"mainPanel": {
"name": "my-editor"
}
},
{
"name": "Approve",
"onActivate": [
"{{#function $['my-editor'].getProofImages() }}"
],
"mainPanel": {
"name": "preview"
}
}
]
Then, use those images in the preview widget.
{
"type": "image-carousel",
"name": "preview",
"params": {
"images": {
"{{ #each $['my-editor'].proofImageUrls as item }}": {
"url": "{{ item[index]}}"
}
}
}
}
For more details, you can refer to the widget reference.