Implementing Copy and Paste for Canvas Elements in Fabric.js
This article explains how to implement copy and paste functionality to elements on a canvas using the Fabric.js library. The process involves cloning selected objects and adding them back to the canvas with appropriate positioning.
Core Implementation Strategy
The basic workflow consists of four steps:
- Create or select an existing element on the canvas.
- Retrieve the currently active object using
canvas.getActiveObject(). - Clone the selected object using Fabric.js's
clone()method. - Add the cloned object back onto the canvas using
canvas.add().
We will address different selection scenarios, including single objects, groups, and multiple selections.
Copying a Single Object
First, set up the HTML with buttons and a canvas, then initialize a Fabric.js canvas with a sample shape.
<div>
<button onclick="copyObject()">Copy</button>
<button onclick="pasteObject()">Paste</button>
</div>
<canvas id="fabricCanvas" width="500" height="400" style="border: 1px solid #ccc;"></canvas>
<script src="https://unpkg.com/fabric@5.3.0/dist/fabric.min.js"></script>
<script>
const canvas = new fabric.Canvas('fabricCanvas');
// Create a sample rectangle
let sampleRect = new fabric.Rect({
left: 100,
top: 50,
fill: '#4A90E2',
width: 100,
height: 100,
strokeWidth: 2,
stroke: '#2C3E50',
rx: 10,
ry: 10,
angle: 30
});
canvas.add(sampleRect);
// Variable to hold the cloned object
let clipboardObject = null;
// Copy function
function copyObject() {
let activeObj = canvas.getActiveObject();
if (activeObj) {
// Clone the active object and store it
activeObj.clone(function(cloned) {
clipboardObject = cloned;
});
}
}
// Paste function
function pasteObject() {
if (!clipboardObject) return;
// Clone the stored object for pasting
clipboardObject.clone(function(newClone) {
// Offset the new clone for visibility
newClone.set({
left: newClone.left + 15,
top: newClone.top + 15,
evented: true // Allows the object to receive events
});
canvas.add(newClone);
// Update the stored object's position for subsequent pastes
clipboardObject.top += 15;
clipboardObject.left += 15;
// Set the newly pasted object as active
canvas.setActiveObject(newClone);
canvas.requestRenderAll();
});
}
</script>
Key points in this implementation:
- The
clipboardObjectvariable acts as a temporary storage for the cloned element. - The copy function checks if an object is selected before cloning.
- The paste function offsets the new clone's position to prevent it from overlapping the original.
Copying a Group
Copying a group object follows the same logic as copying a single object because a group is treated as a single entity by Fabric.js. The code above works without modification. Here's an example of creating a group:
let circleA = new fabric.Circle({
radius: 50,
fill: '#E74C3C',
left: 0
});
let circleB = new fabric.Circle({
radius: 50,
fill: '#EC7063',
left: 80,
opacity: 0.8
});
let shapeGroup = new fabric.Group([circleA, circleB], {
left: 60,
top: 150
});
canvas.add(shapeGroup);
Using the same copyObject() and pasteObject() functions will copy and paste the entire group.
Copying Multiple Selected Objects
When users select multiple objects (e.g., by dragging a selection box), the active object's type property becomes 'activeSelection'. Handling this requires iterating through the selection.
Here is an updated paste function that accommoadtes both single objects and multiple selections:
function pasteObject() {
if (!clipboardObject) return;
clipboardObject.clone(function(pastedObj) {
// Apply a positional offset
pastedObj.set({
left: pastedObj.left + 15,
top: pastedObj.top + 15,
evented: true
});
if (pastedObj.type === 'activeSelection') {
// Handle multiple selected objects
pastedObj.canvas = canvas;
pastedObj.forEachObject(function(obj) {
canvas.add(obj);
});
pastedObj.setCoords(); // Recalculate coordinates
} else {
// Handle a single object or group
canvas.add(pastedObj);
}
// Update the clipboard position for the next paste operation
clipboardObject.top += 15;
clipboardObject.left += 15;
// Set the newly pasted object(s) as active
canvas.setActiveObject(pastedObj);
canvas.requestRenderAll();
});
}
The logic checks the type of the cloned object. If it is an activeSelection, it iterates through its child objects using forEachObject() and adds each one individually to the canvas. For single objects or groups, it adds the object directly.
Summary
This implementation provides a foundation for copy-paste operations in Fabric.js. The core steps are:
- Store a cloned version of the active selection.
- On paste, clone the stored object again.
- Add the new clone to the canvas with an offset.
- Handle different selection types (
activeSelectionfor multiple objects).
For a more complete user experience, you could extend this by adding keyboard shortcuts (Ctrl+C/Ctrl+V or Cmd+C/Cmd+V) using JavaScript event listeners.