Table of Contents
- Introduction
- Available APIs
- UI Components
- Basic Examples
- Intermediate Examples
- Advanced Examples
- Best Practices
Introduction
Custom plugins in Yo! Draw let you extend functionality with JavaScript. Your code has full access to:
canvas- Main Fabric.js canvas instancefabric- Fabric.js libraryanimState- Animation system statehistory- Undo/redo history- All global functions and variables
Available APIs
Canvas Operations
// Get canvas properties
canvas.width
canvas.height
canvas.backgroundColor
// Get all objects
const allObjects = canvas.getObjects();
// Get selected object
const obj = canvas.getActiveObject();
// Add object
canvas.add(myObject);
// Remove object
canvas.remove(obj);
// Render changes (ALWAYS call after modifications)
canvas.renderAll();
// Save to undo/redo history
saveHistory();
// Clear selection
canvas.discardActiveObject();
// Select object
canvas.setActiveObject(obj); Object Properties
const obj = canvas.getActiveObject();
// Common properties
obj.type // 'rect', 'circle', 'i-text', 'image', etc.
obj.left // X position
obj.top // Y position
obj.width // Object width
obj.height // Object height
obj.scaleX // Horizontal scale
obj.scaleY // Vertical scale
obj.angle // Rotation angle
obj.opacity // 0 to 1
obj.fill // Fill color
obj.stroke // Border color
obj.strokeWidth // Border width
obj.visible // true/false
// Modify properties
obj.set({
left: 100,
top: 50,
fill: '#ff0000',
opacity: 0.5,
angle: 45
});
// Clone object
obj.clone(function(cloned) {
canvas.add(cloned);
}); Creating Shapes
// Rectangle
const rect = new fabric.Rect({
left: 100,
top: 100,
width: 200,
height: 100,
fill: '#ff0000',
stroke: '#000000',
strokeWidth: 2
});
// Circle
const circle = new fabric.Circle({
left: 100,
top: 100,
radius: 50,
fill: '#00ff00'
});
// Triangle
const triangle = new fabric.Triangle({
left: 100,
top: 100,
width: 100,
height: 100,
fill: '#0000ff'
});
// Line
const line = new fabric.Line([50, 50, 200, 200], {
stroke: '#000000',
strokeWidth: 3
});
// Text
const text = new fabric.IText('Hello World', {
left: 100,
top: 100,
fontSize: 40,
fill: '#000000',
fontFamily: 'Arial'
});
// Polygon (Star example)
const star = new fabric.Polygon([
{x: 50, y: 0},
{x: 61, y: 35},
{x: 98, y: 35},
{x: 68, y: 57},
{x: 79, y: 91},
{x: 50, y: 70},
{x: 21, y: 91},
{x: 32, y: 57},
{x: 2, y: 35},
{x: 39, y: 35}
], {
left: 100,
top: 100,
fill: '#ffff00'
}); Loading Images
// From URL
fabric.Image.fromURL('https://example.com/image.png', function(img) {
img.scaleToWidth(200);
img.set({ left: 100, top: 100 });
canvas.add(img);
canvas.renderAll();
});
// From data URL
const dataURL = 'data:image/png;base64,...';
fabric.Image.fromURL(dataURL, function(img) {
canvas.add(img);
canvas.renderAll();
}); Working with SVG
// Load SVG from string
const svgString = '<svg>...</svg>';
fabric.loadSVGFromString(svgString, function(objects, options) {
const svg = fabric.util.groupSVGElements(objects, options);
canvas.add(svg);
canvas.renderAll();
});
// Load SVG from URL
fabric.loadSVGFromURL('https://example.com/image.svg', function(objects, options) {
const svg = fabric.util.groupSVGElements(objects, options);
canvas.add(svg);
canvas.renderAll();
}); UI Components
Dialog Box
// Simple message
showDialog('Title', 'Your message here');
// With button callback
showDialog('Success', 'Operation completed!'); Prompt (Get User Input)
// Get text input
showPrompt('Enter Name', 'Please enter your name:', 'Default Value').then(result => {
if (result) {
showDialog('Hello', 'Hello ' + result + '!');
} else {
showDialog('Cancelled', 'User cancelled');
}
});
// Get number input
showPrompt('Enter Angle', 'Enter rotation angle:', '45').then(result => {
if (result) {
const angle = parseFloat(result);
const obj = canvas.getActiveObject();
if (obj) {
obj.set('angle', angle);
canvas.renderAll();
saveHistory();
}
}
}); Confirmation Dialog
showConfirm('Delete All', 'Are you sure you want to delete everything?').then(confirmed => {
if (confirmed) {
canvas.clear();
canvas.backgroundColor = '#ffffff';
canvas.renderAll();
saveHistory();
showDialog('Done', 'Canvas cleared!');
}
}); Chaining Prompts
async function multiStepInput() {
const name = await showPrompt('Step 1', 'Enter text:', 'Hello');
if (!name) return;
const size = await showPrompt('Step 2', 'Enter font size:', '40');
if (!size) return;
const color = await showPrompt('Step 3', 'Enter color (hex):', '#ff0000');
if (!color) return;
const text = new fabric.IText(name, {
left: 100,
top: 100,
fontSize: parseInt(size),
fill: color
});
canvas.add(text);
canvas.renderAll();
saveHistory();
showDialog('Done', 'Text created!');
}
multiStepInput(); Basic Examples
1. Change Selected Object Color
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Please select an object first!');
} else {
obj.set('fill', '#ff0000');
canvas.renderAll();
saveHistory();
showDialog('Done', 'Color changed to red!');
} 2. Rotate Selected Object
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Please select an object first!');
} else {
showPrompt('Rotate', 'Enter angle in degrees:', '45').then(result => {
if (result) {
obj.set('angle', parseFloat(result));
canvas.renderAll();
saveHistory();
}
});
} 3. Set Random Colors
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
const randomColor = '#' + Math.floor(Math.random()*16777215).toString(16);
obj.set('fill', randomColor);
canvas.renderAll();
saveHistory();
showDialog('Random Color', 'Applied: ' + randomColor);
} 4. Duplicate Object Multiple Times
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
showPrompt('Duplicate', 'How many copies?', '5').then(result => {
if (result) {
const count = parseInt(result);
for (let i = 1; i <= count; i++) {
obj.clone(function(cloned) {
cloned.set({
left: obj.left + (i * 20),
top: obj.top + (i * 20)
});
canvas.add(cloned);
});
}
canvas.renderAll();
saveHistory();
showDialog('Done', count + ' copies created!');
}
});
} 5. Center Selected Object
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
obj.set({
left: canvas.width / 2,
top: canvas.height / 2,
originX: 'center',
originY: 'center'
});
canvas.renderAll();
saveHistory();
showDialog('Done', 'Object centered!');
} Intermediate Examples
6. Create Grid of Shapes
showPrompt('Grid', 'Enter grid size (e.g., 5 for 5x5):', '5').then(result => {
if (!result) return;
const gridSize = parseInt(result);
const spacing = 60;
const size = 40;
for (let row = 0; row < gridSize; row++) {
for (let col = 0; col < gridSize; col++) {
const rect = new fabric.Rect({
left: col * spacing + 50,
top: row * spacing + 50,
width: size,
height: size,
fill: '#' + Math.floor(Math.random()*16777215).toString(16),
stroke: '#000000',
strokeWidth: 1
});
canvas.add(rect);
}
}
canvas.renderAll();
saveHistory();
showDialog('Done', 'Created ' + (gridSize * gridSize) + ' squares!');
}); 7. Add Border to All Objects
showPrompt('Border Width', 'Enter border width in pixels:', '3').then(result => {
if (!result) return;
const width = parseInt(result);
const objects = canvas.getObjects();
objects.forEach(obj => {
obj.set({
stroke: '#000000',
strokeWidth: width
});
});
canvas.renderAll();
saveHistory();
showDialog('Done', 'Added borders to ' + objects.length + ' objects!');
}); 8. Fade Animation
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
let opacity = 1;
const interval = setInterval(() => {
opacity -= 0.05;
if (opacity <= 0) {
opacity = 1;
}
obj.set('opacity', opacity);
canvas.renderAll();
}, 50);
setTimeout(() => {
clearInterval(interval);
obj.set('opacity', 1);
canvas.renderAll();
showDialog('Done', 'Animation complete!');
}, 3000);
} 9. Circular Arrangement
const objects = canvas.getActiveObjects();
if (!objects || objects.length < 2) {
showDialog('Error', 'Select multiple objects (Ctrl+Click)');
} else {
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 150;
const angleStep = (2 * Math.PI) / objects.length;
objects.forEach((obj, index) => {
const angle = index * angleStep;
obj.set({
left: centerX + radius * Math.cos(angle),
top: centerY + radius * Math.sin(angle),
originX: 'center',
originY: 'center'
});
});
canvas.renderAll();
saveHistory();
showDialog('Done', 'Arranged ' + objects.length + ' objects in a circle!');
} 10. Text Shadow Effect
const obj = canvas.getActiveObject();
if (!obj || (obj.type !== 'i-text' && obj.type !== 'text')) {
showDialog('Error', 'Select a text object first!');
} else {
obj.set('shadow', new fabric.Shadow({
color: 'rgba(0,0,0,0.5)',
blur: 10,
offsetX: 5,
offsetY: 5
}));
canvas.renderAll();
saveHistory();
showDialog('Done', 'Shadow effect applied!');
} Advanced Examples
11. Spiral Generator
showPrompt('Spiral', 'Enter number of points:', '100').then(result => {
if (!result) return;
const points = [];
const numPoints = parseInt(result);
const angleStep = 360 / 20;
for (let i = 0; i < numPoints; i++) {
const angle = (i * angleStep) * Math.PI / 180;
const radius = i * 2;
points.push({
x: 400 + radius * Math.cos(angle),
y: 300 + radius * Math.sin(angle)
});
}
const spiral = new fabric.Polyline(points, {
fill: 'transparent',
stroke: '#ff0000',
strokeWidth: 2
});
canvas.add(spiral);
canvas.renderAll();
saveHistory();
showDialog('Done', 'Spiral created!');
}); 12. Gradient Generator
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
const gradient = new fabric.Gradient({
type: 'linear',
coords: {
x1: 0,
y1: 0,
x2: obj.width,
y2: 0
},
colorStops: [
{ offset: 0, color: '#ff0000' },
{ offset: 0.5, color: '#00ff00' },
{ offset: 1, color: '#0000ff' }
]
});
obj.set('fill', gradient);
canvas.renderAll();
saveHistory();
showDialog('Done', 'Rainbow gradient applied!');
} 13. Pattern Fill from Image
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
showPrompt('Pattern', 'Enter image URL:', 'https://via.placeholder.com/50').then(url => {
if (!url) return;
fabric.util.loadImage(url, function(img) {
const pattern = new fabric.Pattern({
source: img,
repeat: 'repeat'
});
obj.set('fill', pattern);
canvas.renderAll();
saveHistory();
showDialog('Done', 'Pattern applied!');
});
});
} 14. Object Inspector
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
const info = `
Type: ${obj.type}
Position: (${Math.round(obj.left)}, ${Math.round(obj.top)})
Size: ${Math.round(obj.width * obj.scaleX)} x ${Math.round(obj.height * obj.scaleY)}
Rotation: ${Math.round(obj.angle)}°
Opacity: ${obj.opacity}
Fill: ${obj.fill}
Stroke: ${obj.stroke || 'none'}
`;
showDialog('Object Inspector', info);
} 15. Batch Resize
showConfirm('Batch Resize', 'Resize all objects to 50% of current size?').then(confirmed => {
if (!confirmed) return;
const objects = canvas.getObjects();
objects.forEach(obj => {
obj.set({
scaleX: obj.scaleX * 0.5,
scaleY: obj.scaleY * 0.5
});
});
canvas.renderAll();
saveHistory();
showDialog('Done', 'Resized ' + objects.length + ' objects!');
}); 16. Color Palette Extractor
const obj = canvas.getActiveObject();
if (!obj || obj.type !== 'image') {
showDialog('Error', 'Select an image first!');
} else {
const imgElement = obj.getElement();
const tempCanvas = document.createElement('canvas');
tempCanvas.width = imgElement.width;
tempCanvas.height = imgElement.height;
const ctx = tempCanvas.getContext('2d');
ctx.drawImage(imgElement, 0, 0);
const colors = new Set();
const imageData = ctx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4000) {
const r = data[i];
const g = data[i + 1];
const b = data[i + 2];
const hex = '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
colors.add(hex);
if (colors.size >= 10) break;
}
let x = 50;
colors.forEach(color => {
const swatch = new fabric.Rect({
left: x,
top: 50,
width: 50,
height: 50,
fill: color,
stroke: '#000000',
strokeWidth: 1
});
canvas.add(swatch);
x += 60;
});
canvas.renderAll();
saveHistory();
showDialog('Done', 'Extracted ' + colors.size + ' colors!');
} 17. Text to Path Converter
const obj = canvas.getActiveObject();
if (!obj || (obj.type !== 'i-text' && obj.type !== 'text')) {
showDialog('Error', 'Select a text object first!');
} else {
const text = obj.text;
const fontSize = obj.fontSize;
const fontFamily = obj.fontFamily;
// Create temporary SVG
const svgString = `
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="200">
<text x="10" y="${fontSize}" font-family="${fontFamily}" font-size="${fontSize}" fill="${obj.fill}">
${text}
</text>
</svg>
`;
fabric.loadSVGFromString(svgString, function(objects, options) {
const svg = fabric.util.groupSVGElements(objects, options);
svg.set({
left: obj.left,
top: obj.top
});
canvas.add(svg);
canvas.renderAll();
saveHistory();
showDialog('Done', 'Text converted to path!');
});
} 18. Smart Duplicate (No Overlap)
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
showPrompt('Duplicate', 'How many copies?', '5').then(result => {
if (!result) return;
const count = parseInt(result);
let currentX = obj.left;
let currentY = obj.top;
for (let i = 1; i <= count; i++) {
obj.clone(function(cloned) {
// Move to right, if goes off canvas, move to next row
currentX += obj.width * obj.scaleX + 20;
if (currentX + obj.width * obj.scaleX > canvas.width) {
currentX = 50;
currentY += obj.height * obj.scaleY + 20;
}
cloned.set({
left: currentX,
top: currentY
});
canvas.add(cloned);
});
}
canvas.renderAll();
saveHistory();
showDialog('Done', count + ' copies created without overlap!');
});
} 19. Object Outline Generator
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Select an object first!');
} else {
showPrompt('Outline', 'Enter outline thickness:', '10').then(result => {
if (!result) return;
const thickness = parseInt(result);
obj.clone(function(cloned) {
cloned.set({
fill: 'transparent',
stroke: obj.fill,
strokeWidth: thickness,
scaleX: obj.scaleX * 1.05,
scaleY: obj.scaleY * 1.05
});
canvas.add(cloned);
cloned.sendToBack();
canvas.renderAll();
saveHistory();
showDialog('Done', 'Outline created!');
});
});
} 20. Canvas Statistics
const objects = canvas.getObjects();
const stats = {
total: objects.length,
rectangles: 0,
circles: 0,
triangles: 0,
text: 0,
images: 0,
other: 0
};
objects.forEach(obj => {
switch(obj.type) {
case 'rect': stats.rectangles++; break;
case 'circle': stats.circles++; break;
case 'triangle': stats.triangles++; break;
case 'i-text':
case 'text': stats.text++; break;
case 'image': stats.images++; break;
default: stats.other++; break;
}
});
const message = `
Total Objects: ${stats.total}
Rectangles: ${stats.rectangles}
Circles: ${stats.circles}
Triangles: ${stats.triangles}
Text: ${stats.text}
Images: ${stats.images}
Other: ${stats.other}
Canvas Size: ${canvas.width} x ${canvas.height}
Background: ${canvas.backgroundColor || 'transparent'}
`;
showDialog('Canvas Statistics', message); Best Practices
1. Always Check for Selection
const obj = canvas.getActiveObject();
if (!obj) {
showDialog('Error', 'Please select an object first!');
return;
}
// ... your code 2. Always Call canvas.renderAll()
// After ANY modification
obj.set('fill', '#ff0000');
canvas.renderAll(); // REQUIRED! 3. Always Call saveHistory()
// After making changes you want to be undoable
canvas.add(newObject);
canvas.renderAll();
saveHistory(); // Enables undo/redo 4. Use Async/Await for Prompts
async function myPlugin() {
const input1 = await showPrompt('Step 1', 'Enter value:');
if (!input1) return;
const input2 = await showPrompt('Step 2', 'Enter another value:');
if (!input2) return;
// Do something with input1 and input2
}
myPlugin(); 5. Handle Errors Gracefully
try {
// Your code here
const obj = canvas.getActiveObject();
obj.set('fill', '#ff0000');
canvas.renderAll();
saveHistory();
} catch (e) {
showDialog('Error', 'Something went wrong: ' + e.message);
} 6. Validate User Input
showPrompt('Number', 'Enter a number:', '10').then(result => {
if (!result) return;
const num = parseFloat(result);
if (isNaN(num)) {
showDialog('Error', 'Please enter a valid number!');
return;
}
// Use num
}); 7. Give User Feedback
// Show what happened
showDialog('Success', 'Created 10 objects!');
// Show what was applied
showDialog('Color Applied', 'Applied color: #ff0000'); 8. Clean Up After Animations
const interval = setInterval(() => {
// Animation code
}, 100);
// Always clear intervals
setTimeout(() => {
clearInterval(interval);
showDialog('Done', 'Animation complete!');
}, 3000); Tips & Tricks
Access Animation System
// Current frame
animState.currentFrame
// Total frames
animState.totalFrames
// Is playing?
animState.playing
// Go to frame
goToFrame(10);
// Add keyframe
addKeyframe(obj, animState.currentFrame); Access Canvas State
// Current zoom
zoom
// Current tool
currentTool
// Current style
currentStyle.fill
currentStyle.stroke
currentStyle.strokeWidth Iterate All Objects
canvas.getObjects().forEach(obj => {
// Do something with each object
obj.set('opacity', 0.5);
});
canvas.renderAll(); Find Objects by Type
const allRectangles = canvas.getObjects().filter(obj => obj.type === 'rect');
const allText = canvas.getObjects().filter(obj => obj.type === 'i-text' || obj.type === 'text'); Debugging
Log to Console
console.log('Object:', obj);
console.log('Canvas width:', canvas.width); Inspect Object Properties
const obj = canvas.getActiveObject();
console.log('All properties:', obj);
console.log('Position:', obj.left, obj.top);
console.log('Size:', obj.width, obj.height); Need Help?
- Check the Fabric.js documentation
- Experiment with small code snippets
- Use
console.log()to debug - Start with simple examples and build up